From 5ae77b2a2fc35328505ee753d7ff524d3b66b657 Mon Sep 17 00:00:00 2001 From: Damian Nolan Date: Thu, 9 Mar 2023 17:43:12 +0100 Subject: [PATCH] chore: sync channel upgradability branch with main (#3224) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add changelog entries for v5.0.0 * chore: makefile cleanup/fixes (#2549) * cleaning up makefile * chore: removing goimports (#2548) * more cleanup * missing files * empty new line at the end * fix dockerfile * rename import Co-authored-by: Carlos Rodriguez Co-authored-by: Cian Hatton * use controller module address instead of module name for NewMsgChannelOpenInit (#2568) ## Description In controller keeper's `registerInterchainAccount` we execute a `MsgChannelOpenInit` to set up an ica channel. Currently, we have [`icatypes.ModuleName`](https://github.com/cosmos/ibc-go/blob/95cec44c9fb10a2e84a327cdd98c7d588f091f42/modules/apps/27-interchain-accounts/controller/keeper/account.go#L63) as the msg signer. This is not good because the [`ValidateBasic()`](https://github.com/cosmos/ibc-go/blob/692790402a033c1d9647c1e1eded5351332283bf/modules/core/04-channel/types/msgs.go#L34) of `MsgChannelOpenInit` has check if msg signer is bech32 or not. ref #2566 closes: #2559 --- 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)) - [ ] 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/main/docs/docs/building-modules/10-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 * test: 03-connection e2e test param change (#2139) * test: adding initial test boilerplate * fix: test * extend test to add token transfer * change to send from B to A and add test matrix * add test matrices * fix imports * fix some more imports * review comments * compare uint64 instead of string * review comments * fix test Co-authored-by: Carlos Rodriguez Co-authored-by: Carlos Rodriguez * fix: forbid negative values for trusting period, unbonding period and max clock drift (#2555) Co-authored-by: Carlos Rodriguez * refactor!: allow for serialization of proto message without fulfillment of sdk.Msg interface (#2607) ## Description ref: #2397 --- 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. - [x] Targeted PR against correct branch (see [CONTRIBUTING.md](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#pr-targeting)) - [x] Linked to Github issue with discussion and accepted design OR link to spec that describes this work. - [x] Code follows the [module structure standards](https://github.com/cosmos/cosmos-sdk/blob/main/docs/docs/building-modules/10-structure.md). - [x] Wrote unit and integration [tests](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#testing) - [x] Updated relevant documentation (`docs/`) or specification (`x//spec/`) - [x] Added relevant `godoc` [comments](https://blog.golang.org/godoc-documenting-go-code). - [x] Added a relevant changelog entry to the `Unreleased` section in `CHANGELOG.md` - [x] Re-reviewed `Files changed` in the Github PR explorer - [x] Review `Codecov Report` in the comment section below once CI passes * alignment * fix e2e ica tests (#2628) * rename `ClientParams` gRPC method to `Params` (#2573) * rename ClientParams to Params * add changelog * review comment to update rest api url Co-authored-by: Carlos Rodriguez * remove port prefix requirement (#2590) * remove port prefix requirement * chore: remove depcrated test and fix lint * add changelog entry * Update CHANGELOG.md Co-authored-by: Damian Nolan Co-authored-by: Damian Nolan * Update package-lock.json (#2569) Co-authored-by: Carlos Rodriguez * Update package-lock.json (#2570) Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> Co-authored-by: Carlos Rodriguez * fix: rm bech32 check from owner address on ICA controller msgs RegisterInterchainAccount and SendTx (#2601) * rm validate basic owner address check * Fetch makefile dependencies before building docker image (#2615) ## Description closes: #XXXX --- 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)) - [ ] 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/main/docs/docs/building-modules/10-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 * post v2.4.2, v3.3.1, v4.1.1 and v5.0.1 release chores (#2627) * chore: update changelog and versions for docs site * add new release lines to stable release policy * post release chores * update mergify Co-authored-by: Carlos Rodriguez * fix: skip emission of unpopulated memo field in ics20 (#2651) ## Description By setting `EmitDefaults` to false, we will not include an empty memo field in the marshaled json bytes closes: #2645 --- 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. - [x] 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/main/docs/docs/building-modules/10-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 * docs: ics27 v6 documentation updates (#2561) * WIP * updating messages doc * updating msgs doc * refactoring and organising ics27 docs * updating owner field validation docs * adding legacy api heading to auth modules doc * Update docs/apps/interchain-accounts/messages.md Co-authored-by: Charly * Update docs/apps/interchain-accounts/messages.md Co-authored-by: Carlos Rodriguez * applying suggestions from review * updating future improvements section of active channels Co-authored-by: Charly Co-authored-by: Carlos Rodriguez * e2e: adding e2e upgrade test for ibc-go/v6 (#2490) * adding x/group to simapp * [WIP] initial groups e2e scaffolding * work in progress * clean * adding interchain account address query to ica controller * adding basic cli query * satisfy linter, aligning recvr var naming * initial passing, register account proposal * successfully passing locally * updating default genesis state with allow all ica msgs, cleanup * reinstate num validators and num full nodes * unpin ibctest * Update e2e/tests/interchain_accounts/groups_test.go Co-authored-by: Carlos Rodriguez * updating swagger docs * updating e2e to ibc-go/v6 * WIP e2e upgrades for ibc-go/v6 * test manual e2e icad from branch * whoops * replace transfer logic with ics27 logic * removing pr number version tags * upgrade ica dep and tidy * removing upgrade name const * update deps, remove genesis code, extract consts * code comments and consts * readding intertx query client * fixing godoc * removing json entry in test matracies * refactor upgrades tests * hardcoding image values * updating workflows and correcting import * adding sanity checks and in-line comment * chore: adapt upgrade e2e workflow for custom images (#2541) * e2e upgrade test workflows in progress * adding chain binary to workflow * adding upgrade to compatibility workflows * Revert "adding upgrade to compatibility workflows" This reverts commit 77019997ed71c9c73b0077ecee45e5c0739111f6. * replace v5 tag in upgrade e2e * updating image tags * use v5.0.1 release version for upgrades e2e Co-authored-by: Carlos Rodriguez Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> * e2e: add transfer with memo compatibility tests (#2640) * Build E2E tests in workflow (#2649) * Add scopedkeeper as an interface to exported package (#2638) ## Description Move all expected scoped keepers to a unified interface in `core/exported` package. closes: #2020 --- 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)) - [ ] 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/main/docs/docs/building-modules/10-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 * chore: add potential use cases documentation for ica (#2594) ## Description will fix ordering after #2561 is merged, so review that first closes: #2472 --- 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)) - [ ] 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/main/docs/docs/building-modules/10-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 * chore: small README nits for readability and usability (#2669) * update code owners (#2665) * fix(testing): fix block proposer and allow Default gas to be modified (#2657) * carry block proposer, and allow Default gas to be modified * add changelog entry Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> Co-authored-by: Carlos Rodriguez * add check send enabled (#2679) * add check send enabled * changelog * fix: avoid race conditions in ics27 handshakes (#2682) * wip adding conditional to msg server and go apis, adding tests * cleanup * cleanup middleware enabled code * adding additional test case for reopening channel via msg server * Update modules/apps/27-interchain-accounts/controller/keeper/keeper.go Co-authored-by: Cian Hatton * updating error msgs and test case assertion * updating InitGenesis to set middleware disabled Co-authored-by: Cian Hatton * Removing stale comment (#2685) * chore: removing stale comment * Update modules/apps/27-interchain-accounts/controller/keeper/msg_server.go Co-authored-by: Damian Nolan * chore: fixing indentation Co-authored-by: Damian Nolan Co-authored-by: Carlos Rodriguez * post release chores (#2695) Co-authored-by: Carlos Rodriguez * removed unused variables (#2692) ## Description closes: #2486 --- 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. - [x] Targeted PR against correct branch (see [CONTRIBUTING.md](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#pr-targeting)) - [x] Linked to Github issue with discussion and accepted design OR link to spec that describes this work. - [x] Code follows the [module structure standards](https://github.com/cosmos/cosmos-sdk/blob/main/docs/docs/building-modules/10-structure.md). - [ ] Wrote unit and integration [tests](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#testing) - [x] Updated relevant documentation (`docs/`) or specification (`x//spec/`) - [ ] Added relevant `godoc` [comments](https://blog.golang.org/godoc-documenting-go-code). - [x] Added a relevant changelog entry to the `Unreleased` section in `CHANGELOG.md` - [x] Re-reviewed `Files changed` in the Github PR explorer - [ ] Review `Codecov Report` in the comment section below once CI passes * deps: updating sdk to v0.46.4 (#2647) ## Description closes: #XXXX --- 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)) - [ ] 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/main/docs/docs/building-modules/10-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 * e2e: updating upgrade test to use msg server (#2681) * chore: update protobuf generation to latest buf changes (#2705) * updating buf proto generation setup * revert adding test msg type * updating proto-swagger-gen configs * updating to latest proto builder image * downgrade to 0.46 support * go mod tidy e2e * cleanup makefile * rerun make proto-update-deps * updating buf yaml * updating cosmos/gogo-proto pin * regenerate protos * go mod tidy e2e * removing pulsar buf yaml * Update proto/buf.yaml * adding proto-registry.yml workflow for buf push to BSR * Update check x/bank sendEnabled link in changelog (#2714) Co-authored-by: Cian Hatton * e2e: fix memo test version declaration (#2723) ## Description closes: #XXXX --- 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)) - [ ] 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/main/docs/docs/building-modules/10-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 * chore: rm event emission after context caching (#2662) * post v5.1.0 release chores (#2722) * post v5.1.0 release chores * split transfer tests to workaround github's 256 job limit Co-authored-by: Carlos Rodriguez * Update transfer-memo.json * build(deps): bump github.com/spf13/viper from 1.13.0 to 1.14.0 (#2702) * build(deps): bump github.com/spf13/viper from 1.13.0 to 1.14.0 Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.13.0 to 1.14.0. - [Release notes](https://github.com/spf13/viper/releases) - [Commits](https://github.com/spf13/viper/compare/v1.13.0...v1.14.0) --- updated-dependencies: - dependency-name: github.com/spf13/viper dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * revert bump of tendermint to v0.35 * go mod tidy Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Carlos Rodriguez Co-authored-by: Carlos Rodriguez * chore: e2e for ICA reopening (#2720) Co-authored-by: crodriguezvega * build(deps): bump golangci/golangci-lint-action from 3.3.0 to 3.3.1 (#2738) Bumps [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action) from 3.3.0 to 3.3.1. - [Release notes](https://github.com/golangci/golangci-lint-action/releases) - [Commits](https://github.com/golangci/golangci-lint-action/compare/v3.3.0...v3.3.1) --- updated-dependencies: - dependency-name: golangci/golangci-lint-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Compare signature data in misbehaviours only if path differs (#2744) fix(statemachine)!: (06-solomachine) [#2744] Misbehaviour.ValidateBasic() now only enforces that signature data does not match when the signature paths are different. * chore: rename all occurances of smtypes and solomachinetypes import aliases to solomachine (#2743) * add missing set order functions for ica (#2740) Co-authored-by: Carlos Rodriguez * test: added test to verify signbytes marshal correctly for single signature solomachine * docs: fix migration/docs for ICA controller middleware (#2737) * update docs/migration with the change to middleware for ICA controller * improve variable naming * alignment Co-authored-by: Carlos Rodriguez * chore: updating `VerifyMembership` and `VerifyNonMembership` methods to use `Path` interface (#2736) * updating VerifyMembership and VerifyNonMembership interfaces to use exported.Path in favour of []byte * adding mock struct KeyPath to ibcmock testing pkg, adding additional tests to solomachine and tm clients * e2e: updated compatibility tests to support running against all unreleased versions (#2680) * chore: integrated git cliff into the code base to automate generation of changelogs * chore: fixing dead link in PR template (#2777) * deprecated(api)!: (06-solomachine) [#2761] removed deprecated `ClientId` field from `Misbehaviour` and `allow_update_after_proposal` field from `ClientState` * feat: adding connection params grpc and cli query (#2745) imp: (core/03-connection) Adding `ConnectionParams` grpc query and CLI to 03-connection. fix: (modules/core/keeper) Fix request wiring for `UpgradedConsensusState` in core query server. * chore: adding sentinel path for solomachine header (#2748) chore(statemachine)!: Adding sentinel value for header path in 06-solomachine. * fix: added check for empty path in 06-solomachine (#2741) * chore: fixing link to interchain accounts blogpost (#2787) * deps: bump SDK to v0.46.6 (#2784) * deps: bump SDK to v0.46.5 * bump SDK to v0.46.5 in e2e * deps: bump to SDK v0.46.6 Co-authored-by: Carlos Rodriguez * chore: adding migration doc for v6 to vuepress config (#2788) * e2e: remove irrelevant compatibility tests (#2780) * remove irrelevant compatibility tests (i.e. tests between already released tags) * add separate workflow to run unreleased compatibility tests * review comments * skip missing json files * fix condition for skipping missing json files * rename workflow Co-authored-by: Carlos Rodriguez * remove link to ICS 009 (#2797) * chore: adding defensive checks to ics27 capability migrations (#2798) * adding defensive checks and additional tests to ics27 capability migrations * assert mock capabilities remain unchanged * Update modules/apps/27-interchain-accounts/controller/migrations/v6/migrations_test.go Co-authored-by: Carlos Rodriguez Co-authored-by: Carlos Rodriguez * chore: move verifyMisbehavour to the top of misbehaviour_handle.go * chore: move solomachine CheckForMisbehaviour to `misbehaviour_handle.go` (#2802) * chore: move Tendermint CheckForMisbehaviour to `misbehaviour_handle.go` (#2803) * chore: remove custom dependabot reviewers (#2804) Co-authored-by: Carlos Rodriguez * fix: allow zero proof height, solo machine discards provided proof height in favor of sequence (#2746) imp: allow proof height to be zero for all core IBC `sdk.Msg` types that contain proofs. imp: discard proofHeight for solo machines and use the solo machine sequence instead. * chore: mergify backport task for v5.2.x (#2806) * Save gas on IsFeeEnabled (#2786) * Save gas on IsFeeEnabled * add entry for #2786 Co-authored-by: Carlos Rodriguez * chore: removed usage of deprecated `set-output` field in all Github workflows. (#2790) * Add more events to on IBC transfer, add tests for ibc transfer event (#2643) improvements: add amount, denom, and memo to transfer event emission. * build(deps): bump github.com/cosmos/cosmos-proto from 1.0.0-alpha7 to 1.0.0-alpha8 (#2726) * build(deps): bump github.com/cosmos/cosmos-proto Bumps [github.com/cosmos/cosmos-proto](https://github.com/cosmos/cosmos-proto) from 1.0.0-alpha7 to 1.0.0-alpha8. - [Release notes](https://github.com/cosmos/cosmos-proto/releases) - [Commits](https://github.com/cosmos/cosmos-proto/compare/v1.0.0-alpha7...v1.0.0-alpha8) --- updated-dependencies: - dependency-name: github.com/cosmos/cosmos-proto dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * chore: downgrade tendermint version * chore: go mod tidy e2e go module Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Colin Axnér <25233464+colin-axner@users.noreply.github.com> Co-authored-by: Damian Nolan * e2e: added ICS27 e2e test with governance. (#2808) * feat: add helper function for parsing clientID from client state path (#2820) feat: add `MustParseClientStatePath` which parses the clientID from a client state key path * e2e: add compatibility tests for ICA to v6.0.x (#2805) e2e: add ica compatibility tests for v6.0.x * test: updated solomachine tests to perform an ICS20 transfer instead of mocking functionality. * e2e: update icad tags for manual e2e workflow Co-authored-by: Carlos Rodriguez * e2e: enabling compatibility tests to be usable from other repositories' CI. (#2838) * feat: add optional migration pruning for tendermint consensus states (#2800) feat: add optional in-place store migration function to prune all expired tendermint consensus states * feat: add AppModuleBasic for tendermint client (#2825) feat: add `AppModuleBasic` for the 07-tendermint client and remove tendermint type registration from core IBC. Chains must register the `AppModuleBasic` of light clients. * feat: add AppModuleBasic for solo machine client (#2826) feat: add `AppModuleBasic` for the 06-solomachine client and remove solo machine type registration from core IBC. Chains must register the `AppModuleBasic` of light clients. * feat: add automatic in-place migrations for 02-client-refactor (v7) (#2819) feat: add automatic in-place store migrations to remove the localhost client and migrate existing solo machine definitions. * build(deps): bump google.golang.org/grpc from 1.50.1 to 1.51.0 (#2794) * build(deps): bump google.golang.org/grpc from 1.50.1 to 1.51.0 Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.50.1 to 1.51.0. - [Release notes](https://github.com/grpc/grpc-go/releases) - [Commits](https://github.com/grpc/grpc-go/compare/v1.50.1...v1.51.0) --- updated-dependencies: - dependency-name: google.golang.org/grpc dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * fix conflicts * bump grpc in e2e Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Carlos Rodriguez Co-authored-by: Carlos Rodriguez * e2e: add base and incentivized ica e2e tests to v6.0.x (#2832) * bug: fix clientID validation regex to conform closer to spec (#2510) Rejects non-ASCII plus whitespaces and slashes to get it much closer to the specifications per https://github.com/cosmos/ibc/tree/main/spec/core/ics-024-host-requirements#paths-identifiers-separators while here added test vectors that were previously failing. However, we still need much better specifications for how long of values for {client-type} are accepted. Fixes #2269 * follow up for #2510 * Revert "follow up for #2510" This reverts commit bfaf6e0118cca32838cd05eb732f4f4b7b4b634c. * e2e: Added support for running compatibility tests off the main branch. (#2848) * Renaming variable SubModuleName to ModuleName (#2851) * Renaming variable SubmoduleName to ModuleName in 07-tendermint (#2852) * refactor: rename IterateClients to IterateClientStates, add a prefix (#2856) feat(api)!: rename `IterateClients` to `IterateClientStates`. The function now takes a prefix argument which may be used for prefix iteration over the client store. feat: add `PrefixedClientStorePath` and `PrefixedClientStoreKey` functions to 24-host * chore: add upgrade handler to simapp for v6 -> v7 (#2842) * chore: follow up for #2510 (#2854) * follow up for #2510 * Update modules/core/02-client/types/keys.go Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> * e2e: move tag determination logic into the e2e Github workflows. (#2849) * deps: Bump ICS 23 to v0.9.0 * chore: bump ics23 to v0.9.0 * chore: add changelog entry Co-authored-by: Carlos Rodriguez * docs: updating v7 migration doc with generic state verification methods (#2789) * updating v6 to v7 migration doc with generic state verification methods * add note about state verification prior to ibc-go/v7 Co-authored-by: Carlos Rodriguez * fix: fix unmarshalling of packet data in send tx CLI * fix error messages * Update tx.go * fix unmarshalling * chore: add wait time for packet relay to e2e test for ICA channel opening after packet timeout * chore: scaffold custom IBC light client development guide (#2860) * chore: changing ibcprotocol.org to ibcprotocol.dev (#2884) * chore: update README to include link to IDA (#2887) * update README to reflect new website * pr comment * feat: Add genesis migrations for v6 to v7. The migration migrates the solo machine client state definition, removes all solo machine consensus states and removes the localhost client. (#2824) * refactor: simplify automatic migration code by using client keeper functions (#2864) * imp(api)!: remove legacy migrations required for upgrading from Stargate release line to ibc-go >= v1.x.x. (#2897) * chore: remove unnecessary file (#2898) * Update release-tracker.md * docs: v6 ICA migration docs improvements * e2e: changing default relayer tag to be v2.1.2 instead of main. (#2903) * refactor: simplify optional tendermint pruning migrations (#2862) * chore: add tmsdkeys to CODEOWNER for docs (#2904) * docs: add legacy docs for ICA * docs: added documentation for client state methods. (#2886) * docs: improvements to ICA docs * docs: move solomachine docs into the docs directory. (#2908) * deps: bump technote-space/get-diff-action from 6.1.1 to 6.1.2 (#2899) * deps: bump github.com/cosmos/cosmos-proto from 1.0.0-alpha8 to 1.0.0-beta.1 (#2870) * update README (#2910) * Update CHANGELOG.md * chore: update ibctest to latest commit (#2909) * update chainconfig * update to commit fixing broadcastTx * update to use SDK default cointype 118 * update so relayer tag isn't required * Fixing markdown link (#2924) * post v6.0.0 release chores (#2919) * docs: update integration docs to include light client registration. (#2905) * imp: Add `AssertEvents` which asserts events against expected event map. (#2829) * e2e: adding e2e upgrade test for ibc-go v7 (#2902) * add test for automatic migration of solomachine clientstate version * add clientID generation functions * update godoc * update sprintf message * e2e: update tags in e2e upgrade test * chore: use diffs to make registration more clear (#2927) * nit: fix typo in a comment * refactor: require light clients to set the initial client state and consensus state via the client state `Initialize` method (#2936) * set the initial client and consensus state via the client state Initialize method. update godocs * updating godocs * updating migration doc * updating light client guide docs for Initialize method * updating migration doc * Apply suggestions from code review Co-authored-by: Charly * Update docs/ibc/light-clients/client-state.md Co-authored-by: Carlos Rodriguez * adding tests to lightclients * updating 02-client tests Co-authored-by: Charly Co-authored-by: Carlos Rodriguez * chore: add backports for v4.3.x and v6.1.x * update compatibility tests with new release branches (#2946) * update compatibility tests * compatibility tests for v4.3.x * adding tags to tests * Skip e2e if test matrix does not exist (#2949) Co-authored-by: Carlos Rodriguez Co-authored-by: Cian Hatton * fix(statemachine)!: check x/bank send enabled before escrowing fees * chore(deps): bump goreleaser/goreleaser-action from 3 to 4 (#2932) Bumps [goreleaser/goreleaser-action](https://github.com/goreleaser/goreleaser-action) from 3 to 4. - [Release notes](https://github.com/goreleaser/goreleaser-action/releases) - [Commits](https://github.com/goreleaser/goreleaser-action/compare/v3...v4) --- updated-dependencies: - dependency-name: goreleaser/goreleaser-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Carlos Rodriguez * chore(api)!: removing solomachine header sequence (#2941) * removing solomachine header sequence * removing commented out code in validate basic * chore(deps): bump bufbuild/buf-setup-action from 1.9.0 to 1.10.0 (#2933) * chore: adding defensive check to v7 migrations (#2948) * adding defensive check to v7 migrations * adding test coverage for absence of tendermint clients * build(deps): bump cosmossdk.io/math from 1.0.0-beta.3 to 1.0.0-beta.4 (#2835) * build(deps): bump cosmossdk.io/math from 1.0.0-beta.3 to 1.0.0-beta.4 Bumps [cosmossdk.io/math](https://github.com/cosmos/cosmos-sdk) from 1.0.0-beta.3 to 1.0.0-beta.4. - [Release notes](https://github.com/cosmos/cosmos-sdk/releases) - [Changelog](https://github.com/cosmos/cosmos-sdk/blob/main/CHANGELOG.md) - [Commits](https://github.com/cosmos/cosmos-sdk/compare/math/v1.0.0-beta.3...math/v1.0.0-beta.4) --- updated-dependencies: - dependency-name: cosmossdk.io/math dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * downgrade SDK and Tendermint versions * go mod tidy in e2e Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: crodriguezvega * docs: update consensus state docs (#2937) * docs: lightclient dev guide overview section (#2901) * adding client/consensus state and height docs * cleanup * rename lightclient -> light client * adding links * adding client message section, updates re. feedback * fix wording * adding brief one liner intro, various minor changes * removing comment * fixing spelling * correct wording * docs: lightclient dev guide setup and client creation (#2922) * adding setup page for configuring light client modules and creation of ibc clients * updating page ordering * pr review suggestions * use 02-client-refactor-beta tag in favour of commit hash links * add link to issue for self managed params * Update docs/ibc/light-clients/client-state.md Co-authored-by: Charly Co-authored-by: Charly * review updates * clean up * docs: update consensus state docs (#2937) Co-authored-by: Charly * Update release-tracker.md * update roadmap * post v5.2.0 and v6.1.0 release chores (#2957) * update release-tracker.md * update roadmap.md * chore: prune expired `07-tendermint` consensus states on duplicate header updates (#2965) * move pruning to above duplicate update check * adding test for pruning on duplicate header update * adding additional check - assert that a consensus state exists at the prune height * chore: updating adr version numbers and removing unnecessary comment in solomachine (#2969) * improvement: allow chains that can't introspect their own `ConsensusState` support IBC * chore: Improved logging in IBC Core (#2976) * docs: adding solomachine v7 migration docs (#2968) * adding solomachine migration docs * updating migration doc * Apply suggestions from code review Co-authored-by: Carlos Rodriguez * updating code snippet formatting * tiny nits Co-authored-by: Carlos Rodriguez * docs: adding export metadata docs for light client module genesis (#2974) ## Description - Light client guide `ExportMetadata` closes: #1857 ### Commit Message / Changelog Entry ```bash docs: adding export metadata docs for light client module genesis ``` see the [guidelines](https://github.com/cosmos/ibc-go/blob/main/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)). - [ ] 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/main/docs/docs/building-modules/10-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). - [ ] Provide a [commit message](https://github.com/cosmos/ibc-go/blob/main/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. * update roadmap * docs: existence and non-existence proofs (#2966) * proofs docs * fix typo * docs: explain how to implement the four client state functions which allow for regular updates and misbehaviour handling (#2939) * update docs * docs: move ica requirements doc + add requirements doc template (#2917) * chore: Moved the ibc module name into the exported package (#2982) * docs: light client dev guide `CheckSubstituteAndUpdateState` (#2981) * docs: light client dev guide `VerifyUpgradeAndUpdateState`, `VerifyMembership`/`VerifyNonMembership` (#2994) * deps: bump google.golang.org/grpc from 1.51.0 to 1.52.0 (#2999) * build(deps): bump bufbuild/buf-setup-action from 1.10.0 to 1.11.0 (#2972) * deps: update to cosmos-sdk v0.47 (#2672) * build(deps): bump bufbuild/buf-setup-action from 1.11.0 to 1.12.0 (#3006) Bumps [bufbuild/buf-setup-action](https://github.com/bufbuild/buf-setup-action) from 1.11.0 to 1.12.0. - [Release notes](https://github.com/bufbuild/buf-setup-action/releases) - [Commits](https://github.com/bufbuild/buf-setup-action/compare/v1.11.0...v1.12.0) --- updated-dependencies: - dependency-name: bufbuild/buf-setup-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps): bump docker/metadata-action from 4.1.1 to 4.3.0 (#3007) Bumps [docker/metadata-action](https://github.com/docker/metadata-action) from 4.1.1 to 4.3.0.
Release notes

Sourced from docker/metadata-action's releases.

v4.3.0

Full Changelog: https://github.com/docker/metadata-action/compare/v4.2.0...v4.3.0

v4.2.0

  • Add tz attribute to handlebar date function by @​chroju (#251)
  • Bump minimatch from 3.0.4 to 3.1.2 (#242)
  • Bump csv-parse from 5.3.1 to 5.3.3 (#245)
  • Bump json5 from 2.2.0 to 2.2.3 (#252)

Full Changelog: https://github.com/docker/metadata-action/compare/v4.1.1...v4.2.0

Commits
  • 507c2f2 Merge pull request #257 from crazy-max/env-output
  • 04861f5 update generated content
  • 6729545 Provide outputs as env vars
  • 05d22bf Merge pull request #256 from crazy-max/fix-readme
  • 70b403b Fix README
  • 9e6ae02 Merge pull request #252 from docker/dependabot/npm_and_yarn/json5-2.2.3
  • 3d239e8 Bump json5 from 2.2.0 to 2.2.3
  • 7cb52e2 Merge pull request #251 from chroju/set_timezone
  • 90a1d5c Add tz attribute to handlebar date function
  • c98ac5e Merge pull request #249 from crazy-max/fix-readme
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=docker/metadata-action&package-manager=github_actions&previous-version=4.1.1&new-version=4.3.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
* lint tests (#2926) * lint tests * don't use prealloc for golangci * fix unnecessary conversions * var-declaration lints * fix ineffectual assignments * fix composite literal lints * address copylocks lints from govet * error checks in tests * handshake_test.go error checks * packet_test.go error checks * error checks * msg_server_test.go * errcheck in upgrade_test.go * goconsts & linting complete * Update CHANGELOG.md * golangci-lint run ./... --fix * last lint * fix lints * tidy * ignore legacy ica api * ignore icacontrollersendtx * golangci lint fixes * fix test Co-authored-by: Carlos Rodriguez Co-authored-by: Carlos Rodriguez * build(deps): bump docker/build-push-action from 3.2.0 to 3.3.0 (#3015) Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 3.2.0 to 3.3.0. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/c56af957549030174b10d6867f20e78cfd7debc5...37abcedcc1da61a57767b7588cb9d03eb57e28b3) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps): bump google.golang.org/grpc from 1.51.0 to 1.52.0 (#3016) Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.51.0 to 1.52.0. - [Release notes](https://github.com/grpc/grpc-go/releases) - [Commits](https://github.com/grpc/grpc-go/compare/v1.51.0...v1.52.0) --- updated-dependencies: - dependency-name: google.golang.org/grpc dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Carlos Rodriguez * Update the ecosystem links in the readme (#3018) The icq and rate limiting links were pointing to outdated links (module repo has moved and rate limiting is no longer as a pr but implemented) so this updates them both Co-authored-by: Carlos Rodriguez * chore(statemachine)!: write channel state before invoking app callbacks in ack and confirm channel handshake steps (#2973) Co-authored-by: Carlos Rodriguez * fix: correctly close iterator in `07-tendermint` store (#3022) * fix closing iterator in 07-tendermint * maintain defer pattern but use new variable to avoiid reassignment issues * fix: change check to disallow optimistic sends (#3009) * change check to disallow optimistic sends * update test * chore: use `sdk.LogDeferred` when closing store iterators * ics29 v1 requirements doc (#2958) * ics29 v1 requirements doc * Update docs/requirements/ics29-v1-requirements.md Co-authored-by: Damian Nolan Co-authored-by: Damian Nolan * docs: refactor contributing guidelines (#2706) * fix testing package in `types` tests for transfer (#3005) * chore: adding distribute fee events to ics29 * fix typos * Fix e2e tests for pre sdk v0.47 chains (#3026) * build(deps): bump github.com/spf13/viper from 1.14.0 to 1.15.0 (#3035) * build(deps): bump github.com/spf13/viper from 1.14.0 to 1.15.0 Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.14.0 to 1.15.0. - [Release notes](https://github.com/spf13/viper/releases) - [Commits](https://github.com/spf13/viper/compare/v1.14.0...v1.15.0) --- updated-dependencies: - dependency-name: github.com/spf13/viper dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * go mod tidy in e2e Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Damian Nolan Co-authored-by: crodriguezvega * temporarily update dockerfile to add all (#3050) * deps: update cosmos-sdk buf dependency and rebuild protos (#3024) Co-authored-by: Carlos Rodriguez * chore: use correct BSR proto repo in buf.yaml (#3053) * chore: bumping go mod to ibc-go/v7 (#3014) * bumping go mod to ibc-go/v7 * updating e2e go module with v7 deps * fix: update `Paginate` to use `FilterPaginate` in `ClientStates` and `ConnectionChannels` grpc queries (#3010) * update paginate to use filter paginate * build(deps): bump google.golang.org/grpc from 1.52.0 to 1.52.1 (#3059) * build(deps): bump google.golang.org/grpc from 1.52.0 to 1.52.1 Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.52.0 to 1.52.1. - [Release notes](https://github.com/grpc/grpc-go/releases) - [Commits](https://github.com/grpc/grpc-go/compare/v1.52.0...v1.52.1) --- updated-dependencies: - dependency-name: google.golang.org/grpc dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * go mod tidy e2e Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Damian Nolan * docs: add documentation for `TransferAuthorization` (#3044) * transferauthz docs * chore: add compatibility tests for v7.0.x and backport task (#3062) * build(deps): bump golangci/golangci-lint-action from 3.3.1 to 3.4.0 (#3051) Bumps [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action) from 3.3.1 to 3.4.0. - [Release notes](https://github.com/golangci/golangci-lint-action/releases) - [Commits](https://github.com/golangci/golangci-lint-action/compare/v3.3.1...v3.4.0) --- updated-dependencies: - dependency-name: golangci/golangci-lint-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps): bump google.golang.org/grpc from 1.52.1 to 1.52.3 (#3065) * build(deps): bump google.golang.org/grpc from 1.52.1 to 1.52.3 Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.52.1 to 1.52.3. - [Release notes](https://github.com/grpc/grpc-go/releases) - [Commits](https://github.com/grpc/grpc-go/compare/v1.52.1...v1.52.3) --- updated-dependencies: - dependency-name: google.golang.org/grpc dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * go mod tidy in e2e Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: crodriguezvega * fix: allow value with slashes in URL template (#3045) * go code snippets are properly formatted only with the go keyword * fix: moved non-verification misbehaviour checks to checkForMisbehaviour (#3046) * move misbehaviour check * add test coverage * Fix dockerfile on main (#3069) * Updating build simd image workflow (#3060) * Ensure we never use the Dockerfile on main when working with other branches (#3071) * chore: Add `HasConnection` and `HasChannel` methods. (#3082) * Allow specification of arbitrary values when running e2e tests (#3036) * followup from review comments for #2709 (#3027) ## Description This PR addresses these two comments from #2709: - [This comment about using the suffix `Fn`](https://github.com/cosmos/ibc-go/pull/2709#discussion_r1024070188). - [This comment about adding `doc.go` for packages](https://github.com/cosmos/ibc-go/pull/2709#discussion_r1080734398). closes: #XXXX ### Commit Message / Changelog Entry ```bash type: commit message ``` see the [guidelines](https://github.com/cosmos/ibc-go/blob/main/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)). - [ ] 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/main/docs/docs/building-modules/10-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). - [ ] Provide a [commit message](https://github.com/cosmos/ibc-go/blob/main/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. * deps: Update to Cosmos SDK v0.47 rc-2 (#3095) Co-authored-by: Marko Baricevic Co-authored-by: Cian Hatton * build(deps): bump docker/build-push-action from 3.3.0 to 4.0.0 (#3090) Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 3.3.0 to 4.0.0.
Release notes

Sourced from docker/build-push-action's releases.

v4.0.0

Warning

Buildx v0.10 enables support for a minimal SLSA Provenance attestation, which requires support for OCI-compliant multi-platform images. This may introduce issues with registry and runtime support (e.g. Google Cloud Run and AWS Lambda). You can optionally disable the default provenance attestation functionality using provenance: false.

Full Changelog: https://github.com/docker/build-push-action/compare/v3.3.1...v4.0.0

v3.3.1

Full Changelog: https://github.com/docker/build-push-action/compare/v3.3.0...v3.3.1

Commits
  • 3b5e802 Merge pull request #784 from crazy-max/enable-provenance
  • 02d3266 update generated content
  • f403daf revert disable provenance by default if not set
  • 1104d47 Merge pull request #781 from crazy-max/disable-provenance
  • 838bf90 update generated content
  • 337a09d disable provenance by default if not set
  • See full diff in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=docker/build-push-action&package-manager=github_actions&previous-version=3.3.0&new-version=4.0.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
* Fix e2e memo test for broken versions (#3096) * feat: Added authz support for ics20 (#3079) Co-authored-by: Damian Nolan Co-authored-by: Cian Hatton Co-authored-by: Charleen Fei Co-authored-by: Zaki Manian * Fix Compatibility Tests using govv1beta1 GenesisStates (#3052) * chores: post v4.3.0 release chores (#3080) ## Description closes: #XXXX ### Commit Message / Changelog Entry ```bash type: commit message ``` see the [guidelines](https://github.com/cosmos/ibc-go/blob/main/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. - [x] 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/main/docs/docs/building-modules/10-structure.md) and [Go style guide](../docs/dev/go-style-guide.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). - [ ] Provide a [commit message](https://github.com/cosmos/ibc-go/blob/main/CONTRIBUTING.md#commit-messages) to be used for the changelog entry in the PR description for review. - [x] Re-reviewed `Files changed` in the Github PR explorer. - [ ] Review `Codecov Report` in the comment section below once CI passes. * Add transfer authz tests to workflow (#3102) * build(deps): bump bufbuild/buf-setup-action from 1.12.0 to 1.13.0 (#3075) Bumps [bufbuild/buf-setup-action](https://github.com/bufbuild/buf-setup-action) from 1.12.0 to 1.13.0. - [Release notes](https://github.com/bufbuild/buf-setup-action/releases) - [Commits](https://github.com/bufbuild/buf-setup-action/compare/v1.12.0...v1.13.0) --- updated-dependencies: - dependency-name: bufbuild/buf-setup-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Damian Nolan * set light client module names (#3113) * set light client module names * go mod tidy * imp: adding channelID to `MsgChannelOpenTryResponse` (#3117) * build(deps): bump bufbuild/buf-setup-action from 1.13.0 to 1.13.1 (#3108) Bumps [bufbuild/buf-setup-action](https://github.com/bufbuild/buf-setup-action) from 1.13.0 to 1.13.1. - [Release notes](https://github.com/bufbuild/buf-setup-action/releases) - [Commits](https://github.com/bufbuild/buf-setup-action/compare/v1.13.0...v1.13.1) --- updated-dependencies: - dependency-name: bufbuild/buf-setup-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Carlos Rodriguez * attempting to silence deprecation warnings on golangci lint (#3125) * build(deps): bump cosmossdk.io/math from 1.0.0-beta.4 to 1.0.0-beta.6 (#3112) * build(deps): bump cosmossdk.io/math from 1.0.0-beta.4 to 1.0.0-beta.6 Bumps [cosmossdk.io/math](https://github.com/cosmos/cosmos-sdk) from 1.0.0-beta.4 to 1.0.0-beta.6. - [Release notes](https://github.com/cosmos/cosmos-sdk/releases) - [Changelog](https://github.com/cosmos/cosmos-sdk/blob/main/CHANGELOG.md) - [Commits](https://github.com/cosmos/cosmos-sdk/compare/math/v1.0.0-beta.4...math/v1.0.0-beta.6) --- updated-dependencies: - dependency-name: cosmossdk.io/math dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * go mod tidy e2e --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Carlos Rodriguez * fix docker build for forks (#3126) * v2 is EoL (#3121) * updated links in PR template * imp: add page result to ics29-fee queries (#3054) * build(deps): bump google.golang.org/grpc from 1.52.3 to 1.53.0 (#3127) * build(deps): bump google.golang.org/grpc from 1.52.3 to 1.53.0 Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.52.3 to 1.53.0. - [Release notes](https://github.com/grpc/grpc-go/releases) - [Commits](https://github.com/grpc/grpc-go/compare/v1.52.3...v1.53.0) --- updated-dependencies: - dependency-name: google.golang.org/grpc dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * go mod tidy e2e --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: crodriguezvega * docs: final review of light client developer guide (#3081) Co-authored-by: crodriguezvega * chore: unifiy tabs of 2 spaces in all go snippets in markdown files * docs: add link to BSR for proto docs (#3135) * Update CI to account for multiple types of relayers (#3043) * docs: add linter for markdown documents (#3133) Co-authored-by: Jacob Gadikian * build(deps): bump bufbuild/buf-setup-action from 1.13.1 to 1.14.0 (#3137) Bumps [bufbuild/buf-setup-action](https://github.com/bufbuild/buf-setup-action) from 1.13.1 to 1.14.0. - [Release notes](https://github.com/bufbuild/buf-setup-action/releases) - [Commits](https://github.com/bufbuild/buf-setup-action/compare/v1.13.1...v1.14.0) --- updated-dependencies: - dependency-name: bufbuild/buf-setup-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> * imp(testing): use testing.TB instead of testing.T (#3138) * chore: adding go.work and go.work.sum to gitignore (#3109) * Register required types for upgrade E2E tests (#3132) * simapp and upgrade configuration for e2e v7 upgrade (#3136) * Register required types for upgrade E2E tests * removed temporary function update * registering additional types and specifying rc0 tag in upgrade test * updated workflow tag * bump version to 6.1.0 * adding keytables to params subspaces in app.go * replace with pr docker build for testing * adding more wait for blocks * temporarily add new grpc services * removing last addition * configure store loaders for upgrade * adding consensus params migration from baseapp * testing without baseapp param migration * readd baseapp params migration * commiting updates, autocli and reflection svc * fix in run e2e script * adding crisis storekey to store upgrades * removing additional wait for blocks --------- Co-authored-by: Cian Hatton * docs: adding migration doc info for sdk v0.47 (#3107) * adding migration doc info for sdk v0.47 * code alignment * title formatting * chore: preemptively apply changes from #3149 * chore: apply changes from #3150 * chore: formatting * chore: combine proposal contents removal into existing section --------- Co-authored-by: Carlos Rodriguez Co-authored-by: Colin Axnér <25233464+colin-axner@users.noreply.github.com> * chore: remove legacy interface function `RandomizedParams`, which is no longer used. * chore: rename any var to protoAny (#2986) * chore: remove unused `ProposalContents` function * additional merge conflicts * cleanup post merge * cleanup post merge * go mod tidy * remove stale keys.go file * e2e: refactor importable workflow call to support chain upgrades (#3155) * chore: use fully qualified typeURL interface names in protos (#3165) * Fix test matrix generation (#3172) Co-authored-by: Damian Nolan * removing sdk module params subspaces (#3182) * docs: Add ADR 010, IBC light clients as SDK modules * Add IBC module level errors (#3184) * imp: add log message for debug error failed acknowledgement errors (#3077) * add log message for generate ack * add more log * correct message * nits * merge main * change structure of the logging * change some return error msg * nit * Adding scripts to run linting on only modified files (#3192) * Fix the way linting works for packages (#3198) * chore: Migrated all SDK errors to the new errors go module. (#3186) * build(deps): bump github.com/cosmos/gogoproto from 1.4.4 to 1.4.6 (#3177) * build(deps): bump github.com/cosmos/gogoproto from 1.4.4 to 1.4.6 Bumps [github.com/cosmos/gogoproto](https://github.com/cosmos/gogoproto) from 1.4.4 to 1.4.6. - [Release notes](https://github.com/cosmos/gogoproto/releases) - [Changelog](https://github.com/cosmos/gogoproto/blob/main/CHANGELOG.md) - [Commits](https://github.com/cosmos/gogoproto/compare/v1.4.4...v1.4.6) --- updated-dependencies: - dependency-name: github.com/cosmos/gogoproto dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * go mod tidy e2e --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: crodriguezvega * build(deps): bump cosmossdk.io/api from 0.2.6 to 0.3.1 (#3180) * build(deps): bump cosmossdk.io/api from 0.2.6 to 0.3.1 Bumps [cosmossdk.io/api](https://github.com/cosmos/cosmos-sdk) from 0.2.6 to 0.3.1. - [Release notes](https://github.com/cosmos/cosmos-sdk/releases) - [Changelog](https://github.com/cosmos/cosmos-sdk/blob/v0.3.1/CHANGELOG.md) - [Commits](https://github.com/cosmos/cosmos-sdk/compare/api/v0.2.6...v0.3.1) --- updated-dependencies: - dependency-name: cosmossdk.io/api dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * go mod tidy e2e --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: crodriguezvega * imp: make event emission functions unexported (#3205) * imp: add port id into msg register interchain account response (#3204) * build(deps): bump github.com/cosmos/cosmos-proto from 1.0.0-beta.1 to 1.0.0-beta.2 (#3190) * build(deps): bump github.com/cosmos/cosmos-proto Bumps [github.com/cosmos/cosmos-proto](https://github.com/cosmos/cosmos-proto) from 1.0.0-beta.1 to 1.0.0-beta.2. - [Release notes](https://github.com/cosmos/cosmos-proto/releases) - [Commits](https://github.com/cosmos/cosmos-proto/compare/v1.0.0-beta.1...v1.0.0-beta.2) --- updated-dependencies: - dependency-name: github.com/cosmos/cosmos-proto dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * go mod tidy e2e --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Damian Nolan Co-authored-by: Carlos Rodriguez * build(deps): bump github.com/stretchr/testify from 1.8.1 to 1.8.2 (#3212) * build(deps): bump github.com/stretchr/testify from 1.8.1 to 1.8.2 Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.8.1 to 1.8.2. - [Release notes](https://github.com/stretchr/testify/releases) - [Commits](https://github.com/stretchr/testify/compare/v1.8.1...v1.8.2) --- updated-dependencies: - dependency-name: github.com/stretchr/testify dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * go mod tidy e2e --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: crodriguezvega * fixing linter errors --------- Signed-off-by: dependabot[bot] Co-authored-by: Carlos Rodriguez Co-authored-by: Carlos Rodriguez Co-authored-by: Cian Hatton Co-authored-by: khanh-notional <50263489+catShaark@users.noreply.github.com> Co-authored-by: Sean King Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> Co-authored-by: LaurensKubat <32776056+LaurensKubat@users.noreply.github.com> Co-authored-by: theinternetofdefcon <116480549+theinternetofdefcon@users.noreply.github.com> Co-authored-by: Charly Co-authored-by: Anmol Co-authored-by: Ramiro Carlucho Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> Co-authored-by: Daniyal Ishfaq Co-authored-by: Calvin Lau <38898718+calvinaco@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Alexander Peters Co-authored-by: Emmanuel T Odeke Co-authored-by: Charly Co-authored-by: Auridas F Co-authored-by: Web3 Philosopher Co-authored-by: Marko Co-authored-by: Jacob Gadikian Co-authored-by: Susannah Evans <65018876+womensrights@users.noreply.github.com> Co-authored-by: Marko Baricevic Co-authored-by: Cian Hatton Co-authored-by: Zaki Manian Co-authored-by: MD Aleem <72057206+aleem1314@users.noreply.github.com> Co-authored-by: tmsdkeys <98807841+tmsdkeys@users.noreply.github.com> Co-authored-by: Régis Co-authored-by: GNaD13 <89174180+GNaD13@users.noreply.github.com> --- .github/CODEOWNERS | 34 +- .github/ISSUE_TEMPLATE/release-tracker.md | 38 +- .github/PULL_REQUEST_TEMPLATE.md | 37 +- .../main/client-chain-a.json | 11 + .../main/client-chain-b.json | 11 + .../main/connection-chain-a.json | 11 + .../main/connection-chain-b.json | 11 + .../main/ica-chain-a.json | 13 + .../main/ica-gov-chain-a.json | 11 + .../main/ica-groups-chain-a.json | 11 + .../main/incentivized-ica-chain-a.json | 12 + .../main/incentivized-transfer-chain-a.json | 16 + .../main/incentivized-transfer-chain-b.json | 16 + .../main/transfer-chain-a.json | 16 + .../main/transfer-chain-b.json | 16 + .../release-v3.4.x/transfer-chain-a.json | 16 + .../release-v3.4.x/transfer-chain-b.json | 16 + .../incentivized-transfer-chain-a.json | 16 + .../incentivized-transfer-chain-b.json | 16 + .../release-v4.2.x/transfer-chain-a.json | 16 + .../release-v4.2.x/transfer-chain-b.json | 16 + .../incentivized-transfer-chain-a.json | 16 + .../incentivized-transfer-chain-b.json | 16 + .../release-v4.3.x/transfer-chain-a.json | 16 + .../release-v4.3.x/transfer-chain-b.json | 16 + .../incentivized-transfer-chain-a.json | 16 + .../incentivized-transfer-chain-b.json | 16 + .../release-v5.2.x/transfer-chain-a.json | 16 + .../release-v5.2.x/transfer-chain-b.json | 16 + .../release-v6.1.x/client-chain-a.json | 11 + .../release-v6.1.x/client-chain-b.json | 11 + .../release-v6.1.x/connection-chain-a.json | 11 + .../release-v6.1.x/connection-chain-b.json | 11 + .../release-v6.1.x/ica-chain-a.json | 13 + .../release-v6.1.x/ica-chain-b.json | 13 + .../release-v6.1.x/ica-gov-chain-a.json | 11 + .../release-v6.1.x/ica-gov-chain-b.json | 11 + .../release-v6.1.x/ica-groups-chain-a.json | 11 + .../release-v6.1.x/ica-groups-chain-b.json | 11 + .../incentivized-ica-chain-a.json | 12 + .../incentivized-ica-chain-b.json | 12 + .../incentivized-transfer-chain-a.json | 16 + .../incentivized-transfer-chain-b.json | 16 + .../release-v6.1.x/transfer-chain-a.json | 16 + .../release-v6.1.x/transfer-chain-b.json | 16 + .../release-v7.0.x/client-chain-a.json | 11 + .../release-v7.0.x/client-chain-b.json | 11 + .../release-v7.0.x/connection-chain-a.json | 11 + .../release-v7.0.x/connection-chain-b.json | 11 + .../release-v7.0.x/ica-chain-a.json | 13 + .../release-v7.0.x/ica-chain-b.json | 13 + .../release-v7.0.x/ica-gov-chain-a.json | 11 + .../release-v7.0.x/ica-gov-chain-b.json | 11 + .../release-v7.0.x/ica-groups-chain-a.json | 11 + .../release-v7.0.x/ica-groups-chain-b.json | 11 + .../incentivized-ica-chain-a.json | 12 + .../incentivized-ica-chain-b.json | 12 + .../incentivized-transfer-chain-a.json | 16 + .../incentivized-transfer-chain-b.json | 16 + .../transfer-authz-chain-a.json | 11 + .../release-v7.0.x/transfer-chain-a.json | 16 + .../release-v7.0.x/transfer-chain-b.json | 16 + .../unreleased/client.json | 11 + .../unreleased/connection.json | 11 + .../unreleased/incentivized-transfer.json | 16 + .../unreleased/transfer.json | 16 + .github/dependabot.yml | 4 - .github/mergify.yml | 48 +- .github/scripts/go-imports.sh | 20 - .../workflows/build-simd-image-from-tag.yml | 39 + .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/deploy-docs.yml | 42 + .../e2e-compatibility-unreleased.yaml | 49 + .../e2e-compatibility-workflow-call.yaml | 56 + .github/workflows/e2e-compatibility.yaml | 194 + .github/workflows/e2e-fork.yml | 23 +- .github/workflows/e2e-manual-icad.yaml | 67 + .github/workflows/e2e-manual-simd.yaml | 92 + .github/workflows/e2e-test-workflow-call.yml | 165 + .github/workflows/e2e-upgrade.yaml | 43 + .github/workflows/e2e.yaml | 151 +- .github/workflows/goimports.yaml | 9 - .github/workflows/golangci.yml | 26 + .github/workflows/link-check.yml | 4 +- .github/workflows/proto-registry.yml | 20 + .github/workflows/release.yml | 47 +- .github/workflows/test.yml | 26 +- .gitignore | 3 + .golangci.yml | 33 +- .markdownlint.jsonc | 10 + .markdownlintignore | 3 + CHANGELOG.md | 478 +- CONTRIBUTING.md | 269 +- Dockerfile | 14 +- MAINTAINERSHIP.md | 62 + Makefile | 218 +- README.md | 101 +- RELEASES.md | 102 +- buf.work.yaml | 1 - cliff.toml | 119 + cmd/build_test_matrix/main.go | 78 +- cmd/build_test_matrix/main_test.go | 75 +- cmd/determine_simd_tag/main.go | 26 - contrib/devtools/Makefile | 76 + contrib/test_cover.sh | 14 + docs/.vuepress/config.js | 204 +- docs/DOCS_GUIDELINES.md | 41 + docs/DOCS_README.md | 9 +- docs/OLD_README.md | 117 - docs/README.md | 3 +- .../interchain-accounts/active-channels.md | 31 +- docs/apps/interchain-accounts/auth-modules.md | 390 +- docs/apps/interchain-accounts/client.md | 180 + docs/apps/interchain-accounts/development.md | 36 + docs/apps/interchain-accounts/integration.md | 136 +- .../legacy/auth-modules.md | 268 + .../interchain-accounts/legacy/integration.md | 195 + .../interchain-accounts/legacy/keeper-api.md | 121 + docs/apps/interchain-accounts/messages.md | 74 + docs/apps/interchain-accounts/overview.md | 31 +- docs/apps/interchain-accounts/parameters.md | 39 +- docs/apps/interchain-accounts/transactions.md | 21 - docs/apps/transfer/authorizations.md | 47 + docs/apps/transfer/events.md | 3 + docs/apps/transfer/messages.md | 1 + docs/apps/transfer/metrics.md | 2 +- docs/apps/transfer/params.md | 10 +- docs/architecture/README.md | 2 - .../adr-001-coin-source-tracing.md | 19 +- .../adr-002-go-module-versioning.md | 35 +- .../adr-003-ics27-acknowledgement.md | 88 +- .../adr-004-ics29-lock-fee-module.md | 29 +- .../adr-005-consensus-height-events.md | 92 + .../adr-006-02-client-refactor.md | 203 + .../adr-007-solomachine-signbytes.md | 52 + .../adr-009-v6-ics27-msgserver.md | 115 + .../adr-010-light-clients-as-sdk-modules.md | 100 + .../adr-015-ibc-packet-receiver.md | 12 +- .../adr-025-ibc-passive-channels.md | 66 +- .../adr-026-ibc-client-recovery-mechanisms.md | 10 +- docs/architecture/adr-027-ibc-wasm.md | 102 +- docs/architecture/adr-template.md | 3 +- docs/assets/auth-module-decision-tree.png | Bin 0 -> 80522 bytes docs/assets/ica/ica-pre-v6.png | Bin 0 -> 34698 bytes docs/assets/ica/ica-v6.png | Bin 0 -> 163580 bytes docs/client/swagger-ui/swagger.yaml | 1871 +- docs/dev/development-setup.md | 61 + docs/dev/go-style-guide.md | 115 + docs/dev/project-structure.md | 46 + docs/dev/pull-requests.md | 68 + docs/dev/release-management.md | 82 + docs/ibc-go-image.png | Bin 1460199 -> 1454326 bytes docs/ibc/apps.md | 282 +- docs/ibc/apps/apps.md | 2 +- docs/ibc/apps/bindports.md | 146 +- docs/ibc/apps/ibcmodule.md | 229 +- docs/ibc/apps/keeper.md | 56 +- docs/ibc/apps/packets_acks.md | 20 +- docs/ibc/apps/routing.md | 20 +- docs/ibc/events.md | 13 +- docs/ibc/integration.md | 45 +- docs/ibc/light-clients/client-state.md | 75 + docs/ibc/light-clients/consensus-state.md | 23 + docs/ibc/light-clients/genesis.md | 36 + docs/ibc/light-clients/overview.md | 70 + docs/ibc/light-clients/proofs.md | 62 + docs/ibc/light-clients/proposals.md | 32 + docs/ibc/light-clients/setup.md | 129 + .../ibc/light-clients/solomachine/concepts.md | 49 +- .../light-clients/solomachine/solomachine.md | 8 +- .../ibc/light-clients/solomachine/state.md | 1 - .../solomachine/state_transitions.md | 2 +- .../light-clients/updates-and-misbehaviour.md | 95 + docs/ibc/light-clients/upgrades.md | 62 + docs/ibc/middleware/develop.md | 427 +- docs/ibc/middleware/integration.md | 13 +- docs/ibc/overview.md | 91 +- docs/ibc/proposals.md | 98 +- docs/ibc/proto-docs.md | 5409 +----- docs/ibc/relayer.md | 18 +- docs/ibc/upgrades/README.md | 4 +- docs/ibc/upgrades/developer-guide.md | 43 +- docs/ibc/upgrades/genesis-restart.md | 21 +- docs/ibc/upgrades/quick-guide.md | 8 +- docs/middleware/ics29-fee/fee-distribution.md | 54 +- docs/middleware/ics29-fee/integration.md | 101 +- docs/middleware/ics29-fee/msgs.md | 92 +- docs/migrations/migration-template.md | 14 +- docs/migrations/sdk-to-v1.md | 110 +- .../migrations/support-denoms-with-slashes.md | 52 +- docs/migrations/v1-to-v2.md | 28 +- docs/migrations/v2-to-v3.md | 134 +- docs/migrations/v3-to-v4.md | 85 +- docs/migrations/v4-to-v5.md | 434 + docs/migrations/v5-to-v6.md | 292 + docs/migrations/v6-to-v7.md | 351 + docs/package-lock.json | 14374 +++++++++++++++- docs/package.json | 5 - .../ics27-requirements.md} | 71 +- docs/requirements/ics29-v1-requirements.md | 177 + docs/requirements/requirements-template.md | 53 + docs/roadmap/roadmap.md | 70 +- docs/versions | 14 + e2e/Makefile | 7 +- e2e/README.md | 354 + e2e/fee_middleware_test.go | 439 - e2e/go.mod | 256 +- e2e/go.sum | 973 +- e2e/relayer/relayer.go | 58 + e2e/scripts/run-compatibility-tests.sh | 14 + e2e/scripts/run-e2e.sh | 15 +- e2e/semverutil/semver.go | 61 + e2e/semverutil/semver_test.go | 53 + e2e/testconfig/testconfig.go | 325 +- .../core/03-connection/connection_test.go | 132 + e2e/tests/core/client_test.go | 121 + e2e/tests/interchain_accounts/base_test.go | 421 + e2e/tests/interchain_accounts/gov_test.go | 117 + e2e/tests/interchain_accounts/groups_test.go | 214 + .../interchain_accounts/incentivized_test.go | 393 + .../intertx_incentivized_test.go | 355 + e2e/tests/interchain_accounts/intertx_test.go | 215 + e2e/tests/transfer/authz_test.go | 329 + e2e/tests/transfer/base_test.go | 472 + e2e/tests/transfer/incentivized_test.go | 742 + e2e/tests/upgrades/upgrade_test.go | 638 + e2e/testsuite/codec.go | 66 + e2e/testsuite/grpc_query.go | 159 + e2e/testsuite/relayer.go | 24 - e2e/testsuite/testsuite.go | 438 +- e2e/testvalues/values.go | 21 +- go.mod | 188 +- go.sum | 932 +- go.work.example | 6 + internal/errors/errors.go | 63 + internal/logging/logging.go | 23 + maintainership.png | Bin 0 -> 718328 bytes .../27-interchain-accounts/client/cli/cli.go | 24 +- .../controller/client/cli/cli.go | 23 +- .../controller/client/cli/query.go | 36 +- .../controller/client/cli/tx.go | 109 + .../controller/ibc_middleware.go | 95 +- .../controller/ibc_middleware_test.go | 262 +- .../controller/keeper/account.go | 61 +- .../controller/keeper/account_test.go | 8 +- .../controller/keeper/events.go | 4 +- .../controller/keeper/genesis.go | 16 +- .../controller/keeper/genesis_test.go | 45 +- .../controller/keeper/grpc_query.go | 31 +- .../controller/keeper/grpc_query_test.go | 71 +- .../controller/keeper/handshake.go | 45 +- .../controller/keeper/handshake_test.go | 57 +- .../controller/keeper/keeper.go | 109 +- .../controller/keeper/keeper_test.go | 61 +- .../controller/keeper/migrations.go | 50 + .../controller/keeper/migrations_test.go | 75 + .../controller/keeper/msg_server.go | 72 + .../controller/keeper/msg_server_test.go | 200 + .../controller/keeper/params.go | 2 +- .../controller/keeper/params_test.go | 2 +- .../controller/keeper/relay.go | 66 +- .../controller/keeper/relay_test.go | 41 +- .../controller/migrations/v6/migrations.go | 77 + .../migrations/v6/migrations_test.go | 200 + .../controller/types/codec.go | 15 + .../controller/types/controller.pb.go | 8 +- .../controller/types/errors.go | 4 +- .../controller/types/msgs.go | 87 + .../controller/types/msgs_test.go | 196 + .../controller/types/params.go | 6 +- .../controller/types/params_test.go | 2 +- .../controller/types/query.pb.go | 492 +- .../controller/types/query.pb.gw.go | 132 +- .../controller/types/tx.pb.go | 1212 ++ modules/apps/27-interchain-accounts/doc.go | 8 + .../{ => genesis}/types/genesis.go | 13 +- .../{ => genesis}/types/genesis.pb.go | 175 +- .../{ => genesis}/types/genesis_test.go | 99 +- .../host/client/cli/cli.go | 20 +- .../host/client/cli/query.go | 10 +- .../host/client/cli/tx.go | 148 + .../host/client/cli/tx_test.go | 155 + .../27-interchain-accounts/host/ibc_module.go | 30 +- .../host/ibc_module_test.go | 140 +- .../host/keeper/account.go | 17 +- .../host/keeper/account_test.go | 33 - .../host/keeper/events.go | 4 +- .../host/keeper/genesis.go | 13 +- .../host/keeper/genesis_test.go | 30 +- .../host/keeper/grpc_query.go | 2 +- .../host/keeper/grpc_query_test.go | 2 +- .../host/keeper/handshake.go | 59 +- .../host/keeper/handshake_test.go | 147 +- .../host/keeper/keeper.go | 49 +- .../host/keeper/keeper_test.go | 47 +- .../host/keeper/params.go | 2 +- .../host/keeper/params_test.go | 2 +- .../host/keeper/relay.go | 49 +- .../host/keeper/relay_test.go | 84 +- .../host/types/errors.go | 4 +- .../host/types/host.pb.go | 8 +- .../27-interchain-accounts/host/types/keys.go | 5 +- .../host/types/params.go | 8 +- .../host/types/params_test.go | 2 +- .../host/types/query.pb.go | 10 +- .../host/types/query.pb.gw.go | 9 +- modules/apps/27-interchain-accounts/module.go | 115 +- .../27-interchain-accounts/module_test.go | 21 +- .../simulation/decoder.go | 30 + .../simulation/decoder_test.go | 64 + .../simulation/genesis.go | 70 + .../simulation/genesis_test.go | 56 + .../27-interchain-accounts/types/account.go | 25 +- .../types/account.pb.go | 26 +- .../types/account_test.go | 8 +- .../27-interchain-accounts/types/codec.go | 17 +- .../types/codec_test.go | 66 +- .../27-interchain-accounts/types/errors.go | 37 +- .../types/expected_keepers.go | 11 +- .../apps/27-interchain-accounts/types/keys.go | 25 +- .../27-interchain-accounts/types/keys_test.go | 10 +- .../27-interchain-accounts/types/metadata.go | 20 +- .../types/metadata.pb.go | 44 +- .../types/metadata_test.go | 4 +- .../27-interchain-accounts/types/packet.go | 29 +- .../27-interchain-accounts/types/packet.pb.go | 44 +- .../types/packet_test.go | 11 +- .../apps/27-interchain-accounts/types/port.go | 6 +- .../27-interchain-accounts/types/port_test.go | 6 +- .../27-interchain-accounts/types/router.go | 12 + modules/apps/29-fee/client/cli/cli.go | 3 +- modules/apps/29-fee/client/cli/query.go | 12 +- modules/apps/29-fee/client/cli/tx.go | 6 +- modules/apps/29-fee/doc.go | 9 + modules/apps/29-fee/fee_test.go | 10 +- modules/apps/29-fee/ibc_middleware.go | 47 +- modules/apps/29-fee/ibc_middleware_test.go | 40 +- modules/apps/29-fee/ica_test.go | 27 +- modules/apps/29-fee/keeper/escrow.go | 26 +- modules/apps/29-fee/keeper/escrow_test.go | 42 +- modules/apps/29-fee/keeper/events.go | 31 +- modules/apps/29-fee/keeper/events_test.go | 103 +- modules/apps/29-fee/keeper/genesis.go | 2 +- modules/apps/29-fee/keeper/genesis_test.go | 10 +- modules/apps/29-fee/keeper/grpc_query.go | 21 +- modules/apps/29-fee/keeper/grpc_query_test.go | 34 +- modules/apps/29-fee/keeper/keeper.go | 36 +- modules/apps/29-fee/keeper/keeper_test.go | 30 +- modules/apps/29-fee/keeper/msg_server.go | 52 +- modules/apps/29-fee/keeper/msg_server_test.go | 126 +- modules/apps/29-fee/keeper/relay.go | 27 +- modules/apps/29-fee/keeper/relay_test.go | 14 +- modules/apps/29-fee/module.go | 42 +- modules/apps/29-fee/transfer_test.go | 13 +- modules/apps/29-fee/types/ack.pb.go | 46 +- modules/apps/29-fee/types/errors.go | 22 +- modules/apps/29-fee/types/events.go | 3 + modules/apps/29-fee/types/expected_keepers.go | 11 +- modules/apps/29-fee/types/fee.go | 29 +- modules/apps/29-fee/types/fee.pb.go | 70 +- modules/apps/29-fee/types/fee_test.go | 24 +- modules/apps/29-fee/types/genesis.go | 23 +- modules/apps/29-fee/types/genesis.pb.go | 90 +- modules/apps/29-fee/types/genesis_test.go | 16 +- modules/apps/29-fee/types/keys.go | 31 +- modules/apps/29-fee/types/keys_test.go | 8 +- modules/apps/29-fee/types/metadata.pb.go | 8 +- modules/apps/29-fee/types/msgs.go | 45 +- modules/apps/29-fee/types/msgs_test.go | 59 +- modules/apps/29-fee/types/query.pb.go | 362 +- modules/apps/29-fee/types/query.pb.gw.go | 54 +- modules/apps/29-fee/types/tx.pb.go | 98 +- modules/apps/transfer/client/cli/cli.go | 3 +- modules/apps/transfer/client/cli/query.go | 2 +- modules/apps/transfer/client/cli/tx.go | 15 +- modules/apps/transfer/doc.go | 8 + modules/apps/transfer/ibc_module.go | 45 +- modules/apps/transfer/ibc_module_test.go | 12 +- modules/apps/transfer/keeper/MBT_README.md | 36 +- modules/apps/transfer/keeper/encoding.go | 2 +- modules/apps/transfer/keeper/genesis.go | 2 +- modules/apps/transfer/keeper/genesis_test.go | 2 +- modules/apps/transfer/keeper/grpc_query.go | 8 +- .../apps/transfer/keeper/grpc_query_test.go | 12 +- modules/apps/transfer/keeper/keeper.go | 26 +- modules/apps/transfer/keeper/keeper_test.go | 4 +- .../apps/transfer/keeper/mbt_relay_test.go | 72 +- modules/apps/transfer/keeper/migrations.go | 3 +- .../apps/transfer/keeper/migrations_test.go | 8 +- modules/apps/transfer/keeper/msg_server.go | 31 +- .../apps/transfer/keeper/msg_server_test.go | 127 + modules/apps/transfer/keeper/params.go | 2 +- modules/apps/transfer/keeper/params_test.go | 2 +- modules/apps/transfer/keeper/relay.go | 94 +- modules/apps/transfer/keeper/relay_test.go | 199 +- modules/apps/transfer/module.go | 46 +- modules/apps/transfer/simulation/decoder.go | 2 +- .../apps/transfer/simulation/decoder_test.go | 6 +- modules/apps/transfer/simulation/genesis.go | 2 +- .../apps/transfer/simulation/genesis_test.go | 9 +- modules/apps/transfer/simulation/params.go | 31 - .../apps/transfer/simulation/params_test.go | 36 - modules/apps/transfer/transfer_test.go | 14 +- modules/apps/transfer/types/authz.pb.go | 695 + modules/apps/transfer/types/codec.go | 40 + modules/apps/transfer/types/codec_test.go | 25 + modules/apps/transfer/types/coin.go | 3 +- modules/apps/transfer/types/errors.go | 19 +- modules/apps/transfer/types/events.go | 1 + .../apps/transfer/types/expected_keepers.go | 12 +- modules/apps/transfer/types/genesis.go | 2 +- modules/apps/transfer/types/genesis.pb.go | 40 +- modules/apps/transfer/types/genesis_test.go | 2 +- modules/apps/transfer/types/keys_test.go | 2 +- modules/apps/transfer/types/msgs.go | 32 +- modules/apps/transfer/types/msgs_test.go | 50 +- modules/apps/transfer/types/packet.go | 16 +- modules/apps/transfer/types/packet.pb.go | 88 +- modules/apps/transfer/types/packet_test.go | 25 +- modules/apps/transfer/types/params.go | 10 +- modules/apps/transfer/types/params_test.go | 8 +- modules/apps/transfer/types/query.pb.go | 98 +- modules/apps/transfer/types/query.pb.gw.go | 29 +- modules/apps/transfer/types/trace.go | 32 +- modules/apps/transfer/types/trace_test.go | 94 +- modules/apps/transfer/types/transfer.pb.go | 38 +- .../transfer/types/transfer_authorization.go | 128 + .../types/transfer_authorization_test.go | 282 + modules/apps/transfer/types/tx.pb.go | 155 +- .../types/{ack_test.go => types_test.go} | 13 +- modules/core/02-client/abci.go | 24 +- modules/core/02-client/abci_test.go | 82 +- modules/core/02-client/client/cli/cli.go | 6 +- modules/core/02-client/client/cli/query.go | 30 +- modules/core/02-client/client/cli/tx.go | 83 +- .../core/02-client/client/proposal_handler.go | 20 +- modules/core/02-client/client/utils/utils.go | 44 +- modules/core/02-client/doc.go | 7 +- modules/core/02-client/genesis.go | 20 +- modules/core/02-client/keeper/client.go | 227 +- modules/core/02-client/keeper/client_test.go | 434 +- modules/core/02-client/keeper/encoding.go | 4 +- modules/core/02-client/keeper/events.go | 104 +- modules/core/02-client/keeper/grpc_query.go | 42 +- .../core/02-client/keeper/grpc_query_test.go | 28 +- modules/core/02-client/keeper/keeper.go | 87 +- modules/core/02-client/keeper/keeper_test.go | 175 +- modules/core/02-client/keeper/migrations.go | 14 +- modules/core/02-client/keeper/params.go | 2 +- modules/core/02-client/keeper/params_test.go | 4 +- modules/core/02-client/keeper/proposal.go | 41 +- .../core/02-client/keeper/proposal_test.go | 54 +- modules/core/02-client/legacy/v100/genesis.go | 151 - .../02-client/legacy/v100/genesis_test.go | 308 - .../02-client/legacy/v100/solomachine.pb.go | 4121 ----- modules/core/02-client/legacy/v100/store.go | 180 - .../core/02-client/legacy/v100/store_test.go | 231 - .../migrations/v7/expected_keepers.go | 14 + .../core/02-client/migrations/v7/genesis.go | 77 + .../02-client/migrations/v7/genesis_test.go | 139 + .../v100 => migrations/v7}/solomachine.go | 82 +- .../migrations/v7}/solomachine.pb.go | 184 +- modules/core/02-client/migrations/v7/store.go | 206 + .../02-client/migrations/v7/store_test.go | 168 + modules/core/02-client/module.go | 6 +- modules/core/02-client/proposal_handler.go | 11 +- .../core/02-client/proposal_handler_test.go | 21 +- modules/core/02-client/simulation/decoder.go | 6 +- .../core/02-client/simulation/decoder_test.go | 14 +- modules/core/02-client/simulation/genesis.go | 2 +- modules/core/02-client/types/client.go | 16 +- modules/core/02-client/types/client.pb.go | 98 +- modules/core/02-client/types/client_test.go | 4 +- modules/core/02-client/types/codec.go | 91 +- modules/core/02-client/types/codec_test.go | 91 +- modules/core/02-client/types/encoding.go | 26 +- modules/core/02-client/types/encoding_test.go | 12 +- modules/core/02-client/types/errors.go | 58 +- modules/core/02-client/types/events.go | 9 +- modules/core/02-client/types/genesis.go | 10 +- modules/core/02-client/types/genesis.pb.go | 8 +- modules/core/02-client/types/genesis_test.go | 201 +- modules/core/02-client/types/height.go | 15 +- modules/core/02-client/types/height_test.go | 2 +- modules/core/02-client/types/keys.go | 16 +- modules/core/02-client/types/keys_test.go | 37 +- modules/core/02-client/types/msgs.go | 78 +- modules/core/02-client/types/msgs_test.go | 68 +- modules/core/02-client/types/params.go | 2 +- modules/core/02-client/types/params_test.go | 2 +- modules/core/02-client/types/proposal.go | 23 +- modules/core/02-client/types/proposal_test.go | 36 +- modules/core/02-client/types/query.go | 2 +- modules/core/02-client/types/query.pb.go | 144 +- modules/core/02-client/types/query.pb.gw.go | 49 +- modules/core/02-client/types/tx.pb.go | 114 +- modules/core/03-connection/client/cli/cli.go | 3 +- .../core/03-connection/client/cli/query.go | 37 +- .../core/03-connection/client/utils/utils.go | 30 +- modules/core/03-connection/doc.go | 12 + modules/core/03-connection/genesis.go | 4 +- modules/core/03-connection/keeper/events.go | 18 +- .../core/03-connection/keeper/grpc_query.go | 30 +- .../03-connection/keeper/grpc_query_test.go | 15 +- .../core/03-connection/keeper/handshake.go | 58 +- .../03-connection/keeper/handshake_test.go | 27 +- modules/core/03-connection/keeper/keeper.go | 58 +- .../core/03-connection/keeper/keeper_test.go | 24 +- modules/core/03-connection/keeper/params.go | 2 +- .../core/03-connection/keeper/params_test.go | 4 +- modules/core/03-connection/keeper/verify.go | 189 +- .../core/03-connection/keeper/verify_test.go | 63 +- modules/core/03-connection/module.go | 6 +- .../core/03-connection/simulation/decoder.go | 4 +- .../03-connection/simulation/decoder_test.go | 8 +- .../core/03-connection/simulation/genesis.go | 2 +- modules/core/03-connection/types/codec.go | 2 +- .../core/03-connection/types/connection.go | 21 +- .../core/03-connection/types/connection.pb.go | 92 +- .../03-connection/types/connection_test.go | 8 +- modules/core/03-connection/types/errors.go | 22 +- modules/core/03-connection/types/events.go | 4 +- .../03-connection/types/expected_keepers.go | 4 +- modules/core/03-connection/types/genesis.go | 4 +- .../core/03-connection/types/genesis.pb.go | 48 +- .../core/03-connection/types/genesis_test.go | 6 +- modules/core/03-connection/types/keys.go | 8 +- modules/core/03-connection/types/keys_test.go | 2 +- modules/core/03-connection/types/msgs.go | 96 +- modules/core/03-connection/types/msgs_test.go | 84 +- .../core/03-connection/types/params_test.go | 2 +- modules/core/03-connection/types/query.go | 4 +- modules/core/03-connection/types/query.pb.go | 465 +- .../core/03-connection/types/query.pb.gw.go | 94 +- modules/core/03-connection/types/tx.pb.go | 224 +- modules/core/03-connection/types/version.go | 18 +- .../core/03-connection/types/version_test.go | 6 +- modules/core/04-channel/client/cli/cli.go | 2 +- modules/core/04-channel/client/cli/query.go | 26 +- modules/core/04-channel/client/utils/utils.go | 27 +- modules/core/04-channel/doc.go | 17 + modules/core/04-channel/genesis.go | 4 +- modules/core/04-channel/keeper/events.go | 69 +- modules/core/04-channel/keeper/grpc_query.go | 31 +- .../core/04-channel/keeper/grpc_query_test.go | 16 +- modules/core/04-channel/keeper/handshake.go | 90 +- .../core/04-channel/keeper/handshake_test.go | 49 +- modules/core/04-channel/keeper/keeper.go | 88 +- modules/core/04-channel/keeper/keeper_test.go | 96 +- modules/core/04-channel/keeper/packet.go | 190 +- modules/core/04-channel/keeper/packet_test.go | 322 +- modules/core/04-channel/keeper/timeout.go | 56 +- .../core/04-channel/keeper/timeout_test.go | 261 +- modules/core/04-channel/module.go | 6 +- modules/core/04-channel/simulation/decoder.go | 4 +- .../04-channel/simulation/decoder_test.go | 8 +- modules/core/04-channel/simulation/genesis.go | 2 +- .../core/04-channel/types/acknowledgement.go | 10 +- .../04-channel/types/acknowledgement_test.go | 24 +- modules/core/04-channel/types/channel.go | 20 +- modules/core/04-channel/types/channel.pb.go | 130 +- modules/core/04-channel/types/channel_test.go | 2 +- modules/core/04-channel/types/codec.go | 2 +- modules/core/04-channel/types/errors.go | 53 +- modules/core/04-channel/types/events.go | 8 +- .../core/04-channel/types/expected_keepers.go | 4 +- modules/core/04-channel/types/genesis.go | 4 +- modules/core/04-channel/types/genesis.pb.go | 68 +- modules/core/04-channel/types/genesis_test.go | 2 +- modules/core/04-channel/types/keys.go | 13 +- modules/core/04-channel/types/keys_test.go | 2 +- modules/core/04-channel/types/msgs.go | 220 +- modules/core/04-channel/types/msgs_test.go | 59 +- modules/core/04-channel/types/packet.go | 34 +- modules/core/04-channel/types/packet_test.go | 4 +- modules/core/04-channel/types/query.go | 4 +- modules/core/04-channel/types/query.pb.go | 198 +- modules/core/04-channel/types/query.pb.gw.go | 69 +- modules/core/04-channel/types/tx.pb.go | 296 +- modules/core/04-channel/types/upgrade.pb.go | 50 +- modules/core/05-port/doc.go | 7 + modules/core/05-port/keeper/keeper.go | 12 +- modules/core/05-port/keeper/keeper_test.go | 4 +- modules/core/05-port/module.go | 10 +- modules/core/05-port/types/errors.go | 10 +- modules/core/05-port/types/module.go | 13 +- modules/core/05-port/types/query.go | 9 - modules/core/05-port/types/query.pb.go | 843 - modules/core/23-commitment/types/codec.go | 2 +- .../core/23-commitment/types/commitment.pb.go | 51 +- .../23-commitment/types/commitment_test.go | 7 +- modules/core/23-commitment/types/errors.go | 8 +- modules/core/23-commitment/types/merkle.go | 61 +- .../core/23-commitment/types/merkle_test.go | 10 +- modules/core/23-commitment/types/utils.go | 8 +- .../core/23-commitment/types/utils_test.go | 2 +- modules/core/24-host/client_keys.go | 16 +- modules/core/24-host/doc.go | 7 +- modules/core/24-host/errors.go | 8 +- modules/core/24-host/parse.go | 50 +- modules/core/24-host/parse_test.go | 35 +- modules/core/24-host/validate.go | 18 +- modules/core/24-host/validate_test.go | 4 +- modules/core/ante/ante.go | 42 +- modules/core/ante/ante_test.go | 140 +- modules/core/client/cli/cli.go | 15 +- modules/core/client/query.go | 8 +- modules/core/exported/client.go | 191 +- modules/core/exported/commitment.go | 4 +- modules/core/exported/connection.go | 1 - modules/core/exported/expected_keepers.go | 15 + .../{24-host/keys.go => exported/module.go} | 11 +- modules/core/genesis.go | 12 +- modules/core/genesis_test.go | 59 +- modules/core/keeper/grpc_query.go | 16 +- modules/core/keeper/keeper.go | 41 +- modules/core/keeper/keeper_test.go | 49 +- modules/core/keeper/migrations.go | 13 +- modules/core/keeper/msg_server.go | 216 +- modules/core/keeper/msg_server_test.go | 228 +- modules/core/legacy/v100/genesis.go | 54 - modules/core/legacy/v100/genesis_test.go | 178 - modules/core/migrations/v7/genesis.go | 42 + modules/core/migrations/v7/genesis_test.go | 164 + modules/core/module.go | 87 +- modules/core/simulation/decoder.go | 12 +- modules/core/simulation/decoder_test.go | 16 +- modules/core/simulation/genesis.go | 20 +- modules/core/simulation/genesis_test.go | 13 +- modules/core/spec/01_concepts.md | 117 +- modules/core/types/codec.go | 14 +- modules/core/types/genesis.go | 6 +- modules/core/types/genesis.pb.go | 46 +- modules/core/types/query.go | 14 +- .../06-solomachine/client_state.go | 247 + .../06-solomachine/client_state_test.go | 830 + modules/light-clients/06-solomachine/codec.go | 42 + .../{types => }/consensus_state.go | 23 +- .../{types => }/consensus_state_test.go | 33 +- modules/light-clients/06-solomachine/doc.go | 8 +- .../light-clients/06-solomachine/errors.go | 13 + .../06-solomachine/{types => }/header.go | 38 +- .../06-solomachine/{types => }/header_test.go | 37 +- modules/light-clients/06-solomachine/keys.go | 5 + .../06-solomachine/misbehaviour.go | 68 + .../06-solomachine/misbehaviour_handle.go | 75 + .../{types => }/misbehaviour_test.go | 64 +- .../light-clients/06-solomachine/module.go | 54 +- modules/light-clients/06-solomachine/proof.go | 43 + .../06-solomachine/proof_test.go | 67 + .../{types => }/proposal_handle.go | 41 +- .../06-solomachine/proposal_handle_test.go | 83 + .../06-solomachine/{types => }/solomachine.go | 14 +- .../06-solomachine/solomachine.pb.go | 2236 +++ .../06-solomachine/solomachine_test.go | 204 + .../06-solomachine/types/client_state.go | 491 - .../06-solomachine/types/client_state_test.go | 844 - .../06-solomachine/types/codec.go | 131 - .../06-solomachine/types/codec_test.go | 186 - .../06-solomachine/types/errors.go | 18 - .../06-solomachine/types/misbehaviour.go | 77 - .../types/misbehaviour_handle.go | 86 - .../types/misbehaviour_handle_test.go | 264 - .../06-solomachine/types/proof.go | 477 - .../06-solomachine/types/proof_test.go | 103 - .../types/proposal_handle_test.go | 88 - .../06-solomachine/types/solomachine_test.go | 113 - .../06-solomachine/types/update.go | 90 - .../06-solomachine/types/update_test.go | 181 - .../light-clients/06-solomachine/update.go | 107 + .../06-solomachine/update_test.go | 546 + .../07-tendermint/client_state.go | 340 + .../07-tendermint/client_state_test.go | 678 + .../07-tendermint/{types => }/codec.go | 8 +- .../{types => }/consensus_state.go | 16 +- .../{types => }/consensus_state_test.go | 24 +- modules/light-clients/07-tendermint/doc.go | 6 +- modules/light-clients/07-tendermint/errors.go | 22 + .../07-tendermint/{types => }/fraction.go | 2 +- .../07-tendermint/{types => }/genesis.go | 6 +- .../07-tendermint/{types => }/genesis_test.go | 38 +- .../07-tendermint/{types => }/header.go | 28 +- .../07-tendermint/{types => }/header_test.go | 10 +- modules/light-clients/07-tendermint/keys.go | 5 + .../migrations/expected_keepers.go | 16 + .../07-tendermint/migrations/migrations.go | 46 + .../migrations/migrations_test.go | 178 + .../07-tendermint/{types => }/misbehaviour.go | 55 +- .../07-tendermint/misbehaviour_handle.go | 170 + .../07-tendermint/misbehaviour_handle_test.go | 648 + .../{types => }/misbehaviour_test.go | 79 +- modules/light-clients/07-tendermint/module.go | 54 +- .../{types => }/proposal_handle.go | 35 +- .../{types => }/proposal_handle_test.go | 103 +- .../07-tendermint/{types => }/store.go | 82 +- .../07-tendermint/{types => }/store_test.go | 97 +- .../{types => }/tendermint.pb.go | 180 +- .../{types => }/tendermint_test.go | 14 +- .../07-tendermint/types/client_state.go | 583 - .../07-tendermint/types/client_state_test.go | 880 - .../07-tendermint/types/errors.go | 26 - .../types/misbehaviour_handle.go | 143 - .../types/misbehaviour_handle_test.go | 425 - .../07-tendermint/types/update.go | 263 - .../07-tendermint/types/update_test.go | 450 - modules/light-clients/07-tendermint/update.go | 223 + .../07-tendermint/update_test.go | 833 + .../07-tendermint/{types => }/upgrade.go | 61 +- .../07-tendermint/{types => }/upgrade_test.go | 104 +- modules/light-clients/09-localhost/doc.go | 5 - modules/light-clients/09-localhost/module.go | 10 - .../09-localhost/types/client_state.go | 340 - .../09-localhost/types/client_state_test.go | 526 - .../light-clients/09-localhost/types/codec.go | 16 - .../09-localhost/types/errors.go | 10 - .../light-clients/09-localhost/types/keys.go | 6 - .../09-localhost/types/localhost.pb.go | 369 - .../09-localhost/types/localhost_test.go | 41 - proto/buf.gen.gogo.yaml | 8 + proto/buf.gen.swagger.yaml | 5 + proto/buf.lock | 23 + proto/buf.yaml | 14 +- proto/ibc/applications/fee/v1/ack.proto | 2 +- proto/ibc/applications/fee/v1/fee.proto | 2 +- proto/ibc/applications/fee/v1/genesis.proto | 2 +- proto/ibc/applications/fee/v1/metadata.proto | 2 +- proto/ibc/applications/fee/v1/query.proto | 8 +- proto/ibc/applications/fee/v1/tx.proto | 2 +- .../controller/v1/controller.proto | 2 +- .../controller/v1/query.proto | 20 +- .../controller/v1/tx.proto | 51 + .../{ => genesis}/v1/genesis.proto | 14 +- .../interchain_accounts/host/v1/host.proto | 2 +- .../interchain_accounts/host/v1/query.proto | 2 +- .../interchain_accounts/v1/account.proto | 4 +- .../interchain_accounts/v1/metadata.proto | 2 +- .../interchain_accounts/v1/packet.proto | 2 +- .../ibc/applications/transfer/v1/authz.proto | 31 + .../applications/transfer/v1/genesis.proto | 2 +- .../ibc/applications/transfer/v1/query.proto | 6 +- .../applications/transfer/v1/transfer.proto | 2 +- proto/ibc/applications/transfer/v1/tx.proto | 9 +- .../ibc/applications/transfer/v2/packet.proto | 4 +- proto/ibc/core/channel/v1/channel.proto | 2 +- proto/ibc/core/channel/v1/genesis.proto | 2 +- proto/ibc/core/channel/v1/query.proto | 2 +- proto/ibc/core/channel/v1/tx.proto | 5 +- proto/ibc/core/channel/v1/upgrade.proto | 2 +- proto/ibc/core/client/v1/client.proto | 13 +- proto/ibc/core/client/v1/genesis.proto | 2 +- proto/ibc/core/client/v1/query.proto | 6 +- proto/ibc/core/client/v1/tx.proto | 15 +- proto/ibc/core/commitment/v1/commitment.proto | 6 +- proto/ibc/core/connection/v1/connection.proto | 2 +- proto/ibc/core/connection/v1/genesis.proto | 2 +- proto/ibc/core/connection/v1/query.proto | 16 +- proto/ibc/core/connection/v1/tx.proto | 6 +- proto/ibc/core/types/v1/genesis.proto | 2 +- .../lightclients/localhost/v1/localhost.proto | 18 - .../solomachine/v1/solomachine.proto | 189 - .../solomachine/v2/solomachine.proto | 2 +- .../solomachine/v3/solomachine.proto | 99 + .../tendermint/v1/tendermint.proto | 9 +- scripts/linting/lint-changed-go-files.sh | 14 + scripts/linting/lint-changed-md-files.sh | 13 + scripts/protoc-swagger-gen.sh | 15 +- scripts/protocgen.sh | 35 +- testing/README.md | 227 +- testing/app.go | 28 +- testing/chain.go | 121 +- testing/chain_test.go | 17 +- testing/config.go | 30 +- testing/coordinator.go | 6 +- testing/endpoint.go | 147 +- testing/events.go | 75 +- testing/mock/README.md | 2 +- testing/mock/ack.go | 14 +- testing/mock/ibc_app.go | 14 +- testing/mock/ibc_module.go | 26 +- testing/mock/ibc_module_test.go | 6 +- testing/mock/mock.go | 48 +- testing/mock/privval_test.go | 8 +- testing/path.go | 2 +- testing/sdk_test.go | 279 - testing/simapp/README.md | 36 +- testing/simapp/ante_handler.go | 32 +- testing/simapp/app.go | 450 +- testing/simapp/encoding.go | 2 +- testing/simapp/export.go | 16 +- testing/simapp/genesis_account_test.go | 2 +- testing/simapp/helpers/test_helpers.go | 80 - testing/simapp/params/amino.go | 2 +- testing/simapp/params/doc.go | 4 +- testing/simapp/sim_bench_test.go | 7 +- testing/simapp/sim_test.go | 27 +- testing/simapp/simd/cmd/cmd_test.go | 6 +- testing/simapp/simd/cmd/genaccounts.go | 34 +- testing/simapp/simd/cmd/genaccounts_test.go | 6 +- testing/simapp/simd/cmd/root.go | 45 +- testing/simapp/simd/cmd/testnet.go | 396 - testing/simapp/simd/main.go | 6 +- testing/simapp/state.go | 11 +- testing/simapp/test_helpers.go | 352 +- testing/simapp/upgrades/upgrades.go | 23 + testing/simapp/upgrades/v6/upgrades.go | 36 + testing/simapp/upgrades/v7/upgrades.go | 42 + testing/simapp/utils.go | 20 +- testing/solomachine.go | 479 +- testing/types/expected_keepers.go | 12 + testing/utils.go | 6 +- testing/values.go | 25 +- third_party/proto/buf.yaml | 33 - .../proto/cosmos/auth/v1beta1/auth.proto | 51 - .../proto/cosmos/auth/v1beta1/genesis.proto | 18 - .../proto/cosmos/auth/v1beta1/query.proto | 48 - .../base/query/v1beta1/pagination.proto | 50 - .../proto/cosmos/base/v1beta1/coin.proto | 40 - .../cosmos/upgrade/v1beta1/upgrade.proto | 59 - third_party/proto/cosmos_proto/cosmos.proto | 16 - third_party/proto/gogoproto/gogo.proto | 145 - .../proto/google/api/annotations.proto | 31 - third_party/proto/google/api/http.proto | 318 - third_party/proto/google/protobuf/any.proto | 161 - third_party/proto/proofs.proto | 234 - .../proto/tendermint/crypto/keys.proto | 17 - .../proto/tendermint/crypto/proof.proto | 41 - .../proto/tendermint/libs/bits/types.proto | 9 - .../proto/tendermint/types/types.proto | 157 - .../proto/tendermint/types/validator.proto | 25 - .../proto/tendermint/version/types.proto | 24 - 830 files changed, 55433 insertions(+), 34701 deletions(-) create mode 100644 .github/compatibility-test-matrices/main/client-chain-a.json create mode 100644 .github/compatibility-test-matrices/main/client-chain-b.json create mode 100644 .github/compatibility-test-matrices/main/connection-chain-a.json create mode 100644 .github/compatibility-test-matrices/main/connection-chain-b.json create mode 100644 .github/compatibility-test-matrices/main/ica-chain-a.json create mode 100644 .github/compatibility-test-matrices/main/ica-gov-chain-a.json create mode 100644 .github/compatibility-test-matrices/main/ica-groups-chain-a.json create mode 100644 .github/compatibility-test-matrices/main/incentivized-ica-chain-a.json create mode 100644 .github/compatibility-test-matrices/main/incentivized-transfer-chain-a.json create mode 100644 .github/compatibility-test-matrices/main/incentivized-transfer-chain-b.json create mode 100644 .github/compatibility-test-matrices/main/transfer-chain-a.json create mode 100644 .github/compatibility-test-matrices/main/transfer-chain-b.json create mode 100644 .github/compatibility-test-matrices/release-v3.4.x/transfer-chain-a.json create mode 100644 .github/compatibility-test-matrices/release-v3.4.x/transfer-chain-b.json create mode 100644 .github/compatibility-test-matrices/release-v4.2.x/incentivized-transfer-chain-a.json create mode 100644 .github/compatibility-test-matrices/release-v4.2.x/incentivized-transfer-chain-b.json create mode 100644 .github/compatibility-test-matrices/release-v4.2.x/transfer-chain-a.json create mode 100644 .github/compatibility-test-matrices/release-v4.2.x/transfer-chain-b.json create mode 100644 .github/compatibility-test-matrices/release-v4.3.x/incentivized-transfer-chain-a.json create mode 100644 .github/compatibility-test-matrices/release-v4.3.x/incentivized-transfer-chain-b.json create mode 100644 .github/compatibility-test-matrices/release-v4.3.x/transfer-chain-a.json create mode 100644 .github/compatibility-test-matrices/release-v4.3.x/transfer-chain-b.json create mode 100644 .github/compatibility-test-matrices/release-v5.2.x/incentivized-transfer-chain-a.json create mode 100644 .github/compatibility-test-matrices/release-v5.2.x/incentivized-transfer-chain-b.json create mode 100644 .github/compatibility-test-matrices/release-v5.2.x/transfer-chain-a.json create mode 100644 .github/compatibility-test-matrices/release-v5.2.x/transfer-chain-b.json create mode 100644 .github/compatibility-test-matrices/release-v6.1.x/client-chain-a.json create mode 100644 .github/compatibility-test-matrices/release-v6.1.x/client-chain-b.json create mode 100644 .github/compatibility-test-matrices/release-v6.1.x/connection-chain-a.json create mode 100644 .github/compatibility-test-matrices/release-v6.1.x/connection-chain-b.json create mode 100644 .github/compatibility-test-matrices/release-v6.1.x/ica-chain-a.json create mode 100644 .github/compatibility-test-matrices/release-v6.1.x/ica-chain-b.json create mode 100644 .github/compatibility-test-matrices/release-v6.1.x/ica-gov-chain-a.json create mode 100644 .github/compatibility-test-matrices/release-v6.1.x/ica-gov-chain-b.json create mode 100644 .github/compatibility-test-matrices/release-v6.1.x/ica-groups-chain-a.json create mode 100644 .github/compatibility-test-matrices/release-v6.1.x/ica-groups-chain-b.json create mode 100644 .github/compatibility-test-matrices/release-v6.1.x/incentivized-ica-chain-a.json create mode 100644 .github/compatibility-test-matrices/release-v6.1.x/incentivized-ica-chain-b.json create mode 100644 .github/compatibility-test-matrices/release-v6.1.x/incentivized-transfer-chain-a.json create mode 100644 .github/compatibility-test-matrices/release-v6.1.x/incentivized-transfer-chain-b.json create mode 100644 .github/compatibility-test-matrices/release-v6.1.x/transfer-chain-a.json create mode 100644 .github/compatibility-test-matrices/release-v6.1.x/transfer-chain-b.json create mode 100644 .github/compatibility-test-matrices/release-v7.0.x/client-chain-a.json create mode 100644 .github/compatibility-test-matrices/release-v7.0.x/client-chain-b.json create mode 100644 .github/compatibility-test-matrices/release-v7.0.x/connection-chain-a.json create mode 100644 .github/compatibility-test-matrices/release-v7.0.x/connection-chain-b.json create mode 100644 .github/compatibility-test-matrices/release-v7.0.x/ica-chain-a.json create mode 100644 .github/compatibility-test-matrices/release-v7.0.x/ica-chain-b.json create mode 100644 .github/compatibility-test-matrices/release-v7.0.x/ica-gov-chain-a.json create mode 100644 .github/compatibility-test-matrices/release-v7.0.x/ica-gov-chain-b.json create mode 100644 .github/compatibility-test-matrices/release-v7.0.x/ica-groups-chain-a.json create mode 100644 .github/compatibility-test-matrices/release-v7.0.x/ica-groups-chain-b.json create mode 100644 .github/compatibility-test-matrices/release-v7.0.x/incentivized-ica-chain-a.json create mode 100644 .github/compatibility-test-matrices/release-v7.0.x/incentivized-ica-chain-b.json create mode 100644 .github/compatibility-test-matrices/release-v7.0.x/incentivized-transfer-chain-a.json create mode 100644 .github/compatibility-test-matrices/release-v7.0.x/incentivized-transfer-chain-b.json create mode 100644 .github/compatibility-test-matrices/release-v7.0.x/transfer-authz-chain-a.json create mode 100644 .github/compatibility-test-matrices/release-v7.0.x/transfer-chain-a.json create mode 100644 .github/compatibility-test-matrices/release-v7.0.x/transfer-chain-b.json create mode 100644 .github/compatibility-test-matrices/unreleased/client.json create mode 100644 .github/compatibility-test-matrices/unreleased/connection.json create mode 100644 .github/compatibility-test-matrices/unreleased/incentivized-transfer.json create mode 100644 .github/compatibility-test-matrices/unreleased/transfer.json delete mode 100755 .github/scripts/go-imports.sh create mode 100644 .github/workflows/build-simd-image-from-tag.yml create mode 100644 .github/workflows/deploy-docs.yml create mode 100644 .github/workflows/e2e-compatibility-unreleased.yaml create mode 100644 .github/workflows/e2e-compatibility-workflow-call.yaml create mode 100644 .github/workflows/e2e-compatibility.yaml create mode 100644 .github/workflows/e2e-manual-icad.yaml create mode 100644 .github/workflows/e2e-manual-simd.yaml create mode 100644 .github/workflows/e2e-test-workflow-call.yml create mode 100644 .github/workflows/e2e-upgrade.yaml delete mode 100644 .github/workflows/goimports.yaml create mode 100644 .github/workflows/golangci.yml create mode 100644 .github/workflows/proto-registry.yml create mode 100644 .markdownlint.jsonc create mode 100644 .markdownlintignore create mode 100644 MAINTAINERSHIP.md create mode 100644 cliff.toml delete mode 100644 cmd/determine_simd_tag/main.go create mode 100644 contrib/devtools/Makefile create mode 100644 contrib/test_cover.sh create mode 100644 docs/DOCS_GUIDELINES.md delete mode 100644 docs/OLD_README.md create mode 100644 docs/apps/interchain-accounts/client.md create mode 100644 docs/apps/interchain-accounts/development.md create mode 100644 docs/apps/interchain-accounts/legacy/auth-modules.md create mode 100644 docs/apps/interchain-accounts/legacy/integration.md create mode 100644 docs/apps/interchain-accounts/legacy/keeper-api.md create mode 100644 docs/apps/interchain-accounts/messages.md delete mode 100644 docs/apps/interchain-accounts/transactions.md create mode 100644 docs/apps/transfer/authorizations.md create mode 100644 docs/architecture/adr-005-consensus-height-events.md create mode 100644 docs/architecture/adr-006-02-client-refactor.md create mode 100644 docs/architecture/adr-007-solomachine-signbytes.md create mode 100644 docs/architecture/adr-009-v6-ics27-msgserver.md create mode 100644 docs/architecture/adr-010-light-clients-as-sdk-modules.md create mode 100644 docs/assets/auth-module-decision-tree.png create mode 100644 docs/assets/ica/ica-pre-v6.png create mode 100644 docs/assets/ica/ica-v6.png create mode 100644 docs/dev/development-setup.md create mode 100644 docs/dev/go-style-guide.md create mode 100644 docs/dev/project-structure.md create mode 100644 docs/dev/pull-requests.md create mode 100644 docs/dev/release-management.md create mode 100644 docs/ibc/light-clients/client-state.md create mode 100644 docs/ibc/light-clients/consensus-state.md create mode 100644 docs/ibc/light-clients/genesis.md create mode 100644 docs/ibc/light-clients/overview.md create mode 100644 docs/ibc/light-clients/proofs.md create mode 100644 docs/ibc/light-clients/proposals.md create mode 100644 docs/ibc/light-clients/setup.md rename modules/light-clients/06-solomachine/spec/01_concepts.md => docs/ibc/light-clients/solomachine/concepts.md (75%) rename modules/light-clients/06-solomachine/spec/README.md => docs/ibc/light-clients/solomachine/solomachine.md (83%) rename modules/light-clients/06-solomachine/spec/02_state.md => docs/ibc/light-clients/solomachine/state.md (99%) rename modules/light-clients/06-solomachine/spec/03_state_transitions.md => docs/ibc/light-clients/solomachine/state_transitions.md (98%) create mode 100644 docs/ibc/light-clients/updates-and-misbehaviour.md create mode 100644 docs/ibc/light-clients/upgrades.md create mode 100644 docs/migrations/v4-to-v5.md create mode 100644 docs/migrations/v5-to-v6.md create mode 100644 docs/migrations/v6-to-v7.md rename docs/{apps/interchain-accounts/requirements.md => requirements/ics27-requirements.md} (88%) create mode 100644 docs/requirements/ics29-v1-requirements.md create mode 100644 docs/requirements/requirements-template.md create mode 100644 e2e/README.md delete mode 100644 e2e/fee_middleware_test.go create mode 100644 e2e/relayer/relayer.go create mode 100755 e2e/scripts/run-compatibility-tests.sh create mode 100644 e2e/semverutil/semver.go create mode 100644 e2e/semverutil/semver_test.go create mode 100644 e2e/tests/core/03-connection/connection_test.go create mode 100644 e2e/tests/core/client_test.go create mode 100644 e2e/tests/interchain_accounts/base_test.go create mode 100644 e2e/tests/interchain_accounts/gov_test.go create mode 100644 e2e/tests/interchain_accounts/groups_test.go create mode 100644 e2e/tests/interchain_accounts/incentivized_test.go create mode 100644 e2e/tests/interchain_accounts/intertx_incentivized_test.go create mode 100644 e2e/tests/interchain_accounts/intertx_test.go create mode 100644 e2e/tests/transfer/authz_test.go create mode 100644 e2e/tests/transfer/base_test.go create mode 100644 e2e/tests/transfer/incentivized_test.go create mode 100644 e2e/tests/upgrades/upgrade_test.go create mode 100644 e2e/testsuite/codec.go create mode 100644 e2e/testsuite/grpc_query.go delete mode 100644 e2e/testsuite/relayer.go create mode 100644 go.work.example create mode 100644 internal/errors/errors.go create mode 100644 internal/logging/logging.go create mode 100644 maintainership.png create mode 100644 modules/apps/27-interchain-accounts/controller/client/cli/tx.go create mode 100644 modules/apps/27-interchain-accounts/controller/keeper/migrations.go create mode 100644 modules/apps/27-interchain-accounts/controller/keeper/migrations_test.go create mode 100644 modules/apps/27-interchain-accounts/controller/keeper/msg_server.go create mode 100644 modules/apps/27-interchain-accounts/controller/keeper/msg_server_test.go create mode 100644 modules/apps/27-interchain-accounts/controller/migrations/v6/migrations.go create mode 100644 modules/apps/27-interchain-accounts/controller/migrations/v6/migrations_test.go create mode 100644 modules/apps/27-interchain-accounts/controller/types/codec.go create mode 100644 modules/apps/27-interchain-accounts/controller/types/msgs.go create mode 100644 modules/apps/27-interchain-accounts/controller/types/msgs_test.go create mode 100644 modules/apps/27-interchain-accounts/controller/types/tx.pb.go create mode 100644 modules/apps/27-interchain-accounts/doc.go rename modules/apps/27-interchain-accounts/{ => genesis}/types/genesis.go (88%) rename modules/apps/27-interchain-accounts/{ => genesis}/types/genesis.pb.go (85%) rename modules/apps/27-interchain-accounts/{ => genesis}/types/genesis_test.go (50%) create mode 100644 modules/apps/27-interchain-accounts/host/client/cli/tx.go create mode 100644 modules/apps/27-interchain-accounts/host/client/cli/tx_test.go delete mode 100644 modules/apps/27-interchain-accounts/host/keeper/account_test.go create mode 100644 modules/apps/27-interchain-accounts/simulation/decoder.go create mode 100644 modules/apps/27-interchain-accounts/simulation/decoder_test.go create mode 100644 modules/apps/27-interchain-accounts/simulation/genesis.go create mode 100644 modules/apps/27-interchain-accounts/simulation/genesis_test.go create mode 100644 modules/apps/27-interchain-accounts/types/router.go create mode 100644 modules/apps/29-fee/doc.go create mode 100644 modules/apps/transfer/doc.go create mode 100644 modules/apps/transfer/keeper/msg_server_test.go delete mode 100644 modules/apps/transfer/simulation/params.go delete mode 100644 modules/apps/transfer/simulation/params_test.go create mode 100644 modules/apps/transfer/types/authz.pb.go create mode 100644 modules/apps/transfer/types/codec_test.go create mode 100644 modules/apps/transfer/types/transfer_authorization.go create mode 100644 modules/apps/transfer/types/transfer_authorization_test.go rename modules/apps/transfer/types/{ack_test.go => types_test.go} (54%) delete mode 100644 modules/core/02-client/legacy/v100/genesis.go delete mode 100644 modules/core/02-client/legacy/v100/genesis_test.go delete mode 100644 modules/core/02-client/legacy/v100/solomachine.pb.go delete mode 100644 modules/core/02-client/legacy/v100/store.go delete mode 100644 modules/core/02-client/legacy/v100/store_test.go create mode 100644 modules/core/02-client/migrations/v7/expected_keepers.go create mode 100644 modules/core/02-client/migrations/v7/genesis.go create mode 100644 modules/core/02-client/migrations/v7/genesis_test.go rename modules/core/02-client/{legacy/v100 => migrations/v7}/solomachine.go (75%) rename modules/{light-clients/06-solomachine/types => core/02-client/migrations/v7}/solomachine.pb.go (92%) create mode 100644 modules/core/02-client/migrations/v7/store.go create mode 100644 modules/core/02-client/migrations/v7/store_test.go create mode 100644 modules/core/03-connection/doc.go create mode 100644 modules/core/04-channel/doc.go create mode 100644 modules/core/05-port/doc.go delete mode 100644 modules/core/05-port/types/query.go delete mode 100644 modules/core/05-port/types/query.pb.go create mode 100644 modules/core/exported/expected_keepers.go rename modules/core/{24-host/keys.go => exported/module.go} (68%) delete mode 100644 modules/core/legacy/v100/genesis.go delete mode 100644 modules/core/legacy/v100/genesis_test.go create mode 100644 modules/core/migrations/v7/genesis.go create mode 100644 modules/core/migrations/v7/genesis_test.go create mode 100644 modules/light-clients/06-solomachine/client_state.go create mode 100644 modules/light-clients/06-solomachine/client_state_test.go create mode 100644 modules/light-clients/06-solomachine/codec.go rename modules/light-clients/06-solomachine/{types => }/consensus_state.go (66%) rename modules/light-clients/06-solomachine/{types => }/consensus_state_test.go (56%) create mode 100644 modules/light-clients/06-solomachine/errors.go rename modules/light-clients/06-solomachine/{types => }/header.go (50%) rename modules/light-clients/06-solomachine/{types => }/header_test.go (61%) create mode 100644 modules/light-clients/06-solomachine/keys.go create mode 100644 modules/light-clients/06-solomachine/misbehaviour.go create mode 100644 modules/light-clients/06-solomachine/misbehaviour_handle.go rename modules/light-clients/06-solomachine/{types => }/misbehaviour_test.go (54%) create mode 100644 modules/light-clients/06-solomachine/proof.go create mode 100644 modules/light-clients/06-solomachine/proof_test.go rename modules/light-clients/06-solomachine/{types => }/proposal_handle.go (52%) create mode 100644 modules/light-clients/06-solomachine/proposal_handle_test.go rename modules/light-clients/06-solomachine/{types => }/solomachine.go (69%) create mode 100644 modules/light-clients/06-solomachine/solomachine.pb.go create mode 100644 modules/light-clients/06-solomachine/solomachine_test.go delete mode 100644 modules/light-clients/06-solomachine/types/client_state.go delete mode 100644 modules/light-clients/06-solomachine/types/client_state_test.go delete mode 100644 modules/light-clients/06-solomachine/types/codec.go delete mode 100644 modules/light-clients/06-solomachine/types/codec_test.go delete mode 100644 modules/light-clients/06-solomachine/types/errors.go delete mode 100644 modules/light-clients/06-solomachine/types/misbehaviour.go delete mode 100644 modules/light-clients/06-solomachine/types/misbehaviour_handle.go delete mode 100644 modules/light-clients/06-solomachine/types/misbehaviour_handle_test.go delete mode 100644 modules/light-clients/06-solomachine/types/proof.go delete mode 100644 modules/light-clients/06-solomachine/types/proof_test.go delete mode 100644 modules/light-clients/06-solomachine/types/proposal_handle_test.go delete mode 100644 modules/light-clients/06-solomachine/types/solomachine_test.go delete mode 100644 modules/light-clients/06-solomachine/types/update.go delete mode 100644 modules/light-clients/06-solomachine/types/update_test.go create mode 100644 modules/light-clients/06-solomachine/update.go create mode 100644 modules/light-clients/06-solomachine/update_test.go create mode 100644 modules/light-clients/07-tendermint/client_state.go create mode 100644 modules/light-clients/07-tendermint/client_state_test.go rename modules/light-clients/07-tendermint/{types => }/codec.go (79%) rename modules/light-clients/07-tendermint/{types => }/consensus_state.go (77%) rename modules/light-clients/07-tendermint/{types => }/consensus_state_test.go (77%) create mode 100644 modules/light-clients/07-tendermint/errors.go rename modules/light-clients/07-tendermint/{types => }/fraction.go (97%) rename modules/light-clients/07-tendermint/{types => }/genesis.go (79%) rename modules/light-clients/07-tendermint/{types => }/genesis_test.go (74%) rename modules/light-clients/07-tendermint/{types => }/header.go (72%) rename modules/light-clients/07-tendermint/{types => }/header_test.go (88%) create mode 100644 modules/light-clients/07-tendermint/keys.go create mode 100644 modules/light-clients/07-tendermint/migrations/expected_keepers.go create mode 100644 modules/light-clients/07-tendermint/migrations/migrations.go create mode 100644 modules/light-clients/07-tendermint/migrations/migrations_test.go rename modules/light-clients/07-tendermint/{types => }/misbehaviour.go (68%) create mode 100644 modules/light-clients/07-tendermint/misbehaviour_handle.go create mode 100644 modules/light-clients/07-tendermint/misbehaviour_handle_test.go rename modules/light-clients/07-tendermint/{types => }/misbehaviour_test.go (79%) rename modules/light-clients/07-tendermint/{types => }/proposal_handle.go (69%) rename modules/light-clients/07-tendermint/{types => }/proposal_handle_test.go (65%) rename modules/light-clients/07-tendermint/{types => }/store.go (89%) rename modules/light-clients/07-tendermint/{types => }/store_test.go (58%) rename modules/light-clients/07-tendermint/{types => }/tendermint.pb.go (84%) rename modules/light-clients/07-tendermint/{types => }/tendermint_test.go (91%) delete mode 100644 modules/light-clients/07-tendermint/types/client_state.go delete mode 100644 modules/light-clients/07-tendermint/types/client_state_test.go delete mode 100644 modules/light-clients/07-tendermint/types/errors.go delete mode 100644 modules/light-clients/07-tendermint/types/misbehaviour_handle.go delete mode 100644 modules/light-clients/07-tendermint/types/misbehaviour_handle_test.go delete mode 100644 modules/light-clients/07-tendermint/types/update.go delete mode 100644 modules/light-clients/07-tendermint/types/update_test.go create mode 100644 modules/light-clients/07-tendermint/update.go create mode 100644 modules/light-clients/07-tendermint/update_test.go rename modules/light-clients/07-tendermint/{types => }/upgrade.go (70%) rename modules/light-clients/07-tendermint/{types => }/upgrade_test.go (85%) delete mode 100644 modules/light-clients/09-localhost/doc.go delete mode 100644 modules/light-clients/09-localhost/module.go delete mode 100644 modules/light-clients/09-localhost/types/client_state.go delete mode 100644 modules/light-clients/09-localhost/types/client_state_test.go delete mode 100644 modules/light-clients/09-localhost/types/codec.go delete mode 100644 modules/light-clients/09-localhost/types/errors.go delete mode 100644 modules/light-clients/09-localhost/types/keys.go delete mode 100644 modules/light-clients/09-localhost/types/localhost.pb.go delete mode 100644 modules/light-clients/09-localhost/types/localhost_test.go create mode 100644 proto/buf.gen.gogo.yaml create mode 100644 proto/buf.gen.swagger.yaml create mode 100644 proto/buf.lock create mode 100644 proto/ibc/applications/interchain_accounts/controller/v1/tx.proto rename proto/ibc/applications/interchain_accounts/{ => genesis}/v1/genesis.proto (78%) create mode 100644 proto/ibc/applications/transfer/v1/authz.proto delete mode 100644 proto/ibc/lightclients/localhost/v1/localhost.proto delete mode 100644 proto/ibc/lightclients/solomachine/v1/solomachine.proto create mode 100644 proto/ibc/lightclients/solomachine/v3/solomachine.proto create mode 100755 scripts/linting/lint-changed-go-files.sh create mode 100755 scripts/linting/lint-changed-md-files.sh delete mode 100644 testing/sdk_test.go delete mode 100644 testing/simapp/helpers/test_helpers.go delete mode 100644 testing/simapp/simd/cmd/testnet.go create mode 100644 testing/simapp/upgrades/upgrades.go create mode 100644 testing/simapp/upgrades/v6/upgrades.go create mode 100644 testing/simapp/upgrades/v7/upgrades.go create mode 100644 testing/types/expected_keepers.go delete mode 100644 third_party/proto/buf.yaml delete mode 100644 third_party/proto/cosmos/auth/v1beta1/auth.proto delete mode 100644 third_party/proto/cosmos/auth/v1beta1/genesis.proto delete mode 100644 third_party/proto/cosmos/auth/v1beta1/query.proto delete mode 100644 third_party/proto/cosmos/base/query/v1beta1/pagination.proto delete mode 100644 third_party/proto/cosmos/base/v1beta1/coin.proto delete mode 100644 third_party/proto/cosmos/upgrade/v1beta1/upgrade.proto delete mode 100644 third_party/proto/cosmos_proto/cosmos.proto delete mode 100644 third_party/proto/gogoproto/gogo.proto delete mode 100644 third_party/proto/google/api/annotations.proto delete mode 100644 third_party/proto/google/api/http.proto delete mode 100644 third_party/proto/google/protobuf/any.proto delete mode 100644 third_party/proto/proofs.proto delete mode 100644 third_party/proto/tendermint/crypto/keys.proto delete mode 100644 third_party/proto/tendermint/crypto/proof.proto delete mode 100644 third_party/proto/tendermint/libs/bits/types.proto delete mode 100644 third_party/proto/tendermint/types/types.proto delete mode 100644 third_party/proto/tendermint/types/validator.proto delete mode 100644 third_party/proto/tendermint/version/types.proto diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 13e9c5c254c..f53e08bdaa8 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,6 +1,8 @@ # CODEOWNERS: https://help.github.com/articles/about-codeowners/ -* @colin-axner @AdityaSripal @crodriguezvega @seantking @charleenfei @damiannolan @chatton +* @colin-axner @AdityaSripal @crodriguezvega @charleenfei @damiannolan @chatton + +# Our appreciation and gratitude to past code owners: @fedekunze @seantking # Order is important; the last matching pattern takes the most # precedence. When someone opens a pull request that only @@ -9,34 +11,32 @@ # CODEOWNERS for the CODEOWNER file -/.github/CODEOWNERS @colin-axner @AdityaSripal @crodriguezvega +/.github/CODEOWNERS @colin-axner @AdityaSripal @crodriguezvega @damiannolan # CODEOWNERS for the core IBC module -/modules/core/ @colin-axner @AdityaSripal -/proto/ibc/core/ @colin-axner @AdityaSripal - -## CODEOWNERS for core/02-client - -/modules/core/02-client @colin-axner @AdityaSripal @seantking @damiannolan -/proto/ibc/core/client @colin-axner @AdityaSripal @seantking @damiannolan +/modules/core/ @colin-axner @AdityaSripal @damiannolan +/proto/ibc/core/ @colin-axner @AdityaSripal @damiannolan # CODEOWNERS for the light-clients -/modules/light-clients/ @colin-axner @AdityaSripal @seantking @damiannolan -/proto/ibc/lightclients/ @colin-axner @AdityaSripal @seantking @damiannolan +/modules/light-clients/ @colin-axner @AdityaSripal @damiannolan +/proto/ibc/lightclients/ @colin-axner @AdityaSripal @damiannolan # CODEOWNERS for ICS 20 -/modules/apps/transfer/ @colin-axner @AdityaSripal -/proto/ibc/applications/transfer/ @colin-axner @AdityaSripal +/modules/apps/transfer/ @colin-axner @AdityaSripal @damiannolan +/proto/ibc/applications/transfer/ @colin-axner @AdityaSripal @damiannolan # CODEOWNERS for interchain-accounts module -/modules/apps/27-interchain-accounts/ @seantking @colin-axner @AdityaSripal @damiannolan -/proto/ibc/applications/interchain_accounts/ @seantking @colin-axner @AdityaSripal @damiannolan +/modules/apps/27-interchain-accounts/ @colin-axner @AdityaSripal @damiannolan +/proto/ibc/applications/interchain_accounts/ @colin-axner @AdityaSripal @damiannolan # CODEOWNERS for fee module -/modules/apps/29-fee/ @AdityaSripal @seantking @charleenfei @colin-axner @damiannolan -/proto/ibc/applications/fee/ @AdityaSripal @seantking @charleenfei @colin-axner @damiannolan +/modules/apps/29-fee/ @AdityaSripal @charleenfei @colin-axner @damiannolan +/proto/ibc/applications/fee/ @AdityaSripal @charleenfei @colin-axner @damiannolan + +# CODEOWNERS for docs +/docs/ @colin-axner @AdityaSripal @crodriguezvega @charleenfei @damiannolan @chatton @tmsdkeys diff --git a/.github/ISSUE_TEMPLATE/release-tracker.md b/.github/ISSUE_TEMPLATE/release-tracker.md index 873f4e487ab..204c258609e 100644 --- a/.github/ISSUE_TEMPLATE/release-tracker.md +++ b/.github/ISSUE_TEMPLATE/release-tracker.md @@ -15,23 +15,24 @@ v without deliberation -### IBC spec compatibility +## IBC spec compatibility -### QA +## QA -#### Backwards compatibility +### Backwards compatibility -- [ ] Fungible token transfers over a non-incentivised channel works with a counterparty chain running each previous major version. -- [ ] Fungible token transfers over an incentivised channel works with a counterparty chain running each previous major version that supports ICS-29 Fee Middleware. -- [ ] Interchain Accounts over a non-incentivised channel works with a counterparty chain running each previous major version that supports ICS-27 Interchain Accounts over non-incentivised channels. -- [ ] Interchain Accounts over an incentivised channel works with a counterparty chain running each previous major version that supports ICS-27 Interchain Accounts over incentivised channels. +- [ ] [Compatibility tests](https://github.com/cosmos/ibc-go/actions/workflows/e2e-compatibility.yaml) pass for the release branch. +- [ ] [Upgrade tests](https://github.com/cosmos/ibc-go/actions/workflows/e2e-upgrade.yaml) pass. +- [ ] [Interchain Accounts inter-tx tests](https://github.com/cosmos/interchain-accounts-demo/actions/workflows/e2e-compatibility.yaml) pass. -### Migration +### Other testing + +## Migration @@ -41,7 +42,7 @@ versions of ibc-go to guarantee that no regression is introduced --> - [ ] Bump [go package version](https://github.com/cosmos/ibc-go/blob/main/go.mod#L3). - [ ] Change all imports starting with `github.com/cosmos/ibc-go/v{x}` to `github.com/cosmos/ibc-go/v{x+1}`. -- [ ] Branch off main to create release branch in the form of `release/vx.y.z`. +- [ ] Branch off main to create release branch in the form of `release/vx.y.z` and add branch protection rules. - [ ] Add branch protection rules to new release branch. - [ ] Add backport task to [`mergify.yml`](https://github.com/cosmos/ibc-go/blob/main/.github/mergify.yml) - [ ] Upgrade ibc-go version in [ibctest](https://github.com/strangelove-ventures/ibctest). @@ -50,12 +51,25 @@ versions of ibc-go to guarantee that no regression is introduced --> ## Post-release checklist - [ ] Update [`CHANGELOG.md`](https://github.com/cosmos/ibc-go/blob/main/CHANGELOG.md) -- [ ] Update the table of supported release lines (and End of Life dates) in [`RELEASES.md`](https://github.com/cosmos/ibc-go/blob/main/RELEASES.md). -- [ ] Update [version matrix](https://github.com/cosmos/ibc-go/blob/main/RELEASES.md#version-matrix) in `RELEASES.md`. +- [ ] Update the table of supported release lines (and End of Life dates) in [`RELEASES.md`](https://github.com/cosmos/ibc-go/blob/main/RELEASES.md): + - Add the new release line. + - Remove any release lines that might have become discontinued. +- [ ] Update [version matrix](https://github.com/cosmos/ibc-go/blob/main/RELEASES.md#version-matrix) in `RELEASES.md`: + - Add the new release. + - Remove any tags that might not be recommended anymore. +- [ ] Update the list of [supported release lines in README.md](https://github.com/cosmos/ibc-go#releases), if necessary. - [ ] Update docs site: - [ ] Add new release branch to [`docs/versions`](https://github.com/cosmos/ibc-go/blob/main/docs/versions) file. - - [ ] Add `label` and `key` to `versions` array in [`config.js`](https://github.com/cosmos/ibc-go/blob/main/docs/.vuepress/config.js#L33). + - [ ] Add `label` and `key` to `versions` array in [`config.js`](https://github.com/cosmos/ibc-go/blob/main/docs/.vuepress/config.js#L62). +- [ ] Bump ibc-go version in [cosmos/interchain-accounts-demo repository](https://github.com/cosmos/interchain-accounts-demo) and create a tag. +- [ ] Update the [compatibility test matrices](https://github.com/cosmos/ibc-go/tree/main/.github/compatibility-test-matrices): + - Add the new release. + - Remove any tags that might not be recommended anymore. +- [ ] Update the manual [e2e `simd`](https://github.com/cosmos/ibc-go/blob/main/.github/workflows/e2e-manual-simd.yaml) and [e2e `icad`](https://github.com/cosmos/ibc-go/blob/main/.github/workflows/e2e-manual-icad.yaml) test workflows: + - Add the new release and the new `icad` tag. + - Remove any tags that might not be recommended anymore. - [ ] After changes to docs site are deployed, check [ibc.cosmos.network](https://ibc.cosmos.network) is updated. +- [ ] Open issue in [SDK tutorials repo](https://github.com/cosmos/sdk-tutorials) to update tutorials to the released version of ibc-go. ____ diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 435e6a89ca8..bfbcfb6335f 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](https://github.com/cosmos/ibc-go/blob/main/docs/dev/pull-requests.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/main/docs/dev/pull-requests.md#pull-request-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/10-structure.md) and [Go style guide](../docs/dev/go-style-guide.md). +- [ ] Wrote unit and integration [tests](https://github.com/cosmos/ibc-go/blob/main/testing/README.md#ibc-testing-package). +- [ ] 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](https://github.com/cosmos/ibc-go/blob/main/docs/dev/pull-requests.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/compatibility-test-matrices/main/client-chain-a.json b/.github/compatibility-test-matrices/main/client-chain-a.json new file mode 100644 index 00000000000..23b1b5325d5 --- /dev/null +++ b/.github/compatibility-test-matrices/main/client-chain-a.json @@ -0,0 +1,11 @@ +{ + "chain-a": ["main"], + "chain-b": ["main", "v5.1.0", "v5.0.1", "v4.2.0", "v4.1.1", "v3.4.0", "v3.3.1"], + "entrypoint": ["TestClientTestSuite"], + "test": [ + "TestClientUpdateProposal_Succeeds" + ], + "relayer-type": ["rly"], + "chain-binary": ["simd"], + "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] +} diff --git a/.github/compatibility-test-matrices/main/client-chain-b.json b/.github/compatibility-test-matrices/main/client-chain-b.json new file mode 100644 index 00000000000..f97107801af --- /dev/null +++ b/.github/compatibility-test-matrices/main/client-chain-b.json @@ -0,0 +1,11 @@ +{ + "chain-a": ["main", "v5.1.0", "v5.0.1", "v4.2.0", "v4.1.1", "v3.4.0", "v3.3.1"], + "chain-b": ["main"], + "entrypoint": ["TestClientTestSuite"], + "test": [ + "TestClientUpdateProposal_Succeeds" + ], + "relayer-type": ["rly"], + "chain-binary": ["simd"], + "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] +} diff --git a/.github/compatibility-test-matrices/main/connection-chain-a.json b/.github/compatibility-test-matrices/main/connection-chain-a.json new file mode 100644 index 00000000000..c41bca7f985 --- /dev/null +++ b/.github/compatibility-test-matrices/main/connection-chain-a.json @@ -0,0 +1,11 @@ +{ + "chain-a": ["main"], + "chain-b": ["main", "v5.1.0", "v5.0.1", "v4.2.0", "v4.1.1", "v3.4.0", "v3.3.1"], + "entrypoint": ["TestConnectionTestSuite"], + "test": [ + "TestMaxExpectedTimePerBlockParam" + ], + "relayer-type": ["rly"], + "chain-binary": ["simd"], + "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] +} diff --git a/.github/compatibility-test-matrices/main/connection-chain-b.json b/.github/compatibility-test-matrices/main/connection-chain-b.json new file mode 100644 index 00000000000..030e84d84fe --- /dev/null +++ b/.github/compatibility-test-matrices/main/connection-chain-b.json @@ -0,0 +1,11 @@ +{ + "chain-a": ["main", "v5.1.0", "v5.0.1", "v4.2.0", "v4.1.1", "v3.4.0", "v3.3.1"], + "chain-b": ["main"], + "entrypoint": ["TestConnectionTestSuite"], + "test": [ + "TestMaxExpectedTimePerBlockParam" + ], + "relayer-type": ["rly"], + "chain-binary": ["simd"], + "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] +} diff --git a/.github/compatibility-test-matrices/main/ica-chain-a.json b/.github/compatibility-test-matrices/main/ica-chain-a.json new file mode 100644 index 00000000000..2b80c602c5a --- /dev/null +++ b/.github/compatibility-test-matrices/main/ica-chain-a.json @@ -0,0 +1,13 @@ +{ + "chain-a": ["main"], + "chain-b": ["main"], + "entrypoint": ["TestInterchainAccountsTestSuite"], + "test": [ + "TestMsgSendTx_SuccessfulTransfer", + "TestMsgSendTx_FailedTransfer_InsufficientFunds", + "TestMsgSubmitTx_SuccessfulTransfer_AfterReopeningICA" + ], + "relayer-type": ["rly"], + "chain-binary": ["simd"], + "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] +} diff --git a/.github/compatibility-test-matrices/main/ica-gov-chain-a.json b/.github/compatibility-test-matrices/main/ica-gov-chain-a.json new file mode 100644 index 00000000000..7fa0e161267 --- /dev/null +++ b/.github/compatibility-test-matrices/main/ica-gov-chain-a.json @@ -0,0 +1,11 @@ +{ + "chain-a": ["main"], + "chain-b": ["main"], + "entrypoint": ["TestInterchainAccountsGovTestSuite"], + "test": [ + "TestInterchainAccountsGovIntegration" + ], + "relayer-type": ["rly"], + "chain-binary": ["simd"], + "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] +} diff --git a/.github/compatibility-test-matrices/main/ica-groups-chain-a.json b/.github/compatibility-test-matrices/main/ica-groups-chain-a.json new file mode 100644 index 00000000000..1e8fff430b3 --- /dev/null +++ b/.github/compatibility-test-matrices/main/ica-groups-chain-a.json @@ -0,0 +1,11 @@ +{ + "chain-a": ["main"], + "chain-b": ["main"], + "entrypoint": ["TestInterchainAccountsGroupsTestSuite"], + "test": [ + "TestInterchainAccountsGroupsIntegration" + ], + "relayer-type": ["rly"], + "chain-binary": ["simd"], + "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] +} diff --git a/.github/compatibility-test-matrices/main/incentivized-ica-chain-a.json b/.github/compatibility-test-matrices/main/incentivized-ica-chain-a.json new file mode 100644 index 00000000000..9330a274711 --- /dev/null +++ b/.github/compatibility-test-matrices/main/incentivized-ica-chain-a.json @@ -0,0 +1,12 @@ +{ + "chain-a": ["main"], + "chain-b": ["main"], + "entrypoint": ["TestIncentivizedInterchainAccountsTestSuite"], + "test": [ + "TestMsgSendTx_SuccessfulBankSend_Incentivized", + "TestMsgSendTx_FailedBankSend_Incentivized" + ], + "relayer-type": ["rly"], + "chain-binary": ["simd"], + "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] +} diff --git a/.github/compatibility-test-matrices/main/incentivized-transfer-chain-a.json b/.github/compatibility-test-matrices/main/incentivized-transfer-chain-a.json new file mode 100644 index 00000000000..235b4558a5d --- /dev/null +++ b/.github/compatibility-test-matrices/main/incentivized-transfer-chain-a.json @@ -0,0 +1,16 @@ +{ + "chain-a": ["main"], + "chain-b": ["main", "v5.1.0", "v5.0.1", "v4.2.0", "v4.1.1"], + "entrypoint": ["TestIncentivizedTransferTestSuite"], + "test": [ + "TestMsgPayPacketFee_AsyncSingleSender_Succeeds", + "TestMsgPayPacketFee_InvalidReceiverAccount", + "TestMultiMsg_MsgPayPacketFeeSingleSender", + "TestMsgPayPacketFee_SingleSender_TimesOut", + "TestPayPacketFeeAsync_SingleSender_NoCounterPartyAddress", + "TestMsgPayPacketFee_AsyncMultipleSenders_Succeeds" + ], + "relayer-type": ["rly"], + "chain-binary": ["simd"], + "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] +} diff --git a/.github/compatibility-test-matrices/main/incentivized-transfer-chain-b.json b/.github/compatibility-test-matrices/main/incentivized-transfer-chain-b.json new file mode 100644 index 00000000000..ad00897c137 --- /dev/null +++ b/.github/compatibility-test-matrices/main/incentivized-transfer-chain-b.json @@ -0,0 +1,16 @@ +{ + "chain-a": ["main", "v5.1.0", "v5.0.1", "v4.2.0", "v4.1.1"], + "chain-b": ["main"], + "entrypoint": ["TestIncentivizedTransferTestSuite"], + "test": [ + "TestMsgPayPacketFee_AsyncSingleSender_Succeeds", + "TestMsgPayPacketFee_InvalidReceiverAccount", + "TestMultiMsg_MsgPayPacketFeeSingleSender", + "TestMsgPayPacketFee_SingleSender_TimesOut", + "TestPayPacketFeeAsync_SingleSender_NoCounterPartyAddress", + "TestMsgPayPacketFee_AsyncMultipleSenders_Succeeds" + ], + "relayer-type": ["rly"], + "chain-binary": ["simd"], + "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] +} diff --git a/.github/compatibility-test-matrices/main/transfer-chain-a.json b/.github/compatibility-test-matrices/main/transfer-chain-a.json new file mode 100644 index 00000000000..425b14fdfc1 --- /dev/null +++ b/.github/compatibility-test-matrices/main/transfer-chain-a.json @@ -0,0 +1,16 @@ +{ + "chain-a": ["main"], + "chain-b": ["main", "v5.1.0", "v5.0.1", "v4.2.0", "v4.1.1", "v3.4.0", "v3.3.1"], + "entrypoint": ["TestTransferTestSuite"], + "test": [ + "TestMsgTransfer_Succeeds_Nonincentivized", + "TestMsgTransfer_Fails_InvalidAddress", + "TestMsgTransfer_Timeout_Nonincentivized", + "TestMsgTransfer_WithMemo", + "TestSendEnabledParam", + "TestReceiveEnabledParam" + ], + "relayer-type": ["rly"], + "chain-binary": ["simd"], + "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] +} diff --git a/.github/compatibility-test-matrices/main/transfer-chain-b.json b/.github/compatibility-test-matrices/main/transfer-chain-b.json new file mode 100644 index 00000000000..916d1e58744 --- /dev/null +++ b/.github/compatibility-test-matrices/main/transfer-chain-b.json @@ -0,0 +1,16 @@ +{ + "chain-a": ["main", "v5.1.0", "v5.0.1", "v4.2.0", "v4.1.1", "v3.4.0", "v3.3.1"], + "chain-b": ["main"], + "entrypoint": ["TestTransferTestSuite"], + "test": [ + "TestMsgTransfer_Succeeds_Nonincentivized", + "TestMsgTransfer_Fails_InvalidAddress", + "TestMsgTransfer_Timeout_Nonincentivized", + "TestMsgTransfer_WithMemo", + "TestSendEnabledParam", + "TestReceiveEnabledParam" + ], + "relayer-type": ["rly"], + "chain-binary": ["simd"], + "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] +} diff --git a/.github/compatibility-test-matrices/release-v3.4.x/transfer-chain-a.json b/.github/compatibility-test-matrices/release-v3.4.x/transfer-chain-a.json new file mode 100644 index 00000000000..407e9ebd616 --- /dev/null +++ b/.github/compatibility-test-matrices/release-v3.4.x/transfer-chain-a.json @@ -0,0 +1,16 @@ +{ + "chain-a": ["release-v3.4.x"], + "chain-b": ["release-v3.4.x", "v6.1.0", "v5.2.0", "v4.2.0", "v4.1.1", "v3.4.0", "v3.3.1"], + "entrypoint": ["TestTransferTestSuite"], + "test": [ + "TestMsgTransfer_Succeeds_Nonincentivized", + "TestMsgTransfer_Fails_InvalidAddress", + "TestMsgTransfer_Timeout_Nonincentivized", + "TestMsgTransfer_WithMemo", + "TestSendEnabledParam", + "TestReceiveEnabledParam" + ], + "relayer-type": ["rly"], + "chain-binary": ["simd"], + "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] +} diff --git a/.github/compatibility-test-matrices/release-v3.4.x/transfer-chain-b.json b/.github/compatibility-test-matrices/release-v3.4.x/transfer-chain-b.json new file mode 100644 index 00000000000..ae95e67834a --- /dev/null +++ b/.github/compatibility-test-matrices/release-v3.4.x/transfer-chain-b.json @@ -0,0 +1,16 @@ +{ + "chain-a": ["release-v3.4.x", "v6.1.0", "v5.2.0", "v4.2.0", "v4.1.1", "v3.4.0", "v3.3.1"], + "chain-b": ["release-v3.4.x"], + "entrypoint": ["TestTransferTestSuite"], + "test": [ + "TestMsgTransfer_Succeeds_Nonincentivized", + "TestMsgTransfer_Fails_InvalidAddress", + "TestMsgTransfer_Timeout_Nonincentivized", + "TestMsgTransfer_WithMemo", + "TestSendEnabledParam", + "TestReceiveEnabledParam" + ], + "relayer-type": ["rly"], + "chain-binary": ["simd"], + "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] +} diff --git a/.github/compatibility-test-matrices/release-v4.2.x/incentivized-transfer-chain-a.json b/.github/compatibility-test-matrices/release-v4.2.x/incentivized-transfer-chain-a.json new file mode 100644 index 00000000000..c2b4e20e0f9 --- /dev/null +++ b/.github/compatibility-test-matrices/release-v4.2.x/incentivized-transfer-chain-a.json @@ -0,0 +1,16 @@ +{ + "chain-a": ["release-v4.2.x"], + "chain-b": ["release-v4.2.x", "v6.1.0", "v5.2.0", "v4.1.1"], + "entrypoint": ["TestIncentivizedTransferTestSuite"], + "test": [ + "TestMsgPayPacketFee_AsyncSingleSender_Succeeds", + "TestMsgPayPacketFee_InvalidReceiverAccount", + "TestMultiMsg_MsgPayPacketFeeSingleSender", + "TestMsgPayPacketFee_SingleSender_TimesOut", + "TestPayPacketFeeAsync_SingleSender_NoCounterPartyAddress", + "TestMsgPayPacketFee_AsyncMultipleSenders_Succeeds" + ], + "relayer-type": ["rly"], + "chain-binary": ["simd"], + "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] +} diff --git a/.github/compatibility-test-matrices/release-v4.2.x/incentivized-transfer-chain-b.json b/.github/compatibility-test-matrices/release-v4.2.x/incentivized-transfer-chain-b.json new file mode 100644 index 00000000000..3acc5bf05f9 --- /dev/null +++ b/.github/compatibility-test-matrices/release-v4.2.x/incentivized-transfer-chain-b.json @@ -0,0 +1,16 @@ +{ + "chain-a": ["release-v4.2.x", "v6.1.0", "v5.2.0", "v4.1.1"], + "chain-b": ["release-v4.2.x"], + "entrypoint": ["TestIncentivizedTransferTestSuite"], + "test": [ + "TestMsgPayPacketFee_AsyncSingleSender_Succeeds", + "TestMsgPayPacketFee_InvalidReceiverAccount", + "TestMultiMsg_MsgPayPacketFeeSingleSender", + "TestMsgPayPacketFee_SingleSender_TimesOut", + "TestPayPacketFeeAsync_SingleSender_NoCounterPartyAddress", + "TestMsgPayPacketFee_AsyncMultipleSenders_Succeeds" + ], + "relayer-type": ["rly"], + "chain-binary": ["simd"], + "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] +} diff --git a/.github/compatibility-test-matrices/release-v4.2.x/transfer-chain-a.json b/.github/compatibility-test-matrices/release-v4.2.x/transfer-chain-a.json new file mode 100644 index 00000000000..191ca1e3d01 --- /dev/null +++ b/.github/compatibility-test-matrices/release-v4.2.x/transfer-chain-a.json @@ -0,0 +1,16 @@ +{ + "chain-a": ["release-v4.2.x"], + "chain-b": ["release-v4.2.x", "v6.1.0", "v5.2.0", "v4.1.1", "v3.4.0", "v3.3.1"], + "entrypoint": ["TestTransferTestSuite"], + "test": [ + "TestMsgTransfer_Succeeds_Nonincentivized", + "TestMsgTransfer_Fails_InvalidAddress", + "TestMsgTransfer_Timeout_Nonincentivized", + "TestMsgTransfer_WithMemo", + "TestSendEnabledParam", + "TestReceiveEnabledParam" + ], + "relayer-type": ["rly"], + "chain-binary": ["simd"], + "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] +} diff --git a/.github/compatibility-test-matrices/release-v4.2.x/transfer-chain-b.json b/.github/compatibility-test-matrices/release-v4.2.x/transfer-chain-b.json new file mode 100644 index 00000000000..b7024615f69 --- /dev/null +++ b/.github/compatibility-test-matrices/release-v4.2.x/transfer-chain-b.json @@ -0,0 +1,16 @@ +{ + "chain-a": ["release-v4.2.x", "v6.1.0", "v5.2.0", "v4.1.1", "v3.4.0", "v3.3.1"], + "chain-b": ["release-v4.2.x"], + "entrypoint": ["TestTransferTestSuite"], + "test": [ + "TestMsgTransfer_Succeeds_Nonincentivized", + "TestMsgTransfer_Fails_InvalidAddress", + "TestMsgTransfer_Timeout_Nonincentivized", + "TestMsgTransfer_WithMemo", + "TestSendEnabledParam", + "TestReceiveEnabledParam" + ], + "relayer-type": ["rly"], + "chain-binary": ["simd"], + "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] +} diff --git a/.github/compatibility-test-matrices/release-v4.3.x/incentivized-transfer-chain-a.json b/.github/compatibility-test-matrices/release-v4.3.x/incentivized-transfer-chain-a.json new file mode 100644 index 00000000000..884575a8648 --- /dev/null +++ b/.github/compatibility-test-matrices/release-v4.3.x/incentivized-transfer-chain-a.json @@ -0,0 +1,16 @@ +{ + "chain-a": ["release-v4.3.x"], + "chain-b": ["release-v4.3.x", "v6.1.0", "v5.2.0", "v4.3.0", "4.2.0", "v4.1.1"], + "entrypoint": ["TestIncentivizedTransferTestSuite"], + "test": [ + "TestMsgPayPacketFee_AsyncSingleSender_Succeeds", + "TestMsgPayPacketFee_InvalidReceiverAccount", + "TestMultiMsg_MsgPayPacketFeeSingleSender", + "TestMsgPayPacketFee_SingleSender_TimesOut", + "TestPayPacketFeeAsync_SingleSender_NoCounterPartyAddress", + "TestMsgPayPacketFee_AsyncMultipleSenders_Succeeds" + ], + "relayer-type": ["rly"], + "chain-binary": ["simd"], + "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] +} diff --git a/.github/compatibility-test-matrices/release-v4.3.x/incentivized-transfer-chain-b.json b/.github/compatibility-test-matrices/release-v4.3.x/incentivized-transfer-chain-b.json new file mode 100644 index 00000000000..a3190d1da42 --- /dev/null +++ b/.github/compatibility-test-matrices/release-v4.3.x/incentivized-transfer-chain-b.json @@ -0,0 +1,16 @@ +{ + "chain-a": ["release-v4.3.x", "v6.1.0", "v5.2.0", "v4.3.0", "4.2.0", "v4.1.1"], + "chain-b": ["release-v4.3.x"], + "entrypoint": ["TestIncentivizedTransferTestSuite"], + "test": [ + "TestMsgPayPacketFee_AsyncSingleSender_Succeeds", + "TestMsgPayPacketFee_InvalidReceiverAccount", + "TestMultiMsg_MsgPayPacketFeeSingleSender", + "TestMsgPayPacketFee_SingleSender_TimesOut", + "TestPayPacketFeeAsync_SingleSender_NoCounterPartyAddress", + "TestMsgPayPacketFee_AsyncMultipleSenders_Succeeds" + ], + "relayer-type": ["rly"], + "chain-binary": ["simd"], + "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] +} diff --git a/.github/compatibility-test-matrices/release-v4.3.x/transfer-chain-a.json b/.github/compatibility-test-matrices/release-v4.3.x/transfer-chain-a.json new file mode 100644 index 00000000000..d4711252f4e --- /dev/null +++ b/.github/compatibility-test-matrices/release-v4.3.x/transfer-chain-a.json @@ -0,0 +1,16 @@ +{ + "chain-a": ["release-v4.3.x"], + "chain-b": ["release-v4.3.x", "v6.1.0", "v5.2.0", "v4.3.0", "v4.2.0", "v4.1.1", "v3.4.0", "v3.3.1"], + "entrypoint": ["TestTransferTestSuite"], + "test": [ + "TestMsgTransfer_Succeeds_Nonincentivized", + "TestMsgTransfer_Fails_InvalidAddress", + "TestMsgTransfer_Timeout_Nonincentivized", + "TestMsgTransfer_WithMemo", + "TestSendEnabledParam", + "TestReceiveEnabledParam" + ], + "relayer-type": ["rly"], + "chain-binary": ["simd"], + "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] +} diff --git a/.github/compatibility-test-matrices/release-v4.3.x/transfer-chain-b.json b/.github/compatibility-test-matrices/release-v4.3.x/transfer-chain-b.json new file mode 100644 index 00000000000..ad11eec6e39 --- /dev/null +++ b/.github/compatibility-test-matrices/release-v4.3.x/transfer-chain-b.json @@ -0,0 +1,16 @@ +{ + "chain-a": ["release-v4.3.x", "v6.1.0", "v5.2.0", "v4.3.0", "v4.2.0", "v4.1.1", "v3.4.0", "v3.3.1"], + "chain-b": ["release-v4.3.x"], + "entrypoint": ["TestTransferTestSuite"], + "test": [ + "TestMsgTransfer_Succeeds_Nonincentivized", + "TestMsgTransfer_Fails_InvalidAddress", + "TestMsgTransfer_Timeout_Nonincentivized", + "TestMsgTransfer_WithMemo", + "TestSendEnabledParam", + "TestReceiveEnabledParam" + ], + "relayer-type": ["rly"], + "chain-binary": ["simd"], + "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] +} diff --git a/.github/compatibility-test-matrices/release-v5.2.x/incentivized-transfer-chain-a.json b/.github/compatibility-test-matrices/release-v5.2.x/incentivized-transfer-chain-a.json new file mode 100644 index 00000000000..6ff10fdc4bd --- /dev/null +++ b/.github/compatibility-test-matrices/release-v5.2.x/incentivized-transfer-chain-a.json @@ -0,0 +1,16 @@ +{ + "chain-a": ["release-v5.2.x"], + "chain-b": ["release-v5.2.x", "v6.1.0", "v5.2.0", "v4.3.0", "v4.2.0", "v4.1.1"], + "entrypoint": ["TestIncentivizedTransferTestSuite"], + "test": [ + "TestMsgPayPacketFee_AsyncSingleSender_Succeeds", + "TestMsgPayPacketFee_InvalidReceiverAccount", + "TestMultiMsg_MsgPayPacketFeeSingleSender", + "TestMsgPayPacketFee_SingleSender_TimesOut", + "TestPayPacketFeeAsync_SingleSender_NoCounterPartyAddress", + "TestMsgPayPacketFee_AsyncMultipleSenders_Succeeds" + ], + "relayer-type": ["rly"], + "chain-binary": ["simd"], + "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] +} diff --git a/.github/compatibility-test-matrices/release-v5.2.x/incentivized-transfer-chain-b.json b/.github/compatibility-test-matrices/release-v5.2.x/incentivized-transfer-chain-b.json new file mode 100644 index 00000000000..dba1d97b60f --- /dev/null +++ b/.github/compatibility-test-matrices/release-v5.2.x/incentivized-transfer-chain-b.json @@ -0,0 +1,16 @@ +{ + "chain-a": ["release-v5.2.x", "v6.1.0", "v5.2.0", "v4.3.0", "v4.2.0", "v4.1.1"], + "chain-b": ["release-v5.2.x"], + "entrypoint": ["TestIncentivizedTransferTestSuite"], + "test": [ + "TestMsgPayPacketFee_AsyncSingleSender_Succeeds", + "TestMsgPayPacketFee_InvalidReceiverAccount", + "TestMultiMsg_MsgPayPacketFeeSingleSender", + "TestMsgPayPacketFee_SingleSender_TimesOut", + "TestPayPacketFeeAsync_SingleSender_NoCounterPartyAddress", + "TestMsgPayPacketFee_AsyncMultipleSenders_Succeeds" + ], + "relayer-type": ["rly"], + "chain-binary": ["simd"], + "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] +} diff --git a/.github/compatibility-test-matrices/release-v5.2.x/transfer-chain-a.json b/.github/compatibility-test-matrices/release-v5.2.x/transfer-chain-a.json new file mode 100644 index 00000000000..7f18e5f202d --- /dev/null +++ b/.github/compatibility-test-matrices/release-v5.2.x/transfer-chain-a.json @@ -0,0 +1,16 @@ +{ + "chain-a": ["release-v5.2.x"], + "chain-b": ["release-v5.2.x", "v6.1.0", "v5.2.0", "v4.3.0", "v4.2.0", "v4.1.1", "v3.4.0", "v3.3.1"], + "entrypoint": ["TestTransferTestSuite"], + "test": [ + "TestMsgTransfer_Succeeds_Nonincentivized", + "TestMsgTransfer_Fails_InvalidAddress", + "TestMsgTransfer_Timeout_Nonincentivized", + "TestMsgTransfer_WithMemo", + "TestSendEnabledParam", + "TestReceiveEnabledParam" + ], + "relayer-type": ["rly"], + "chain-binary": ["simd"], + "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] +} diff --git a/.github/compatibility-test-matrices/release-v5.2.x/transfer-chain-b.json b/.github/compatibility-test-matrices/release-v5.2.x/transfer-chain-b.json new file mode 100644 index 00000000000..b4f20372860 --- /dev/null +++ b/.github/compatibility-test-matrices/release-v5.2.x/transfer-chain-b.json @@ -0,0 +1,16 @@ +{ + "chain-a": ["release-v5.2.x", "v6.1.0", "v5.2.0", "v4.3.0", "v4.2.0", "v4.1.1", "v3.4.0", "v3.3.1"], + "chain-b": ["release-v5.2.x"], + "entrypoint": ["TestTransferTestSuite"], + "test": [ + "TestMsgTransfer_Succeeds_Nonincentivized", + "TestMsgTransfer_Fails_InvalidAddress", + "TestMsgTransfer_Timeout_Nonincentivized", + "TestMsgTransfer_WithMemo", + "TestSendEnabledParam", + "TestReceiveEnabledParam" + ], + "relayer-type": ["rly"], + "chain-binary": ["simd"], + "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] +} diff --git a/.github/compatibility-test-matrices/release-v6.1.x/client-chain-a.json b/.github/compatibility-test-matrices/release-v6.1.x/client-chain-a.json new file mode 100644 index 00000000000..79bc3005ed1 --- /dev/null +++ b/.github/compatibility-test-matrices/release-v6.1.x/client-chain-a.json @@ -0,0 +1,11 @@ +{ + "chain-a": ["release-v6.1.x"], + "chain-b": ["release-v6.1.x", "v6.1.0", "v5.2.0", "v4.3.0", "v4.2.0", "v4.1.1", "v3.4.0", "v3.3.1"], + "entrypoint": ["TestClientTestSuite"], + "test": [ + "TestClientUpdateProposal_Succeeds" + ], + "relayer-type": ["rly"], + "chain-binary": ["simd"], + "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] +} diff --git a/.github/compatibility-test-matrices/release-v6.1.x/client-chain-b.json b/.github/compatibility-test-matrices/release-v6.1.x/client-chain-b.json new file mode 100644 index 00000000000..53f70071854 --- /dev/null +++ b/.github/compatibility-test-matrices/release-v6.1.x/client-chain-b.json @@ -0,0 +1,11 @@ +{ + "chain-a": ["release-v6.1.x", "v6.1.0", "v5.2.0", "v4.3.0", "v4.2.0", "v4.1.1", "v3.4.0", "v3.3.1"], + "chain-b": ["release-v6.1.x"], + "entrypoint": ["TestClientTestSuite"], + "test": [ + "TestClientUpdateProposal_Succeeds" + ], + "relayer-type": ["rly"], + "chain-binary": ["simd"], + "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] +} diff --git a/.github/compatibility-test-matrices/release-v6.1.x/connection-chain-a.json b/.github/compatibility-test-matrices/release-v6.1.x/connection-chain-a.json new file mode 100644 index 00000000000..a740b2701d0 --- /dev/null +++ b/.github/compatibility-test-matrices/release-v6.1.x/connection-chain-a.json @@ -0,0 +1,11 @@ +{ + "chain-a": ["release-v6.1.x"], + "chain-b": ["release-v6.1.x", "v6.1.0", "v5.2.0", "v4.3.0", "v4.2.0", "v4.1.1", "v3.4.0", "v3.3.1"], + "entrypoint": ["TestConnectionTestSuite"], + "test": [ + "TestMaxExpectedTimePerBlockParam" + ], + "relayer-type": ["rly"], + "chain-binary": ["simd"], + "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] +} diff --git a/.github/compatibility-test-matrices/release-v6.1.x/connection-chain-b.json b/.github/compatibility-test-matrices/release-v6.1.x/connection-chain-b.json new file mode 100644 index 00000000000..eae5513e627 --- /dev/null +++ b/.github/compatibility-test-matrices/release-v6.1.x/connection-chain-b.json @@ -0,0 +1,11 @@ +{ + "chain-a": ["release-v6.1.x", "v6.1.0", "v5.2.0", "v4.3.0", "v4.2.0", "v4.1.1", "v3.4.0", "v3.3.1"], + "chain-b": ["release-v6.1.x"], + "entrypoint": ["TestConnectionTestSuite"], + "test": [ + "TestMaxExpectedTimePerBlockParam" + ], + "relayer-type": ["rly"], + "chain-binary": ["simd"], + "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] +} diff --git a/.github/compatibility-test-matrices/release-v6.1.x/ica-chain-a.json b/.github/compatibility-test-matrices/release-v6.1.x/ica-chain-a.json new file mode 100644 index 00000000000..56bdb35c6f2 --- /dev/null +++ b/.github/compatibility-test-matrices/release-v6.1.x/ica-chain-a.json @@ -0,0 +1,13 @@ +{ + "chain-a": ["release-v6.1.x"], + "chain-b": ["release-v6.1.x", "v6.1.0"], + "entrypoint": ["TestInterchainAccountsTestSuite"], + "test": [ + "TestMsgSendTx_SuccessfulTransfer", + "TestMsgSendTx_FailedTransfer_InsufficientFunds", + "TestMsgSubmitTx_SuccessfulTransfer_AfterReopeningICA" + ], + "relayer-type": ["rly"], + "chain-binary": ["simd"], + "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] +} diff --git a/.github/compatibility-test-matrices/release-v6.1.x/ica-chain-b.json b/.github/compatibility-test-matrices/release-v6.1.x/ica-chain-b.json new file mode 100644 index 00000000000..0d6c894d461 --- /dev/null +++ b/.github/compatibility-test-matrices/release-v6.1.x/ica-chain-b.json @@ -0,0 +1,13 @@ +{ + "chain-a": ["release-v6.1.x", "v6.1.0"], + "chain-b": ["release-v6.1.x"], + "entrypoint": ["TestInterchainAccountsTestSuite"], + "test": [ + "TestMsgSendTx_SuccessfulTransfer", + "TestMsgSendTx_FailedTransfer_InsufficientFunds", + "TestMsgSubmitTx_SuccessfulTransfer_AfterReopeningICA" + ], + "relayer-type": ["rly"], + "chain-binary": ["simd"], + "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] +} diff --git a/.github/compatibility-test-matrices/release-v6.1.x/ica-gov-chain-a.json b/.github/compatibility-test-matrices/release-v6.1.x/ica-gov-chain-a.json new file mode 100644 index 00000000000..a5db177b6df --- /dev/null +++ b/.github/compatibility-test-matrices/release-v6.1.x/ica-gov-chain-a.json @@ -0,0 +1,11 @@ +{ + "chain-a": ["release-v6.1.x", "v6.1.0"], + "chain-b": ["release-v6.1.x"], + "entrypoint": ["TestInterchainAccountsGovTestSuite"], + "test": [ + "TestInterchainAccountsGovIntegration" + ], + "relayer-type": ["rly"], + "chain-binary": ["simd"], + "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] +} diff --git a/.github/compatibility-test-matrices/release-v6.1.x/ica-gov-chain-b.json b/.github/compatibility-test-matrices/release-v6.1.x/ica-gov-chain-b.json new file mode 100644 index 00000000000..210a45a9d9f --- /dev/null +++ b/.github/compatibility-test-matrices/release-v6.1.x/ica-gov-chain-b.json @@ -0,0 +1,11 @@ +{ + "chain-a": ["release-v6.1.x"], + "chain-b": ["release-v6.1.x", "v6.1.0"], + "entrypoint": ["TestInterchainAccountsGovTestSuite"], + "test": [ + "TestInterchainAccountsGovIntegration" + ], + "relayer-type": ["rly"], + "chain-binary": ["simd"], + "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] +} diff --git a/.github/compatibility-test-matrices/release-v6.1.x/ica-groups-chain-a.json b/.github/compatibility-test-matrices/release-v6.1.x/ica-groups-chain-a.json new file mode 100644 index 00000000000..bcf9da79a3f --- /dev/null +++ b/.github/compatibility-test-matrices/release-v6.1.x/ica-groups-chain-a.json @@ -0,0 +1,11 @@ +{ + "chain-a": ["release-v6.1.x"], + "chain-b": ["release-v6.1.x", "v6.1.0"], + "entrypoint": ["TestInterchainAccountsGroupsTestSuite"], + "test": [ + "TestInterchainAccountsGroupsIntegration" + ], + "relayer-type": ["rly"], + "chain-binary": ["simd"], + "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] +} diff --git a/.github/compatibility-test-matrices/release-v6.1.x/ica-groups-chain-b.json b/.github/compatibility-test-matrices/release-v6.1.x/ica-groups-chain-b.json new file mode 100644 index 00000000000..33705ad8c9b --- /dev/null +++ b/.github/compatibility-test-matrices/release-v6.1.x/ica-groups-chain-b.json @@ -0,0 +1,11 @@ +{ + "chain-a": ["release-v6.1.x", "v6.1.0"], + "chain-b": ["release-v6.1.x"], + "entrypoint": ["TestInterchainAccountsGroupsTestSuite"], + "test": [ + "TestInterchainAccountsGroupsIntegration" + ], + "relayer-type": ["rly"], + "chain-binary": ["simd"], + "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] +} diff --git a/.github/compatibility-test-matrices/release-v6.1.x/incentivized-ica-chain-a.json b/.github/compatibility-test-matrices/release-v6.1.x/incentivized-ica-chain-a.json new file mode 100644 index 00000000000..bcf9e42659d --- /dev/null +++ b/.github/compatibility-test-matrices/release-v6.1.x/incentivized-ica-chain-a.json @@ -0,0 +1,12 @@ +{ + "chain-a": ["release-v6.1.x"], + "chain-b": ["release-v6.1.x", "v6.1.0"], + "entrypoint": ["TestIncentivizedInterchainAccountsTestSuite"], + "test": [ + "TestMsgSendTx_SuccessfulBankSend_Incentivized", + "TestMsgSendTx_FailedBankSend_Incentivized" + ], + "relayer-type": ["rly"], + "chain-binary": ["simd"], + "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] +} diff --git a/.github/compatibility-test-matrices/release-v6.1.x/incentivized-ica-chain-b.json b/.github/compatibility-test-matrices/release-v6.1.x/incentivized-ica-chain-b.json new file mode 100644 index 00000000000..141eecac664 --- /dev/null +++ b/.github/compatibility-test-matrices/release-v6.1.x/incentivized-ica-chain-b.json @@ -0,0 +1,12 @@ +{ + "chain-a": ["release-v6.1.x", "v6.1.0"], + "chain-b": ["release-v6.1.x"], + "entrypoint": ["TestIncentivizedInterchainAccountsTestSuite"], + "test": [ + "TestMsgSendTx_SuccessfulBankSend_Incentivized", + "TestMsgSendTx_FailedBankSend_Incentivized" + ], + "relayer-type": ["rly"], + "chain-binary": ["simd"], + "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] +} diff --git a/.github/compatibility-test-matrices/release-v6.1.x/incentivized-transfer-chain-a.json b/.github/compatibility-test-matrices/release-v6.1.x/incentivized-transfer-chain-a.json new file mode 100644 index 00000000000..004418020be --- /dev/null +++ b/.github/compatibility-test-matrices/release-v6.1.x/incentivized-transfer-chain-a.json @@ -0,0 +1,16 @@ +{ + "chain-a": ["release-v6.1.x"], + "chain-b": ["release-v6.1.x", "v6.1.0", "v5.2.0", "v4.3.0", "v4.2.0", "v4.1.1"], + "entrypoint": ["TestIncentivizedTransferTestSuite"], + "test": [ + "TestMsgPayPacketFee_AsyncSingleSender_Succeeds", + "TestMsgPayPacketFee_InvalidReceiverAccount", + "TestMultiMsg_MsgPayPacketFeeSingleSender", + "TestMsgPayPacketFee_SingleSender_TimesOut", + "TestPayPacketFeeAsync_SingleSender_NoCounterPartyAddress", + "TestMsgPayPacketFee_AsyncMultipleSenders_Succeeds" + ], + "relayer-type": ["rly"], + "chain-binary": ["simd"], + "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] +} diff --git a/.github/compatibility-test-matrices/release-v6.1.x/incentivized-transfer-chain-b.json b/.github/compatibility-test-matrices/release-v6.1.x/incentivized-transfer-chain-b.json new file mode 100644 index 00000000000..4d0b6a7ee99 --- /dev/null +++ b/.github/compatibility-test-matrices/release-v6.1.x/incentivized-transfer-chain-b.json @@ -0,0 +1,16 @@ +{ + "chain-a": ["release-v6.1.x", "v6.1.0", "v5.2.0", "v4.3.0", "v4.2.0", "v4.1.1"], + "chain-b": ["release-v6.1.x"], + "entrypoint": ["TestIncentivizedTransferTestSuite"], + "test": [ + "TestMsgPayPacketFee_AsyncSingleSender_Succeeds", + "TestMsgPayPacketFee_InvalidReceiverAccount", + "TestMultiMsg_MsgPayPacketFeeSingleSender", + "TestMsgPayPacketFee_SingleSender_TimesOut", + "TestPayPacketFeeAsync_SingleSender_NoCounterPartyAddress", + "TestMsgPayPacketFee_AsyncMultipleSenders_Succeeds" + ], + "relayer-type": ["rly"], + "chain-binary": ["simd"], + "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] +} diff --git a/.github/compatibility-test-matrices/release-v6.1.x/transfer-chain-a.json b/.github/compatibility-test-matrices/release-v6.1.x/transfer-chain-a.json new file mode 100644 index 00000000000..14d9ee98579 --- /dev/null +++ b/.github/compatibility-test-matrices/release-v6.1.x/transfer-chain-a.json @@ -0,0 +1,16 @@ +{ + "chain-a": ["release-v6.1.x"], + "chain-b": ["release-v6.1.x", "v6.1.0", "v5.2.0", "v4.3.0", "v4.2.0", "v4.1.1", "v3.4.0", "v3.3.1"], + "entrypoint": ["TestTransferTestSuite"], + "test": [ + "TestMsgTransfer_Succeeds_Nonincentivized", + "TestMsgTransfer_Fails_InvalidAddress", + "TestMsgTransfer_Timeout_Nonincentivized", + "TestMsgTransfer_WithMemo", + "TestSendEnabledParam", + "TestReceiveEnabledParam" + ], + "relayer-type": ["rly"], + "chain-binary": ["simd"], + "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] +} diff --git a/.github/compatibility-test-matrices/release-v6.1.x/transfer-chain-b.json b/.github/compatibility-test-matrices/release-v6.1.x/transfer-chain-b.json new file mode 100644 index 00000000000..468049cc707 --- /dev/null +++ b/.github/compatibility-test-matrices/release-v6.1.x/transfer-chain-b.json @@ -0,0 +1,16 @@ +{ + "chain-a": ["release-v6.1.x", "v6.1.0", "v5.2.0", "v4.3.0", "v4.2.0", "v4.1.1", "v3.4.0", "v3.3.1"], + "chain-b": ["release-v6.1.x"], + "entrypoint": ["TestTransferTestSuite"], + "test": [ + "TestMsgTransfer_Succeeds_Nonincentivized", + "TestMsgTransfer_Fails_InvalidAddress", + "TestMsgTransfer_Timeout_Nonincentivized", + "TestMsgTransfer_WithMemo", + "TestSendEnabledParam", + "TestReceiveEnabledParam" + ], + "relayer-type": ["rly"], + "chain-binary": ["simd"], + "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] +} diff --git a/.github/compatibility-test-matrices/release-v7.0.x/client-chain-a.json b/.github/compatibility-test-matrices/release-v7.0.x/client-chain-a.json new file mode 100644 index 00000000000..ce0b032b90b --- /dev/null +++ b/.github/compatibility-test-matrices/release-v7.0.x/client-chain-a.json @@ -0,0 +1,11 @@ +{ + "chain-a": ["release-v7.0.x"], + "chain-b": ["release-v7.0.x", "v6.1.0", "v5.2.0", "v4.3.0", "v4.2.0", "v4.1.1", "v3.4.0", "v3.3.1"], + "entrypoint": ["TestClientTestSuite"], + "test": [ + "TestClientUpdateProposal_Succeeds" + ], + "relayer-type": ["rly"], + "chain-binary": ["simd"], + "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] +} diff --git a/.github/compatibility-test-matrices/release-v7.0.x/client-chain-b.json b/.github/compatibility-test-matrices/release-v7.0.x/client-chain-b.json new file mode 100644 index 00000000000..5c49fbc7b9c --- /dev/null +++ b/.github/compatibility-test-matrices/release-v7.0.x/client-chain-b.json @@ -0,0 +1,11 @@ +{ + "chain-a": ["release-v7.0.x", "v6.1.0", "v5.2.0", "v4.3.0", "v4.2.0", "v4.1.1", "v3.4.0", "v3.3.1"], + "chain-b": ["release-v7.0.x"], + "entrypoint": ["TestClientTestSuite"], + "test": [ + "TestClientUpdateProposal_Succeeds" + ], + "relayer-type": ["rly"], + "chain-binary": ["simd"], + "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] +} diff --git a/.github/compatibility-test-matrices/release-v7.0.x/connection-chain-a.json b/.github/compatibility-test-matrices/release-v7.0.x/connection-chain-a.json new file mode 100644 index 00000000000..0cc16812e03 --- /dev/null +++ b/.github/compatibility-test-matrices/release-v7.0.x/connection-chain-a.json @@ -0,0 +1,11 @@ +{ + "chain-a": ["release-v7.0.x"], + "chain-b": ["release-v7.0.x", "v6.1.0", "v5.2.0", "v4.3.0", "v4.2.0", "v4.1.1", "v3.4.0", "v3.3.1"], + "entrypoint": ["TestConnectionTestSuite"], + "test": [ + "TestMaxExpectedTimePerBlockParam" + ], + "relayer-type": ["rly"], + "chain-binary": ["simd"], + "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] +} diff --git a/.github/compatibility-test-matrices/release-v7.0.x/connection-chain-b.json b/.github/compatibility-test-matrices/release-v7.0.x/connection-chain-b.json new file mode 100644 index 00000000000..c6aa5e28dbb --- /dev/null +++ b/.github/compatibility-test-matrices/release-v7.0.x/connection-chain-b.json @@ -0,0 +1,11 @@ +{ + "chain-a": ["release-v7.0.x", "v6.1.0", "v5.2.0", "v4.3.0", "v4.2.0", "v4.1.1", "v3.4.0", "v3.3.1"], + "chain-b": ["release-v7.0.x"], + "entrypoint": ["TestConnectionTestSuite"], + "test": [ + "TestMaxExpectedTimePerBlockParam" + ], + "relayer-type": ["rly"], + "chain-binary": ["simd"], + "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] +} diff --git a/.github/compatibility-test-matrices/release-v7.0.x/ica-chain-a.json b/.github/compatibility-test-matrices/release-v7.0.x/ica-chain-a.json new file mode 100644 index 00000000000..f6a63997f3d --- /dev/null +++ b/.github/compatibility-test-matrices/release-v7.0.x/ica-chain-a.json @@ -0,0 +1,13 @@ +{ + "chain-a": ["release-v7.0.x"], + "chain-b": ["release-v7.0.x", "v6.1.0"], + "entrypoint": ["TestInterchainAccountsTestSuite"], + "test": [ + "TestMsgSendTx_SuccessfulTransfer", + "TestMsgSendTx_FailedTransfer_InsufficientFunds", + "TestMsgSubmitTx_SuccessfulTransfer_AfterReopeningICA" + ], + "relayer-type": ["rly"], + "chain-binary": ["simd"], + "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] +} diff --git a/.github/compatibility-test-matrices/release-v7.0.x/ica-chain-b.json b/.github/compatibility-test-matrices/release-v7.0.x/ica-chain-b.json new file mode 100644 index 00000000000..452fbebe98b --- /dev/null +++ b/.github/compatibility-test-matrices/release-v7.0.x/ica-chain-b.json @@ -0,0 +1,13 @@ +{ + "chain-a": ["release-v7.0.x", "v6.1.0"], + "chain-b": ["release-v7.0.x"], + "entrypoint": ["TestInterchainAccountsTestSuite"], + "test": [ + "TestMsgSendTx_SuccessfulTransfer", + "TestMsgSendTx_FailedTransfer_InsufficientFunds", + "TestMsgSubmitTx_SuccessfulTransfer_AfterReopeningICA" + ], + "relayer-type": ["rly"], + "chain-binary": ["simd"], + "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] +} diff --git a/.github/compatibility-test-matrices/release-v7.0.x/ica-gov-chain-a.json b/.github/compatibility-test-matrices/release-v7.0.x/ica-gov-chain-a.json new file mode 100644 index 00000000000..c339471ea3d --- /dev/null +++ b/.github/compatibility-test-matrices/release-v7.0.x/ica-gov-chain-a.json @@ -0,0 +1,11 @@ +{ + "chain-a": ["release-v7.0.x", "v6.1.0"], + "chain-b": ["release-v7.0.x"], + "entrypoint": ["TestInterchainAccountsGovTestSuite"], + "test": [ + "TestInterchainAccountsGovIntegration" + ], + "relayer-type": ["rly"], + "chain-binary": ["simd"], + "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] +} diff --git a/.github/compatibility-test-matrices/release-v7.0.x/ica-gov-chain-b.json b/.github/compatibility-test-matrices/release-v7.0.x/ica-gov-chain-b.json new file mode 100644 index 00000000000..c8489613a48 --- /dev/null +++ b/.github/compatibility-test-matrices/release-v7.0.x/ica-gov-chain-b.json @@ -0,0 +1,11 @@ +{ + "chain-a": ["release-v7.0.x"], + "chain-b": ["release-v7.0.x", "v6.1.0"], + "entrypoint": ["TestInterchainAccountsGovTestSuite"], + "test": [ + "TestInterchainAccountsGovIntegration" + ], + "relayer-type": ["rly"], + "chain-binary": ["simd"], + "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] +} diff --git a/.github/compatibility-test-matrices/release-v7.0.x/ica-groups-chain-a.json b/.github/compatibility-test-matrices/release-v7.0.x/ica-groups-chain-a.json new file mode 100644 index 00000000000..888f7e22e07 --- /dev/null +++ b/.github/compatibility-test-matrices/release-v7.0.x/ica-groups-chain-a.json @@ -0,0 +1,11 @@ +{ + "chain-a": ["release-v7.0.x"], + "chain-b": ["release-v7.0.x", "v6.1.0"], + "entrypoint": ["TestInterchainAccountsGroupsTestSuite"], + "test": [ + "TestInterchainAccountsGroupsIntegration" + ], + "relayer-type": ["rly"], + "chain-binary": ["simd"], + "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] +} diff --git a/.github/compatibility-test-matrices/release-v7.0.x/ica-groups-chain-b.json b/.github/compatibility-test-matrices/release-v7.0.x/ica-groups-chain-b.json new file mode 100644 index 00000000000..ff75181eb46 --- /dev/null +++ b/.github/compatibility-test-matrices/release-v7.0.x/ica-groups-chain-b.json @@ -0,0 +1,11 @@ +{ + "chain-a": ["release-v7.0.x", "v6.1.0"], + "chain-b": ["release-v7.0.x"], + "entrypoint": ["TestInterchainAccountsGroupsTestSuite"], + "test": [ + "TestInterchainAccountsGroupsIntegration" + ], + "relayer-type": ["rly"], + "chain-binary": ["simd"], + "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] +} diff --git a/.github/compatibility-test-matrices/release-v7.0.x/incentivized-ica-chain-a.json b/.github/compatibility-test-matrices/release-v7.0.x/incentivized-ica-chain-a.json new file mode 100644 index 00000000000..bc937f1d6a5 --- /dev/null +++ b/.github/compatibility-test-matrices/release-v7.0.x/incentivized-ica-chain-a.json @@ -0,0 +1,12 @@ +{ + "chain-a": ["release-v7.0.x"], + "chain-b": ["release-v7.0.x", "v6.1.0"], + "entrypoint": ["TestIncentivizedInterchainAccountsTestSuite"], + "test": [ + "TestMsgSendTx_SuccessfulBankSend_Incentivized", + "TestMsgSendTx_FailedBankSend_Incentivized" + ], + "relayer-type": ["rly"], + "chain-binary": ["simd"], + "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] +} diff --git a/.github/compatibility-test-matrices/release-v7.0.x/incentivized-ica-chain-b.json b/.github/compatibility-test-matrices/release-v7.0.x/incentivized-ica-chain-b.json new file mode 100644 index 00000000000..000e6fcf2be --- /dev/null +++ b/.github/compatibility-test-matrices/release-v7.0.x/incentivized-ica-chain-b.json @@ -0,0 +1,12 @@ +{ + "chain-a": ["release-v7.0.x", "v6.1.0"], + "chain-b": ["release-v7.0.x"], + "entrypoint": ["TestIncentivizedInterchainAccountsTestSuite"], + "test": [ + "TestMsgSendTx_SuccessfulBankSend_Incentivized", + "TestMsgSendTx_FailedBankSend_Incentivized" + ], + "relayer-type": ["rly"], + "chain-binary": ["simd"], + "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] +} diff --git a/.github/compatibility-test-matrices/release-v7.0.x/incentivized-transfer-chain-a.json b/.github/compatibility-test-matrices/release-v7.0.x/incentivized-transfer-chain-a.json new file mode 100644 index 00000000000..54841e7e08c --- /dev/null +++ b/.github/compatibility-test-matrices/release-v7.0.x/incentivized-transfer-chain-a.json @@ -0,0 +1,16 @@ +{ + "chain-a": ["release-v7.0.x"], + "chain-b": ["release-v7.0.x", "v6.1.0", "v5.2.0", "v4.3.0", "v4.2.0", "v4.1.1"], + "entrypoint": ["TestIncentivizedTransferTestSuite"], + "test": [ + "TestMsgPayPacketFee_AsyncSingleSender_Succeeds", + "TestMsgPayPacketFee_InvalidReceiverAccount", + "TestMultiMsg_MsgPayPacketFeeSingleSender", + "TestMsgPayPacketFee_SingleSender_TimesOut", + "TestPayPacketFeeAsync_SingleSender_NoCounterPartyAddress", + "TestMsgPayPacketFee_AsyncMultipleSenders_Succeeds" + ], + "relayer-type": ["rly"], + "chain-binary": ["simd"], + "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] +} diff --git a/.github/compatibility-test-matrices/release-v7.0.x/incentivized-transfer-chain-b.json b/.github/compatibility-test-matrices/release-v7.0.x/incentivized-transfer-chain-b.json new file mode 100644 index 00000000000..f62e5040c05 --- /dev/null +++ b/.github/compatibility-test-matrices/release-v7.0.x/incentivized-transfer-chain-b.json @@ -0,0 +1,16 @@ +{ + "chain-a": ["release-v7.0.x", "v6.1.0", "v5.2.0", "v4.3.0", "v4.2.0", "v4.1.1"], + "chain-b": ["release-v7.0.x"], + "entrypoint": ["TestIncentivizedTransferTestSuite"], + "test": [ + "TestMsgPayPacketFee_AsyncSingleSender_Succeeds", + "TestMsgPayPacketFee_InvalidReceiverAccount", + "TestMultiMsg_MsgPayPacketFeeSingleSender", + "TestMsgPayPacketFee_SingleSender_TimesOut", + "TestPayPacketFeeAsync_SingleSender_NoCounterPartyAddress", + "TestMsgPayPacketFee_AsyncMultipleSenders_Succeeds" + ], + "relayer-type": ["rly"], + "chain-binary": ["simd"], + "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] +} diff --git a/.github/compatibility-test-matrices/release-v7.0.x/transfer-authz-chain-a.json b/.github/compatibility-test-matrices/release-v7.0.x/transfer-authz-chain-a.json new file mode 100644 index 00000000000..23ac0a9ed1b --- /dev/null +++ b/.github/compatibility-test-matrices/release-v7.0.x/transfer-authz-chain-a.json @@ -0,0 +1,11 @@ +{ + "chain-a": ["release-v7.0.x"], + "chain-b": ["release-v7.0.x"], + "entrypoint": ["TestAuthzTransferTestSuite"], + "test": [ + "TestAuthz_MsgTransfer_Succeeds", + "TestAuthz_InvalidTransferAuthorizations" + ], + "chain-binary": ["simd"], + "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] +} diff --git a/.github/compatibility-test-matrices/release-v7.0.x/transfer-chain-a.json b/.github/compatibility-test-matrices/release-v7.0.x/transfer-chain-a.json new file mode 100644 index 00000000000..afc45db5d08 --- /dev/null +++ b/.github/compatibility-test-matrices/release-v7.0.x/transfer-chain-a.json @@ -0,0 +1,16 @@ +{ + "chain-a": ["release-v7.0.x"], + "chain-b": ["release-v7.0.x", "v6.1.0", "v5.2.0", "v4.3.0", "v4.2.0", "v4.1.1", "v3.4.0", "v3.3.1"], + "entrypoint": ["TestTransferTestSuite"], + "test": [ + "TestMsgTransfer_Succeeds_Nonincentivized", + "TestMsgTransfer_Fails_InvalidAddress", + "TestMsgTransfer_Timeout_Nonincentivized", + "TestMsgTransfer_WithMemo", + "TestSendEnabledParam", + "TestReceiveEnabledParam" + ], + "relayer-type": ["rly"], + "chain-binary": ["simd"], + "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] +} diff --git a/.github/compatibility-test-matrices/release-v7.0.x/transfer-chain-b.json b/.github/compatibility-test-matrices/release-v7.0.x/transfer-chain-b.json new file mode 100644 index 00000000000..d3cf643c553 --- /dev/null +++ b/.github/compatibility-test-matrices/release-v7.0.x/transfer-chain-b.json @@ -0,0 +1,16 @@ +{ + "chain-a": ["release-v7.0.x", "v6.1.0", "v5.2.0", "v4.3.0", "v4.2.0", "v4.1.1", "v3.4.0", "v3.3.1"], + "chain-b": ["release-v7.0.x"], + "entrypoint": ["TestTransferTestSuite"], + "test": [ + "TestMsgTransfer_Succeeds_Nonincentivized", + "TestMsgTransfer_Fails_InvalidAddress", + "TestMsgTransfer_Timeout_Nonincentivized", + "TestMsgTransfer_WithMemo", + "TestSendEnabledParam", + "TestReceiveEnabledParam" + ], + "relayer-type": ["rly"], + "chain-binary": ["simd"], + "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] +} diff --git a/.github/compatibility-test-matrices/unreleased/client.json b/.github/compatibility-test-matrices/unreleased/client.json new file mode 100644 index 00000000000..48d0bab8140 --- /dev/null +++ b/.github/compatibility-test-matrices/unreleased/client.json @@ -0,0 +1,11 @@ +{ + "chain-a": ["release-v7.0.x", "release-v6.1.x", "release-v5.2.x", "release-v4.3.x", "release-v4.2.x", "release-v3.4.x"], + "chain-b": ["release-v7.0.x", "release-v6.1.x", "release-v5.2.x", "release-v4.3.x", "release-v4.2.x", "release-v3.4.x"], + "entrypoint": ["TestClientTestSuite"], + "test": [ + "TestClientUpdateProposal_Succeeds" + ], + "relayer-type": ["rly"], + "chain-binary": ["simd"], + "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] +} diff --git a/.github/compatibility-test-matrices/unreleased/connection.json b/.github/compatibility-test-matrices/unreleased/connection.json new file mode 100644 index 00000000000..f386a945054 --- /dev/null +++ b/.github/compatibility-test-matrices/unreleased/connection.json @@ -0,0 +1,11 @@ +{ + "chain-a": ["release-v7.0.x", "release-v6.1.x", "release-v5.2.x", "release-v4.3.x", "release-v4.2.x", "release-v3.4.x"], + "chain-b": ["release-v7.0.x", "release-v6.1.x", "release-v5.2.x", "release-v4.3.x", "release-v4.2.x", "release-v3.4.x"], + "entrypoint": ["TestConnectionTestSuite"], + "test": [ + "TestMaxExpectedTimePerBlockParam" + ], + "relayer-type": ["rly"], + "chain-binary": ["simd"], + "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] +} diff --git a/.github/compatibility-test-matrices/unreleased/incentivized-transfer.json b/.github/compatibility-test-matrices/unreleased/incentivized-transfer.json new file mode 100644 index 00000000000..53637437077 --- /dev/null +++ b/.github/compatibility-test-matrices/unreleased/incentivized-transfer.json @@ -0,0 +1,16 @@ +{ + "chain-a": ["release-v7.0.x", "release-v6.1.x", "release-v5.2.x", "release-v4.3.x", "release-v4.2.x"], + "chain-b": ["release-v7.0.x", "release-v6.1.x", "release-v5.2.x", "release-v4.3.x", "release-v4.2.x"], + "entrypoint": ["TestIncentivizedTransferTestSuite"], + "test": [ + "TestMsgPayPacketFee_AsyncSingleSender_Succeeds", + "TestMsgPayPacketFee_InvalidReceiverAccount", + "TestMultiMsg_MsgPayPacketFeeSingleSender", + "TestMsgPayPacketFee_SingleSender_TimesOut", + "TestPayPacketFeeAsync_SingleSender_NoCounterPartyAddress", + "TestMsgPayPacketFee_AsyncMultipleSenders_Succeeds" + ], + "relayer-type": ["rly"], + "chain-binary": ["simd"], + "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] +} diff --git a/.github/compatibility-test-matrices/unreleased/transfer.json b/.github/compatibility-test-matrices/unreleased/transfer.json new file mode 100644 index 00000000000..dc93ca6f85b --- /dev/null +++ b/.github/compatibility-test-matrices/unreleased/transfer.json @@ -0,0 +1,16 @@ +{ + "chain-a": ["release-v7.0.x", "release-v6.1.x", "release-v5.2.x", "release-v4.2.x", "release-v3.4.x"], + "chain-b": ["release-v7.0.x", "release-v6.1.x", "release-v5.2.x", "release-v4.2.x", "release-v3.4.x"], + "entrypoint": ["TestTransferTestSuite"], + "test": [ + "TestMsgTransfer_Succeeds_Nonincentivized", + "TestMsgTransfer_Fails_InvalidAddress", + "TestMsgTransfer_Timeout_Nonincentivized", + "TestMsgTransfer_WithMemo", + "TestSendEnabledParam", + "TestReceiveEnabledParam" + ], + "relayer-type": ["rly"], + "chain-binary": ["simd"], + "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] +} diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 1fed7f0c1ac..a9b4e5309f9 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -10,9 +10,5 @@ updates: schedule: interval: daily open-pull-requests-limit: 10 - reviewers: - - colin-axner - - fedekunze - - AdityaSripal labels: - dependencies diff --git a/.github/mergify.yml b/.github/mergify.yml index 2dfc4653a7d..8ff9b606452 100644 --- a/.github/mergify.yml +++ b/.github/mergify.yml @@ -18,67 +18,67 @@ pull_request_rules: commit_message_template: | {{ title }} (#{{ number }}) {{ body }} - - name: backport patches to v2.1.x branch + - name: backport patches to v3.3.x branch conditions: - base=main - - label=backport-to-v2.1.x + - label=backport-to-v3.3.x actions: backport: branches: - - release/v2.1.x - - name: backport patches to v2.2.x branch + - release/v3.3.x + - name: backport patches to v3.4.x branch conditions: - base=main - - label=backport-to-v2.2.x + - label=backport-to-v3.4.x actions: backport: branches: - - release/v2.2.x - - name: backport patches to v2.3.x branch + - release/v3.4.x + - name: backport patches to v4.1.x branch conditions: - base=main - - label=backport-to-v2.3.x + - label=backport-to-v4.1.x actions: backport: branches: - - release/v2.3.x - - name: backport patches to v2.4.x branch + - release/v4.1.x + - name: backport patches to v4.2.x branch conditions: - base=main - - label=backport-to-v2.4.x + - label=backport-to-v4.2.x actions: backport: branches: - - release/v2.4.x - - name: backport patches to v3.0.x branch + - release/v4.2.x + - name: backport patches to v4.3.x branch conditions: - base=main - - label=backport-to-v3.0.x + - label=backport-to-v4.3.x actions: backport: branches: - - release/v3.0.x - - name: backport patches to v3.1.x branch + - release/v4.3.x + - name: backport patches to v5.2.x branch conditions: - base=main - - label=backport-to-v3.1.x + - label=backport-to-v5.2.x actions: backport: branches: - - release/v3.1.x - - name: backport patches to v3.2.x branch + - release/v5.2.x + - name: backport patches to v6.1.x branch conditions: - base=main - - label=backport-to-v3.2.x + - label=backport-to-v6.1.x actions: backport: branches: - - release/v3.2.x - - name: backport patches to v4.0.x branch + - release/v6.1.x + - name: backport patches to v7.0.x branch conditions: - base=main - - label=backport-to-v4.0.x + - label=backport-to-v7.0.x actions: backport: branches: - - release/v4.0.x + - release/v7.0.x diff --git a/.github/scripts/go-imports.sh b/.github/scripts/go-imports.sh deleted file mode 100755 index bc5e6db3f86..00000000000 --- a/.github/scripts/go-imports.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env bash -formatted_files="$(docker run -v "$(pwd)":/ibc-go --rm -w "/ibc-go" --entrypoint="" cytopia/goimports goimports -l -local 'github.com/cosmos/ibc-go' /ibc-go)" - -exit_code=0 -for f in $formatted_files -do - # we don't care about formatting in pb.go files. - if [ "${f: -5}" == "pb.go" ]; then - continue - fi - exit_code=1 - echo "formatted file ${f}..." -done - -if [ "${exit_code}" == 1 ]; then - echo "not all files were correctly formated, run the following:" - echo "make goimports" -fi - -exit $exit_code diff --git a/.github/workflows/build-simd-image-from-tag.yml b/.github/workflows/build-simd-image-from-tag.yml new file mode 100644 index 00000000000..5ac105b9c88 --- /dev/null +++ b/.github/workflows/build-simd-image-from-tag.yml @@ -0,0 +1,39 @@ +name: Build Simd Image +on: + workflow_dispatch: + inputs: + tag: + description: 'The tag of the image to build' + required: true + type: string + ibc-go-version: + description: 'The ibc-go version to be added as a label' + required: true + type: string + +env: + REGISTRY: ghcr.io + ORG: cosmos + IMAGE_NAME: ibc-go-simd + GIT_TAG: "${{ inputs.tag }}" + +jobs: + build-image-at-tag: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + ref: "${{ env.GIT_TAG }}" + fetch-depth: 0 + - name: Log in to the Container registry + uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Build image + run: | + # remove any `/` characters from the docker tag and replace them with a - + docker_tag="$(echo $GIT_TAG | sed 's/\//-/')" + docker build . -t "${REGISTRY}/${ORG}/${IMAGE_NAME}:${docker_tag}" --build-arg IBC_GO_VERSION=${{ inputs.ibc-go-version }} + docker push "${REGISTRY}/${ORG}/${IMAGE_NAME}:${docker_tag}" diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index f9a7c4a9230..9cdbe5618cf 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -40,7 +40,7 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v3 - - uses: technote-space/get-diff-action@v6.1.0 + - uses: technote-space/get-diff-action@v6.1.2 with: PATTERNS: | **/**.go diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml new file mode 100644 index 00000000000..eb9b6ef31ff --- /dev/null +++ b/.github/workflows/deploy-docs.yml @@ -0,0 +1,42 @@ +name: Deploy docs +# This job builds and deploys documenation to github pages. +# It runs on every push to main with a change in the docs folder. +on: + workflow_dispatch: + push: + branches: + - main + - "release/**" + paths: + - "docs/**" + - .github/workflows/deploy-docs.yml + +permissions: + contents: read + +jobs: + build-and-deploy: + permissions: + contents: write # for JamesIves/github-pages-deploy-action to push changes in repo + runs-on: ubuntu-latest + container: + image: ghcr.io/cosmos/website-deployment + steps: + - name: Checkout 🛎️ + uses: actions/checkout@v3 + with: + persist-credentials: false + fetch-depth: 0 + path: "." + + - name: Build 🔧 + run: | + git config --global --add safe.directory /__w/ibc-go/ibc-go + make build-docs LEDGER_ENABLED=false + + - name: Deploy 🚀 + uses: JamesIves/github-pages-deploy-action@v4.4.1 + with: + branch: gh-pages + folder: ~/output + single-commit: true diff --git a/.github/workflows/e2e-compatibility-unreleased.yaml b/.github/workflows/e2e-compatibility-unreleased.yaml new file mode 100644 index 00000000000..19be796dd9f --- /dev/null +++ b/.github/workflows/e2e-compatibility-unreleased.yaml @@ -0,0 +1,49 @@ +name: Compatibility E2E (Unreleased) +on: + workflow_dispatch: + inputs: + release-branch: + description: 'Select unreleased to run all release branches against each other' + required: true + type: choice + options: + - unreleased + +env: + REGISTRY: ghcr.io + ORG: cosmos + IMAGE_NAME: ibc-go-simd + RELEASE_BRANCH: '${{ inputs.release-branch }}' + +jobs: + transfer: + needs: + - build-release-images + uses: ./.github/workflows/e2e-compatibility-workflow-call.yaml + with: + test-file-directory: "unreleased" + test-suite: "transfer" + + connection: + needs: + - build-release-images + uses: ./.github/workflows/e2e-compatibility-workflow-call.yaml + with: + test-file-directory: "unreleased" + test-suite: "connection" + + client: + needs: + - build-release-images + uses: ./.github/workflows/e2e-compatibility-workflow-call.yaml + with: + test-file-directory: "unreleased" + test-suite: "client" + + incentivized-transfer: + needs: + - build-release-images + uses: ./.github/workflows/e2e-compatibility-workflow-call.yaml + with: + test-file-directory: "unreleased" + test-suite: "incentivized-transfer" \ No newline at end of file diff --git a/.github/workflows/e2e-compatibility-workflow-call.yaml b/.github/workflows/e2e-compatibility-workflow-call.yaml new file mode 100644 index 00000000000..58c5642a4f4 --- /dev/null +++ b/.github/workflows/e2e-compatibility-workflow-call.yaml @@ -0,0 +1,56 @@ +on: + workflow_call: + inputs: + test-file-directory: + description: 'Directory containing compatibility matrices' + required: true + type: string + test-suite: + description: "Test suite to run" + required: true + type: string + +jobs: + load-test-matrix: + outputs: + test-matrix: ${{ steps.set-test-matrix.outputs.test-matrix }} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: andstor/file-existence-action@v2 + with: + files: ".github/compatibility-test-matrices/${{ inputs.test-file-directory }}/${{ inputs.test-suite }}.json" + - run: | + # use jq -c to put the full json contents on a single line. This is required when using the json body + # to create the matrix in the following job. + test_matrix="$(cat .github/compatibility-test-matrices/${{ inputs.test-file-directory }}/${{ inputs.test-suite }}.json | jq -c)" + echo $test_matrix + echo "test-matrix=$test_matrix" >> $GITHUB_OUTPUT + id: set-test-matrix + + e2e: + runs-on: ubuntu-latest + needs: load-test-matrix + if: needs.load-test-matrix.outputs.test-matrix + strategy: + fail-fast: false + matrix: ${{ fromJSON(needs.load-test-matrix.outputs.test-matrix) }} + steps: + - name: Checkout the ibc-go repo + uses: actions/checkout@v3 + with: + repository: cosmos/ibc-go + - uses: actions/setup-go@v3 + with: + go-version: 1.19 + - name: Run e2e Test + run: | + cd e2e + make e2e-test entrypoint=${{ matrix.entrypoint }} test=${{ matrix.test }} + env: + # each test has its own set of variables to specify which images are used. + CHAIN_IMAGE: "${{ matrix.chain-image }}" + CHAIN_A_TAG: "${{ matrix.chain-a }}" + CHAIN_B_TAG: "${{ matrix.chain-b }}" + CHAIN_BINARY: "${{ matrix.chain-binary }}" + RELAYER_TYPE: "${{ matrix.relayer-type }}" diff --git a/.github/workflows/e2e-compatibility.yaml b/.github/workflows/e2e-compatibility.yaml new file mode 100644 index 00000000000..76c755a6265 --- /dev/null +++ b/.github/workflows/e2e-compatibility.yaml @@ -0,0 +1,194 @@ +name: Compatibility E2E +on: + workflow_dispatch: + inputs: + release-branch: + description: 'Release branch to test' + required: true + type: choice + options: + - release/v3.4.x + - release/v4.2.x + - release/v4.3.x + - release/v5.2.x + - release/v6.1.x + - release/v7.0.x + - main + ibc-go-version: + description: 'The version of ibc-go that is going to be released' + required: true + type: string + +env: + REGISTRY: ghcr.io + ORG: cosmos + IMAGE_NAME: ibc-go-simd + RELEASE_BRANCH: '${{ inputs.release-branch }}' + +jobs: + determine-test-directory: + runs-on: ubuntu-latest + outputs: + test-directory: ${{ steps.set-test-dir.outputs.test-directory }} + steps: + - run: | + test_dir="$(echo $RELEASE_BRANCH | sed 's/\//-/')" + echo $test_dir + echo "test-directory=$test_dir" >> $GITHUB_OUTPUT + id: set-test-dir + + # build-release-images builds all docker images that are relevant for the compatibility tests. If a single release + # branch is specified, only that image will be built, e.g. release-v6.0.x. + build-release-images: + runs-on: ubuntu-latest + strategy: + matrix: + release-branch: + - release/v3.4.x + - release/v4.2.x + - release/v4.3.x + - release/v5.2.x + - release/v6.1.x + - release/v7.0.x + - main + steps: + - uses: actions/checkout@v3 + if: env.RELEASE_BRANCH == matrix.release-branch + with: + ref: "${{ matrix.release-branch }}" + fetch-depth: 0 + - name: Log in to the Container registry + if: env.RELEASE_BRANCH == matrix.release-branch + uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Build image + if: env.RELEASE_BRANCH == matrix.release-branch + run: | + docker_tag="$(echo ${{ matrix.release-branch }} | sed 's/\//-/')" + docker build . -t "${REGISTRY}/${ORG}/${IMAGE_NAME}:$docker_tag" --build-arg IBC_GO_VERSION=${{ inputs.ibc-go-version }} + docker push "${REGISTRY}/${ORG}/${IMAGE_NAME}:$docker_tag" + - name: Display image details + if: env.RELEASE_BRANCH == matrix.release-branch + run: | + docker_tag="$(echo ${{ matrix.release-branch }} | sed 's/\//-/')" + docker inspect "${REGISTRY}/${ORG}/${IMAGE_NAME}:$docker_tag" + + transfer-chain-a: + needs: + - build-release-images + - determine-test-directory + uses: ./.github/workflows/e2e-compatibility-workflow-call.yaml + with: + test-file-directory: "${{ needs.determine-test-directory.outputs.test-directory }}" + test-suite: "transfer-chain-a" + + transfer-chain-b: + needs: + - build-release-images + - determine-test-directory + uses: ./.github/workflows/e2e-compatibility-workflow-call.yaml + with: + test-file-directory: "${{ needs.determine-test-directory.outputs.test-directory }}" + test-suite: "transfer-chain-b" + + transfer-authz-chain-a: + needs: + - build-release-images + - determine-test-directory + uses: ./.github/workflows/e2e-compatibility-workflow-call.yaml + with: + test-file-directory: "${{ needs.determine-test-directory.outputs.test-directory }}" + test-suite: "transfer-authz-chain-a" + + connection-chain-a: + needs: + - build-release-images + - determine-test-directory + uses: ./.github/workflows/e2e-compatibility-workflow-call.yaml + with: + test-file-directory: "${{ needs.determine-test-directory.outputs.test-directory }}" + test-suite: "connection-chain-a" + + connection-chain-b: + needs: + - build-release-images + - determine-test-directory + uses: ./.github/workflows/e2e-compatibility-workflow-call.yaml + with: + test-file-directory: "${{ needs.determine-test-directory.outputs.test-directory }}" + test-suite: "connection-chain-b" + + client-chain-a: + needs: + - build-release-images + - determine-test-directory + uses: ./.github/workflows/e2e-compatibility-workflow-call.yaml + with: + test-file-directory: "${{ needs.determine-test-directory.outputs.test-directory }}" + test-suite: "client-chain-a" + + client-chain-b: + needs: + - build-release-images + - determine-test-directory + uses: ./.github/workflows/e2e-compatibility-workflow-call.yaml + with: + test-file-directory: "${{ needs.determine-test-directory.outputs.test-directory }}" + test-suite: "client-chain-b" + + incentivized-transfer-chain-a: + needs: + - build-release-images + - determine-test-directory + uses: ./.github/workflows/e2e-compatibility-workflow-call.yaml + with: + test-file-directory: "${{ needs.determine-test-directory.outputs.test-directory }}" + test-suite: "incentivized-transfer-chain-a" + + incentivized-transfer-chain-b: + needs: + - build-release-images + - determine-test-directory + uses: ./.github/workflows/e2e-compatibility-workflow-call.yaml + with: + test-file-directory: "${{ needs.determine-test-directory.outputs.test-directory }}" + test-suite: "incentivized-transfer-chain-b" + + ica-chain-a: + needs: + - build-release-images + - determine-test-directory + uses: ./.github/workflows/e2e-compatibility-workflow-call.yaml + with: + test-file-directory: "${{ needs.determine-test-directory.outputs.test-directory }}" + test-suite: "ica-chain-a" + + incentivized-ica-chain-a: + needs: + - build-release-images + - determine-test-directory + uses: ./.github/workflows/e2e-compatibility-workflow-call.yaml + with: + test-file-directory: "${{ needs.determine-test-directory.outputs.test-directory }}" + test-suite: "incentivized-ica-chain-a" + + ica-groups-chain-a: + needs: + - build-release-images + - determine-test-directory + uses: ./.github/workflows/e2e-compatibility-workflow-call.yaml + with: + test-file-directory: "${{ needs.determine-test-directory.outputs.test-directory }}" + test-suite: "ica-groups-chain-a" + + ica-gov-chain-a: + needs: + - build-release-images + - determine-test-directory + uses: ./.github/workflows/e2e-compatibility-workflow-call.yaml + with: + test-file-directory: "${{ needs.determine-test-directory.outputs.test-directory }}" + test-suite: "ica-gov-chain-a" diff --git a/.github/workflows/e2e-fork.yml b/.github/workflows/e2e-fork.yml index d3717d2c35a..1f3f691c9ae 100644 --- a/.github/workflows/e2e-fork.yml +++ b/.github/workflows/e2e-fork.yml @@ -5,7 +5,9 @@ on: branches: - main paths-ignore: - - docs/** + - "docs/**" + - "**.md" + - "LICENSE" jobs: # dynamically build a matrix of test/test suite pairs to run @@ -18,14 +20,16 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-go@v3 with: - go-version: 1.18 + go-version: 1.19 - id: set-matrix - run: echo "::set-output name=matrix::$(go run cmd/build_test_matrix/main.go)" + run: echo "matrix=$(go run cmd/build_test_matrix/main.go)" >> $GITHUB_OUTPUT + env: + TEST_EXCLUSIONS: "TestInterTxTestSuite,TestIncentivizedInterTxTestSuite,TestUpgradeTestSuite" e2e: env: - SIMD_TAG: latest - SIMD_IMAGE: ibc-go-simd-e2e + CHAIN_A_TAG: latest + CHAIN_IMAGE: ibc-go-simd if: ${{ github.event.pull_request.head.repo.fork || github.actor == 'dependabot[bot]' || github.event_name == 'workflow_dispatch' }} needs: - build-test-matrix @@ -36,15 +40,12 @@ jobs: steps: - uses: actions/checkout@v3 - name: Docker Build - run: docker build . -t "${SIMD_IMAGE}:${SIMD_TAG}" + run: docker build . -t "${CHAIN_IMAGE}:${CHAIN_A_TAG}" --build-arg IBC_GO_VERSION=latest - name: Setup Go uses: actions/setup-go@v3 with: - go-version: 1.18 + go-version: 1.19 - name: Run e2e Test - env: - # see images here https://github.com/cosmos/relayer/pkgs/container/relayer/versions - RLY_TAG: "v2.0.0-rc2" run: | cd e2e - make e2e-test suite=${{ matrix.suite }} test=${{ matrix.test }} + make e2e-test entrypoint=${{ matrix.entrypoint }} test=${{ matrix.test }} diff --git a/.github/workflows/e2e-manual-icad.yaml b/.github/workflows/e2e-manual-icad.yaml new file mode 100644 index 00000000000..4e01772ef75 --- /dev/null +++ b/.github/workflows/e2e-manual-icad.yaml @@ -0,0 +1,67 @@ +name: Manual E2E (Icad) +on: + # when https://github.com/community/community/discussions/11795 is resolved + # we will be able to dynamically build up the list of valid inputs. + # for now this needs to be manual. + workflow_dispatch: + inputs: + test-entry-point: + description: 'Test entry point' + required: true + type: choice + options: + - TestInterTxTestSuite + - TestIncentivizedInterTxTestSuite + - TestUpgradeTestSuite + chain-image: + description: 'The image to use for chain A' + required: true + type: string + default: "ghcr.io/cosmos/ibc-go-icad" + chain-a-tag: + description: 'The tag to use for chain A' + required: true + type: choice + default: master + options: + - master + - v0.4.3 + - v0.3.6 + - v0.2.6 + - v0.1.7 + chain-b-tag: + default: master + description: 'The tag to use for chain B' + required: true + type: choice + options: + - master + - v0.4.3 + - v0.3.6 + - v0.2.6 + - v0.1.7 + relayer-type: + default: rly + description: 'The relayer to use' + required: true + type: choice + options: + - rly + - hermes + relayer-tag: + description: 'The tag to use for the relayer' + required: true + default: "v2.1.2" + type: string + + +jobs: + e2e-manual: + uses: ./.github/workflows/e2e-test-workflow-call.yml + with: + chain-image: "${{ github.event.inputs.chain-image }}" + chain-a-tag: "${{ github.event.inputs.chain-a-tag }}" + chain-b-tag: "${{ github.event.inputs.chain-b-tag }}" + relayer-tag: "${{ github.event.inputs.relayer-tag }}" + test-entry-point: "${{ github.event.inputs.test-entry-point }}" + chain-binary: "icad" diff --git a/.github/workflows/e2e-manual-simd.yaml b/.github/workflows/e2e-manual-simd.yaml new file mode 100644 index 00000000000..3aea4fe4f6f --- /dev/null +++ b/.github/workflows/e2e-manual-simd.yaml @@ -0,0 +1,92 @@ +name: Manual E2E (Simd) +on: + # when https://github.com/community/community/discussions/11795 is resolved + # we will be able to dynamically build up the list of valid inputs. + # for now this needs to be manual. + workflow_dispatch: + inputs: + test-entry-point: + description: 'Test entry point' + required: true + type: choice + options: + - TestTransferTestSuite + - TestIncentivizedTransferTestSuite + - TestConnectionTestSuite + - TestInterchainAccountsTestSuite + - TestInterchainAccountsGroupsTestSuite + - TestInterchainAccountsGovTestSuite + - TestIncentivizedInterchainAccountsTestSuite + - TestAuthzTransferTestSuite + chain-image: + description: 'The image to use for chain A' + required: true + type: string + default: "ghcr.io/cosmos/ibc-go-simd" + chain-binary: + description: 'Specify the chain binary to be used' + required: true + type: string + default: "simd" + chain-a-tag: + description: 'The tag to use for chain A' + required: true + type: choice + default: main + options: + - main + - v6.1.0 + - v5.2.0 + - v4.3.0 + - v4.2.0 + - v4.1.1 + - v3.4.0 + - v3.3.1 + chain-a-tag-override: + description: 'Specify an arbitrary tag for chain A' + required: false + type: string + chain-b-tag: + default: v6.0.0 + description: 'The tag to use for chain B' + required: true + type: choice + options: + - main + - v6.1.0 + - v5.2.0 + - v4.3.0 + - v4.2.0 + - v4.1.1 + - v3.4.0 + - v3.3.1 + chain-b-tag-override: + description: 'Specify an arbitrary tag for chain B' + required: false + type: string + relayer-type: + default: rly + description: 'The relayer to use' + required: true + type: choice + options: + - rly + - hermes + relayer-tag: + description: 'The tag to use for the relayer' + required: true + default: "v2.1.2" + type: string + + +jobs: + e2e-manual: + uses: ./.github/workflows/e2e-test-workflow-call.yml + with: + chain-image: "${{ github.event.inputs.chain-image }}" + chain-a-tag: "${{ github.event.inputs.chain-a-tag-override || github.event.inputs.chain-a-tag }}" + chain-b-tag: "${{ github.event.inputs.chain-b-tag-override || github.event.inputs.chain-b-tag }}" + relayer-tag: "${{ github.event.inputs.relayer-tag }}" + relayer-type: "${{ github.event.inputs.relayer-type }}" + test-entry-point: "${{ github.event.inputs.test-entry-point }}" + chain-binary: "${{ github.event.inputs.chain-binary }}" diff --git a/.github/workflows/e2e-test-workflow-call.yml b/.github/workflows/e2e-test-workflow-call.yml new file mode 100644 index 00000000000..3ed7f572c11 --- /dev/null +++ b/.github/workflows/e2e-test-workflow-call.yml @@ -0,0 +1,165 @@ +on: + workflow_call: + inputs: + test-entry-point: + description: "Test entry point" + required: false + type: string + default: "" # empty string means run all tests + test: + description: "test name to run as standalone" + required: false + type: string + default: "" + test-exclusions: + description: "Comma separated list of tests to skip" + required: false + type: string + default: "" # empty string means don't skip any test. + chain-image: + description: "The image to use for chains" + required: true + type: string + default: "ghcr.io/cosmos/ibc-go-simd" + chain-a-tag: + description: "The tag to use for chain A" + required: true + type: string + default: main + chain-b-tag: + default: v4.0.0 + description: "The tag to use for chain B" + required: true + type: string + chain-binary: + default: "simd" + description: "The chain binary" + required: false + type: string + chain-upgrade-tag: + default: "" + description: "The image tag that the chain will be upgraded to" + required: false + type: string + upgrade-plan-name: + default: "" + description: "The upgrade plan name" + required: false + type: string + relayer-type: + description: "The type of relayer to use" + required: false + default: "rly" + type: string + relayer-tag: + description: "The tag to use for the relayer" + required: false + default: "" # the tests themselves will choose a sensible default when unset. + type: string + build-and-push-docker-image: + description: "Flag to specify if the docker image should be built and pushed beforehand" + required: false + type: boolean + default: false + +env: + REGISTRY: ghcr.io + IMAGE_NAME: ibc-go-simd + +jobs: + test-details: + runs-on: ubuntu-latest + steps: + - name: Display Inputs + run: | + echo "Chain Image: ${{ inputs.chain-image }}" + echo "Chain A Tag: ${{ inputs.chain-a-tag }}" + echo "Chain B Tag: ${{ inputs.chain-b-tag }}" + echo "Chain Upgrade Tag: ${{ inputs.chain-upgrade-tag }}" + echo "Upgrade Plan Name: ${{ inputs.upgrade-plan-name }}" + echo "Relayer Type: ${{ inputs.relayer-type }}" + echo "Relayer Tag: ${{ inputs.relayer-tag }}" + echo "Test Entry Point: ${{ inputs.test-entry-point }}" + echo "Test: ${{ inputs.test }}" + echo "Github Ref Name: ${{ github.ref_name }}" + + # we skip individual steps rather than the full job as e2e-tests will not run if this task + # is skipped. But will run if every individual task is skipped. There is no current way of conditionally needing + # a job. + docker-build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + if: ${{ inputs.build-and-push-docker-image }} + - name: Log in to the Container registry + if: ${{ inputs.build-and-push-docker-image }} + uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata (tags, labels) for Docker + if: ${{ inputs.build-and-push-docker-image }} + id: meta + uses: docker/metadata-action@507c2f2dc502c992ad446e3d7a5dfbe311567a96 + with: + images: ${{ env.REGISTRY }}/cosmos/${{ env.IMAGE_NAME }} + + - name: Build and push Docker image + if: ${{ inputs.build-and-push-docker-image }} + uses: docker/build-push-action@3b5e8027fcad23fda98b2e3ac259d8d67585f671 + with: + context: . + push: true + tags: ${{ steps.meta.outputs.tags }} + build-args: | + IBC_GO_VERSION=${{ github.ref_name }} + + # dynamically build a matrix of test/test suite pairs to run + build-test-matrix: + runs-on: ubuntu-latest + outputs: + matrix: ${{ steps.set-matrix.outputs.matrix }} + steps: + - uses: actions/checkout@v3 + with: + repository: cosmos/ibc-go + - uses: actions/setup-go@v3 + with: + go-version: 1.19 + - id: set-matrix + run: echo "matrix=$(go run cmd/build_test_matrix/main.go)" >> $GITHUB_OUTPUT + env: + TEST_ENTRYPOINT: "${{ inputs.test-entry-point }}" + TEST_EXCLUSIONS: "${{ inputs.test-exclusions }}" + TEST_NAME: "${{ inputs.test }}" + + e2e-tests: + runs-on: ubuntu-latest + needs: + - build-test-matrix + - docker-build + env: + CHAIN_IMAGE: "${{ inputs.chain-image }}" + CHAIN_A_TAG: "${{ inputs.chain-a-tag }}" + CHAIN_B_TAG: "${{ inputs.chain-b-tag }}" + RELAYER_TAG: "${{ inputs.relayer-tag }}" + RELAYER_TYPE: "${{ inputs.relayer-type }}" + CHAIN_BINARY: "${{ inputs.chain-binary }}" + CHAIN_UPGRADE_TAG: "${{ inputs.chain-upgrade-tag }}" + CHAIN_UPGRADE_PLAN: "${{ inputs.upgrade-plan-name }}" + strategy: + fail-fast: false + matrix: ${{ fromJSON(needs.build-test-matrix.outputs.matrix) }} + steps: + - uses: actions/checkout@v3 + with: + repository: cosmos/ibc-go + - uses: actions/setup-go@v3 + with: + go-version: 1.19 + - name: Run e2e Test + run: | + cd e2e + make e2e-test entrypoint=${{ matrix.entrypoint }} test=${{ matrix.test }} diff --git a/.github/workflows/e2e-upgrade.yaml b/.github/workflows/e2e-upgrade.yaml new file mode 100644 index 00000000000..b596f22a9ce --- /dev/null +++ b/.github/workflows/e2e-upgrade.yaml @@ -0,0 +1,43 @@ +name: Tests / E2E Upgrade +on: + workflow_dispatch: + + schedule: + - cron: '0 0 * * *' + +jobs: + upgrade-v5: + uses: cosmos/ibc-go/.github/workflows/e2e-test-workflow-call.yml@main + with: + chain-image: ghcr.io/cosmos/ibc-go-simd + chain-binary: simd + chain-a-tag: v4.3.0 + chain-b-tag: v4.3.0 + chain-upgrade-tag: v5.1.0 + upgrade-plan-name: "normal upgrade" + test-entry-point: "TestUpgradeTestSuite" + test: "TestIBCChainUpgrade" + + upgrade-v6: + uses: cosmos/ibc-go/.github/workflows/e2e-test-workflow-call.yml@main + with: + chain-image: ghcr.io/cosmos/ibc-go-icad + chain-binary: icad + chain-a-tag: v0.3.5 + chain-b-tag: v0.3.5 + chain-upgrade-tag: v0.4.1 + upgrade-plan-name: "ibc-go/v6" + test-entry-point: "TestUpgradeTestSuite" + test: "TestV5ToV6ChainUpgrade" + + upgrade-v7: + uses: cosmos/ibc-go/.github/workflows/e2e-test-workflow-call.yml@main + with: + chain-image: ghcr.io/cosmos/ibc-go-simd + chain-binary: simd + chain-a-tag: v6.1.0 + chain-b-tag: v6.1.0 + chain-upgrade-tag: v7.0.0-rc0 # TODO: needs v7.0.0-rc1 when cut + upgrade-plan-name: "v7" + test-entry-point: "TestUpgradeTestSuite" + test: "TestV6ToV7ChainUpgrade" diff --git a/.github/workflows/e2e.yaml b/.github/workflows/e2e.yaml index ae4d29e7b51..9fe4b02cefd 100644 --- a/.github/workflows/e2e.yaml +++ b/.github/workflows/e2e.yaml @@ -2,104 +2,69 @@ name: Tests / E2E on: workflow_dispatch: pull_request: + paths-ignore: + - "docs/**" + - "**.md" + - "LICENSE" push: branches: - main paths-ignore: - - docs/** - -env: - REGISTRY: ghcr.io - IMAGE_NAME: ibc-go-simd-e2e + - "docs/**" + - "**.md" + - "LICENSE" jobs: - docker-build: - if: ${{ !github.event.pull_request.head.repo.fork && github.actor != 'dependabot[bot]' }} - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: Log in to the Container registry - uses: docker/login-action@49ed152c8eca782a232dede0303416e8f356c37b - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Extract metadata (tags, labels) for Docker - id: meta - uses: docker/metadata-action@69f6fc9d46f2f8bf0d5491e4aabe0bb8c6a4678a - with: - images: ${{ env.REGISTRY }}/cosmos/${{ env.IMAGE_NAME }} - - - name: Build and push Docker image - uses: docker/build-push-action@1cb9d22b932e4832bb29793b7777ec860fc1cde0 - with: - context: . - push: true - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - - # dynamically build a matrix of test/test suite pairs to run - build-test-matrix: - if: ${{ !github.event.pull_request.head.repo.fork && github.actor != 'dependabot[bot]' }} - runs-on: ubuntu-latest - outputs: - matrix: ${{ steps.set-matrix.outputs.matrix }} - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-go@v3 - with: - go-version: 1.18 - - id: set-matrix - run: echo "::set-output name=matrix::$(go run cmd/build_test_matrix/main.go)" - - - # the tag of the image will differ if this is a PR or the branch is being merged into main. - # we store the tag as an environment variable and use it in the E2E tests to determine the tag. - determine-image-tag: - if: ${{ !github.event.pull_request.head.repo.fork && github.actor != 'dependabot[bot]' }} - runs-on: ubuntu-latest - outputs: - simd-tag: ${{ steps.get-tag.outputs.simd-tag }} - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-go@v3 - with: - go-version: 1.18 - - id: get-tag - run: | - tag=$(go run cmd/determine_simd_tag/main.go -pr "${{ github.event.pull_request.number }}" ) - echo "Using tag $tag" - echo "::set-output name=simd-tag::$tag" + determine-image-tag: + if: ${{ !github.event.pull_request.head.repo.fork && github.actor != 'dependabot[bot]' }} + runs-on: ubuntu-latest + outputs: + simd-tag: ${{ steps.get-tag.outputs.simd-tag }} + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-go@v3 + with: + go-version: 1.18 + - id: get-tag + run: | + if [ -z "${{ github.event.pull_request.number }}" ] + then + echo "simd-tag=main" >> $GITHUB_OUTPUT + else + tag="pr-${{ github.event.pull_request.number }}" + echo "Using tag $tag" + echo "simd-tag=$tag" >> $GITHUB_OUTPUT + fi + build-e2e: + if: ${{ !github.event.pull_request.head.repo.fork && github.actor != 'dependabot[bot]' }} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-go@v3 + with: + go-version: 1.19 + - name: Build e2e + run: | + cd e2e + test_dirs="$(ls -A tests)" + for td in $test_dirs + do + go test -c "./tests/${td}" + done - e2e: - if: ${{ !github.event.pull_request.head.repo.fork && github.actor != 'dependabot[bot]' }} - runs-on: ubuntu-latest - needs: - - build-test-matrix - - determine-image-tag - - docker-build - env: - SIMD_TAG: ${{ needs.determine-image-tag.outputs.simd-tag }} - SIMD_IMAGE: ghcr.io/cosmos/ibc-go-simd-e2e - # see images here https://github.com/cosmos/relayer/pkgs/container/relayer/versions - RLY_TAG: "v2.0.0-rc2" - strategy: - fail-fast: false - matrix: ${{ fromJSON(needs.build-test-matrix.outputs.matrix) }} - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-go@v3 - with: - go-version: 1.18 - - name: Log in to the Container registry - uses: docker/login-action@49ed152c8eca782a232dede0303416e8f356c37b - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - name: Run e2e Test - run: | - cd e2e - make e2e-test suite=${{ matrix.suite }} test=${{ matrix.test }} + e2e: + if: ${{ !github.event.pull_request.head.repo.fork && github.actor != 'dependabot[bot]' }} + needs: + - determine-image-tag + - build-e2e # don't attempt any tests unless the e2e code compiles successfully. + uses: ./.github/workflows/e2e-test-workflow-call.yml + secrets: inherit + with: + build-and-push-docker-image: true + chain-image: ghcr.io/cosmos/ibc-go-simd + chain-a-tag: "${{ needs.determine-image-tag.outputs.simd-tag }}" + chain-b-tag: "${{ needs.determine-image-tag.outputs.simd-tag }}" + chain-binary: "simd" + # on regular PRs we won't run interchain account or upgrade tests. + test-exclusions: "TestInterTxTestSuite,TestIncentivizedInterTxTestSuite,TestUpgradeTestSuite" diff --git a/.github/workflows/goimports.yaml b/.github/workflows/goimports.yaml deleted file mode 100644 index b951b17aa1d..00000000000 --- a/.github/workflows/goimports.yaml +++ /dev/null @@ -1,9 +0,0 @@ -name: Goimports -on: pull_request -jobs: - goimports: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: "Go Imports" - run: .github/scripts/go-imports.sh diff --git a/.github/workflows/golangci.yml b/.github/workflows/golangci.yml new file mode 100644 index 00000000000..30d745b89e5 --- /dev/null +++ b/.github/workflows/golangci.yml @@ -0,0 +1,26 @@ +name: golangci-lint +on: + push: + tags: + - v* + branches: + - main + pull_request: +permissions: + contents: read + # Optional: allow read access to pull request. Use with `only-new-issues` option. + # pull-requests: read +jobs: + golangci: + name: lint + runs-on: ubuntu-latest + steps: + - uses: actions/setup-go@v3 + with: + go-version: 1.19 + - uses: actions/checkout@v3 + - name: golangci-lint + uses: golangci/golangci-lint-action@v3.4.0 + with: + version: latest + args: --timeout 5m diff --git a/.github/workflows/link-check.yml b/.github/workflows/link-check.yml index fdf8b184259..6f237e14b79 100644 --- a/.github/workflows/link-check.yml +++ b/.github/workflows/link-check.yml @@ -1,5 +1,5 @@ name: Check Markdown links -on: [push, pull_request] +on: pull_request jobs: markdown-link-check: runs-on: ubuntu-latest @@ -7,4 +7,4 @@ jobs: - uses: actions/checkout@v3 - uses: gaurav-nelson/github-action-markdown-link-check@v1 with: - config-file: '.github/workflows/link-check-config.json' \ No newline at end of file + config-file: '.github/workflows/link-check-config.json' diff --git a/.github/workflows/proto-registry.yml b/.github/workflows/proto-registry.yml new file mode 100644 index 00000000000..dd9e685af42 --- /dev/null +++ b/.github/workflows/proto-registry.yml @@ -0,0 +1,20 @@ +name: Buf-Push +# Protobuf runs buf (https://buf.build/) push updated proto files to https://buf.build/cosmos/ibc +# This workflow is only run when a .proto file has been changed +on: + push: + branches: + - main + paths: + - "proto/**" + +jobs: + push: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: bufbuild/buf-setup-action@v1.14.0 + - uses: bufbuild/buf-push-action@v1 + with: + input: "proto" + buf_token: ${{ secrets.BUF_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 06b11e26d46..f775032c128 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -21,10 +21,10 @@ jobs: - uses: actions/setup-go@v3 with: - go-version: '1.18' + go-version: '1.19' - name: Release - uses: goreleaser/goreleaser-action@v3 + uses: goreleaser/goreleaser-action@v4 if: startsWith(github.ref, 'refs/tags/') with: version: latest @@ -32,23 +32,30 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Log in to the Container registry - uses: docker/login-action@49ed152c8eca782a232dede0303416e8f356c37b - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} + publish-docker-image: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 - - name: Extract metadata (tags, labels) for Docker - id: meta - uses: docker/metadata-action@69f6fc9d46f2f8bf0d5491e4aabe0bb8c6a4678a - with: - images: ${{ env.REGISTRY }}/cosmos/${{ env.IMAGE_NAME }} + - name: Log in to the Container registry + uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} - - name: Build and push Docker image - uses: docker/build-push-action@1cb9d22b932e4832bb29793b7777ec860fc1cde0 - with: - context: . - push: true - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@507c2f2dc502c992ad446e3d7a5dfbe311567a96 + with: + images: ${{ env.REGISTRY }}/cosmos/${{ env.IMAGE_NAME }} + + - name: Build and push Docker image + uses: docker/build-push-action@3b5e8027fcad23fda98b2e3ac259d8d67585f671 + with: + context: . + push: true + tags: ${{ steps.meta.outputs.tags }} + build-args: | + IBC_GO_VERSION=${{ github.ref_name }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 09add80b02b..16f7fb5a421 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -21,7 +21,7 @@ jobs: steps: - uses: actions/setup-go@v3 with: - go-version: 1.18 + go-version: 1.19 - name: Display go version run: go version - name: install tparse @@ -41,23 +41,31 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-go@v3 with: - go-version: 1.18 - - uses: technote-space/get-diff-action@v6.1.0 + go-version: 1.19 + - uses: technote-space/get-diff-action@v6.1.2 id: git_diff with: PATTERNS: | **/**.go go.mod go.sum - - name: Build + - name: Build ibc-go run: GOARCH=${{ matrix.go-arch }} LEDGER_ENABLED=false make build + - name: Build e2e + run: | + cd e2e + test_dirs="$(ls -A tests)" + for td in $test_dirs + do + GOARCH=${{ matrix.go-arch }} go test -c "./tests/${td}" + done split-test-files: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Create a file with all the pkgs - run: go list ./... > pkgs.txt + run: go list ./... | grep -v e2e > pkgs.txt - name: Split pkgs into 4 files run: split -d -n l/4 pkgs.txt pkgs.txt.part. # cache multiple @@ -89,8 +97,8 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-go@v3 with: - go-version: 1.18 - - uses: technote-space/get-diff-action@v6.1.0 + go-version: 1.19 + - uses: technote-space/get-diff-action@v6.1.2 with: PATTERNS: | **/**.go @@ -102,7 +110,7 @@ jobs: if: env.GIT_DIFF - name: test & coverage report creation run: | - cat pkgs.txt.part.${{ matrix.part }} | xargs go test $(go list ./... | grep -v e2e) -race -mod=readonly -timeout 30m -coverprofile=${{ matrix.part }}profile.out -covermode=atomic -tags='ledger test_ledger_mock' + cat pkgs.txt.part.${{ matrix.part }} | xargs go test -mod=readonly -timeout 30m -coverprofile=${{ matrix.part }}profile.out -covermode=atomic -tags='ledger test_ledger_mock' if: env.GIT_DIFF - uses: actions/upload-artifact@v3 with: @@ -114,7 +122,7 @@ jobs: needs: tests steps: - uses: actions/checkout@v3 - - uses: technote-space/get-diff-action@v6.1.0 + - uses: technote-space/get-diff-action@v6.1.2 with: PATTERNS: | **/**.go diff --git a/.gitignore b/.gitignore index e821f35d7e7..1844a2347f6 100644 --- a/.gitignore +++ b/.gitignore @@ -55,3 +55,6 @@ dependency-graph.png *.history +# Go +go.work +go.work.sum \ No newline at end of file diff --git a/.golangci.yml b/.golangci.yml index 34738ccf7e6..76204d5471c 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,53 +1,40 @@ run: - tests: false -# # timeout for analysis, e.g. 30s, 5m, default is 1m -# timeout: 5m + tests: true + # # timeout for analysis, e.g. 30s, 5m, default is 1m + timeout: 5m linters: disable-all: true enable: - - bodyclose - - deadcode - depguard - dogsled - # - errcheck + - exportloopref + - errcheck - goconst - gocritic - - gofmt - - goimports - - golint + - gofumpt - gosec - gosimple - govet - ineffassign - - interfacer - - maligned - misspell - nakedret - - prealloc - - scopelint - staticcheck - - structcheck - stylecheck + - revive - typecheck - unconvert - unused - - unparam - misspell - # - wsl - - nolintlint issues: exclude-rules: + - text: "SA1019:" + linters: + - staticcheck - text: "Use of weak random number generator" linters: - gosec - - text: "comment on exported var" - linters: - - golint - - text: "don't use an underscore in package name" - linters: - - golint - text: "ST1003:" linters: - stylecheck diff --git a/.markdownlint.jsonc b/.markdownlint.jsonc new file mode 100644 index 00000000000..e91db0f891d --- /dev/null +++ b/.markdownlint.jsonc @@ -0,0 +1,10 @@ +{ + "default": true, + "MD013": false, // https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md#md013---line-length + "MD024": false, // https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md#md024---multiple-headings-with-the-same-content + "MD025": false, // https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md#md025---multiple-top-level-headings-in-the-same-document + "MD029": false, // https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md#md029---ordered-list-item-prefix + "MD033": false, // https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md#md033---inline-html + "MD036": false, // https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md#md036---emphasis-used-instead-of-a-heading + "MD041": false // https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md#md041---first-line-in-a-file-should-be-a-top-level-heading +} \ No newline at end of file diff --git a/.markdownlintignore b/.markdownlintignore new file mode 100644 index 00000000000..ad7b2bba0be --- /dev/null +++ b/.markdownlintignore @@ -0,0 +1,3 @@ +vendor +e2e/vendor +docs/node_modules diff --git a/CHANGELOG.md b/CHANGELOG.md index 41c31d738fa..8009b66362c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,7 +28,7 @@ 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/ --> @@ -38,11 +38,263 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### Dependencies -* [\#1615](https://github.com/cosmos/ibc-go/pull/1615) Bump SDK version to v0.45.6 +* [\#2999](https://github.com/cosmos/ibc-go/pull/2999) Bump Cosmos SDK to v0.46.7 and Tendermint to v0.34.24. + +### API Breaking + +* (core) [\#2897](https://github.com/cosmos/ibc-go/pull/2897) Remove legacy migrations required for upgrading from Stargate release line to ibc-go >= v1.x.x. +* (core/02-client) [\#2856](https://github.com/cosmos/ibc-go/pull/2856) Rename `IterateClients` to `IterateClientStates`. The function now takes a prefix argument which may be used for prefix iteration over the client store. +* (light-clients/tendermint)[\#1768](https://github.com/cosmos/ibc-go/pull/1768) Removed `AllowUpdateAfterExpiry`, `AllowUpdateAfterMisbehaviour` booleans as they are deprecated (see ADR026) +* (06-solomachine) [\#1679](https://github.com/cosmos/ibc-go/pull/1679) Remove `types` sub-package from `06-solomachine` lightclient directory. +* (07-tendermint) [\#1677](https://github.com/cosmos/ibc-go/pull/1677) Remove `types` sub-package from `07-tendermint` lightclient directory. +* (06-solomachine) [\#1687](https://github.com/cosmos/ibc-go/pull/1687) Bump `06-solomachine` protobuf version from `v2` to `v3`. +* (06-solomachine) [\#1687](https://github.com/cosmos/ibc-go/pull/1687) Removed `DataType` enum and associated message types from `06-solomachine`. `DataType` has been removed from `SignBytes` and `SignatureAndData` in favour of `path`. +* (02-client) [\#598](https://github.com/cosmos/ibc-go/pull/598) The client state and consensus state return value has been removed from `VerifyUpgradeAndUpdateState`. Light client implementations must update the client state and consensus state after verifying a valid client upgrade. +* (06-solomachine) [\#1100](https://github.com/cosmos/ibc-go/pull/1100) Remove `GetClientID` function from 06-solomachine `Misbehaviour` type. +* (06-solomachine) [\#1100](https://github.com/cosmos/ibc-go/pull/1100) Deprecate `ClientId` field in 06-solomachine `Misbehaviour` type. +* (07-tendermint) [\#1097](https://github.com/cosmos/ibc-go/pull/1097) Remove `GetClientID` function from 07-tendermint `Misbehaviour` type. +* (07-tendermint) [\#1097](https://github.com/cosmos/ibc-go/pull/1097) Deprecate `ClientId` field in 07-tendermint `Misbehaviour` type. +* (modules/core/exported) [\#1107](https://github.com/cosmos/ibc-go/pull/1107) Merging the `Header` and `Misbehaviour` interfaces into a single `ClientMessage` type. +* (06-solomachine)[\#1906](https://github.com/cosmos/ibc-go/pull/1906/files) Removed `AllowUpdateAfterProposal` boolean as it has been deprecated (see 01_concepts of the solo machine spec for more details). +* (07-tendermint) [\#1896](https://github.com/cosmos/ibc-go/pull/1896) Remove error return from `IterateConsensusStateAscending` in `07-tendermint`. +* (apps/27-interchain-accounts) [\#2638](https://github.com/cosmos/ibc-go/pull/2638) Interchain accounts host and controller Keepers now expects a keeper which fulfills the expected `exported.ScopedKeeper` interface for the capability keeper. +* (06-solomachine) [\#2761](https://github.com/cosmos/ibc-go/pull/2761) Removed deprecated `ClientId` field from `Misbehaviour` and `allow_update_after_proposal` field from `ClientState`. +* (apps) [\#3154](https://github.com/cosmos/ibc-go/pull/3154) Remove unused `ProposalContents` function. +* (apps) [#3149](https://github.com/cosmos/ibc-go/pull/3149) Remove legacy interface function `RandomizedParams`, which is no longer used. + +### State Machine Breaking + +* (06-solomachine) [\#2744](https://github.com/cosmos/ibc-go/pull/2744) `Misbehaviour.ValidateBasic()` now only enforces that signature data does not match when the signature paths are different. +* (06-solomachine) [\#2748](https://github.com/cosmos/ibc-go/pull/2748) Adding sentinel value for header path in 06-solomachine. +* (apps/29-fee) [\#2942](https://github.com/cosmos/ibc-go/pull/2942) Check `x/bank` send enabled before escrowing fees. + +### Improvements + +* (tests) [\#3138](https://github.com/cosmos/ibc-go/pull/3138) Support benchmarks and fuzz tests through `testing.TB`. +* (core) [\#3082](https://github.com/cosmos/ibc-go/pull/3082) Add `HasConnection` and `HasChannel` methods. +* (tests) [\#2926](https://github.com/cosmos/ibc-go/pull/2926) Lint tests +* (apps/transfer) [\#2643](https://github.com/cosmos/ibc-go/pull/2643) Add amount, denom, and memo to transfer event emission. +* (core) [\#2746](https://github.com/cosmos/ibc-go/pull/2746) Allow proof height to be zero for all core IBC `sdk.Msg` types that contain proofs. +* (light-clients/06-solomachine) [\#2746](https://github.com/cosmos/ibc-go/pull/2746) Discard proofHeight for solo machines and use the solo machine sequence instead. +* (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. +* (modules/core/02-client) [\#1188](https://github.com/cosmos/ibc-go/pull/1188/files) Routing `MsgSubmitMisbehaviour` to `UpdateClient` keeper function. Deprecating `SubmitMisbehaviour` endpoint. +* (modules/core/02-client) [\#1208](https://github.com/cosmos/ibc-go/pull/1208) Replace `CheckHeaderAndUpdateState` usage in 02-client with calls to `VerifyClientMessage`, `CheckForMisbehaviour`, `UpdateStateOnMisbehaviour` and `UpdateState`. +* (modules/light-clients/09-localhost) [\#1187](https://github.com/cosmos/ibc-go/pull/1187/) Removing localhost light client implementation as it is not functional. An upgrade handler is provided in `modules/migrations/v5` to prune `09-localhost` clients and consensus states from the store. +* [\#1186](https://github.com/cosmos/ibc-go/pull/1186/files) Removing `GetRoot` function from ConsensusState interface in `02-client`. `GetRoot` is unused by core IBC. +* (modules/core/02-client) [\#1196](https://github.com/cosmos/ibc-go/pull/1196) Adding VerifyClientMessage to ClientState interface. +* (modules/core/02-client) [\#1198](https://github.com/cosmos/ibc-go/pull/1198) Adding UpdateStateOnMisbehaviour to ClientState interface. +* (modules/core/02-client) [\#1170](https://github.com/cosmos/ibc-go/pull/1170) Updating `ClientUpdateProposal` to set client state in lightclient implementations `CheckSubstituteAndUpdateState` methods. +* (modules/core/02-client) [\#1197](https://github.com/cosmos/ibc-go/pull/1197) Adding `CheckForMisbehaviour` to `ClientState` interface. +* (modules/core/02-client) [\#1195](https://github.com/cosmos/ibc-go/pull/1210) Removing `CheckHeaderAndUpdateState` from `ClientState` interface & associated light client implementations. +* (modules/core/02-client) [\#1189](https://github.com/cosmos/ibc-go/pull/1212) Removing `CheckMisbehaviourAndUpdateState` from `ClientState` interface & associated light client implementations. +* (modules/core/exported) [\#1206](https://github.com/cosmos/ibc-go/pull/1206) Adding new method `UpdateState` to `ClientState` interface. +* (modules/core/02-client) [\#1741](https://github.com/cosmos/ibc-go/pull/1741) Emitting a new `upgrade_chain` event upon setting upgrade consensus state. +* (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. +* (02-client/cli) [\#897](https://github.com/cosmos/ibc-go/pull/897) Remove `GetClientID()` from `Misbehaviour` interface. Submit client misbehaviour cli command requires an explicit client id now. +* (06-solomachine) [\#1972](https://github.com/cosmos/ibc-go/pull/1972) Solo machine implementation of `ZeroCustomFields` fn now panics as the fn is only used for upgrades which solo machine does not support. +* (light-clients/06-solomachine) Moving `verifyMisbehaviour` function from update.go to misbehaviour_handle.go. +* [\#2434](https://github.com/cosmos/ibc-go/pull/2478) Removed all `TypeMsg` constants +* (modules/core/exported) [#1689](https://github.com/cosmos/ibc-go/pull/2539) Removing `GetVersions` from `ConnectionI` interface. +* (core/02-connection) [#2419](https://github.com/cosmos/ibc-go/pull/2419) Add optional proof data to proto definitions of `MsgConnectionOpenTry` and `MsgConnectionOpenAck` for host state machines that are unable to introspect their own consensus state. +* (modules/light-clients/07-tendermint) [#2007](https://github.com/cosmos/ibc-go/pull/3046) Moved non-verification misbehaviour checks to `CheckForMisbehaviour` + +### Features + +* (core/02-client) [\#2824](https://github.com/cosmos/ibc-go/pull/2824) Add genesis migrations for v6 to v7. The migration migrates the solo machine client state definition, removes all solo machine consensus states and removes the localhost client. +* (core/24-host) [\#2856](https://github.com/cosmos/ibc-go/pull/2856) Add `PrefixedClientStorePath` and `PrefixedClientStoreKey` functions to 24-host +* (core/02-client) [\#2819](https://github.com/cosmos/ibc-go/pull/2819) Add automatic in-place store migrations to remove the localhost client and migrate existing solo machine definitions. +* (light-clients/06-solomachine) [\#2826](https://github.com/cosmos/ibc-go/pull/2826) Add `AppModuleBasic` for the 06-solomachine client and remove solo machine type registration from core IBC. Chains must register the `AppModuleBasic` of light clients. +* (light-clients/07-tendermint) [\#2825](https://github.com/cosmos/ibc-go/pull/2825) Add `AppModuleBasic` for the 07-tendermint client and remove tendermint type registration from core IBC. Chains must register the `AppModuleBasic` of light clients. +* (light-clients/07-tendermint) [\#2800](https://github.com/cosmos/ibc-go/pull/2800) Add optional in-place store migration function to prune all expired tendermint consensus states. +* (core/24-host) [\#2820](https://github.com/cosmos/ibc-go/pull/2820) Add `MustParseClientStatePath` which parses the clientID from a client state key path. +* (testing/simapp) [\#2842](https://github.com/cosmos/ibc-go/pull/2842) Adding the new upgrade handler for v6 -> v7 to simapp which prunes expired Tendermint consensus states. +* (testing) [\#2829](https://github.com/cosmos/ibc-go/pull/2829) Add `AssertEvents` which asserts events against expected event map. + +### Bug Fixes + +* (light-clients/solomachine) [#1839](https://github.com/cosmos/ibc-go/issues/1839) Fixed usage of the new diversifier in validation of changing diversifiers for the solo machine. The current diversifier must sign over the new diversifier. +* (light-clients/07-tendermint) [\#1674](https://github.com/cosmos/ibc-go/pull/1674) Submitted ClientState is zeroed out before checking the proof in order to prevent the proposal from containing information governance is not actually voting on. +* (modules/core/02-client)[\#1676](https://github.com/cosmos/ibc-go/pull/1676) ClientState must be zeroed out for `UpgradeProposals` to pass validation. This prevents a proposal containing information governance is not actually voting on. +* (core/02-client) [\#2510](https://github.com/cosmos/ibc-go/pull/2510) Fix client ID validation regex to conform closer to spec. + +## [v6.0.0](https://github.com/cosmos/ibc-go/releases/tag/v4.0.0) - 2022-12-09 + +### Dependencies + +* [\#2868](https://github.com/cosmos/ibc-go/pull/2868) Bump ICS 23 to v0.9.0. +* [\#2458](https://github.com/cosmos/ibc-go/pull/2458) Bump Cosmos SDK to v0.46.2 +* [\#2784](https://github.com/cosmos/ibc-go/pull/2784) Bump Cosmos SDK to v0.46.6 and Tendermint to v0.34.23. + +### API Breaking + +* (apps/27-interchain-accounts) [\#2607](https://github.com/cosmos/ibc-go/pull/2607) `SerializeCosmosTx` now takes in a `[]proto.Message` instead of `[]sdk.Msg`. +* (apps/transfer) [\#2446](https://github.com/cosmos/ibc-go/pull/2446) Remove `SendTransfer` function in favor of a private `sendTransfer` function. All IBC transfers must be initiated with `MsgTransfer`. +* (apps/29-fee) [\#2395](https://github.com/cosmos/ibc-go/pull/2395) Remove param space from ics29 NewKeeper function. The field was unused. +* (apps/27-interchain-accounts) [\#2133](https://github.com/cosmos/ibc-go/pull/2133) Generates genesis protos in a separate directory to avoid circular import errors. The protobuf package name has changed for the genesis types. +* (apps/27-interchain-accounts) [\#2638](https://github.com/cosmos/ibc-go/pull/2638) Interchain accounts host and controller Keepers now expects a keeper which fulfills the expected `exported.ScopedKeeper` interface for the capability keeper. +* (transfer) [\#2638](https://github.com/cosmos/ibc-go/pull/2638) Transfer Keeper now expects a keeper which fulfills the expected `exported.ScopedKeeper` interface for the capability keeper. +* (05-port) [\#2638](https://github.com/cosmos/ibc-go/pull/2638) Port Keeper now expects a keeper which fulfills the expected `exported.ScopedKeeper` interface for the capability keeper. +* (04-channel) [\#2638](https://github.com/cosmos/ibc-go/pull/2638) Channel Keeper now expects a keeper which fulfills the expected `exported.ScopedKeeper` interface for the capability keeper. +* (core/04-channel)[\#1703](https://github.com/cosmos/ibc-go/pull/1703) Update `SendPacket` API to take in necessary arguments and construct rest of packet rather than taking in entire packet. The generated packet sequence is returned by the `SendPacket` function. +* (modules/apps/27-interchain-accounts) [\#2433](https://github.com/cosmos/ibc-go/pull/2450) Renamed icatypes.PortPrefix to icatypes.ControllerPortPrefix & icatypes.PortID to icatypes.HostPortID +* (testing) [\#2567](https://github.com/cosmos/ibc-go/pull/2567) Modify `SendPacket` API of `Endpoint` to match the API of `SendPacket` in 04-channel. + +### 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`. +* (light-clients/07-tendermint) [\#2555](https://github.com/cosmos/ibc-go/pull/2555) Forbid negative values for `TrustingPeriod`, `UnbondingPeriod` and `MaxClockDrift` (as specified in ICS-07). + +### Improvements + +* (apps/27-interchain-accounts) [\#2134](https://github.com/cosmos/ibc-go/pull/2134) Adding upgrade handler to ICS27 `controller` submodule for migration of channel capabilities. This upgrade handler migrates ownership of channel capabilities from the underlying application to the ICS27 `controller` submodule. +* (apps/27-interchain-accounts) [\#2102](https://github.com/cosmos/ibc-go/pull/2102) ICS27 controller middleware now supports a nil underlying application. This allows chains to make use of interchain accounts with existing auth mechanisms such as x/group and x/gov. +* (apps/27-interchain-accounts) [\#2157](https://github.com/cosmos/ibc-go/pull/2157) Adding `IsMiddlewareEnabled` functionality to enforce calls to ICS27 msg server to *not* route to the underlying application. +* (apps/27-interchain-accounts) [\#2146](https://github.com/cosmos/ibc-go/pull/2146) ICS27 controller now claims the channel capability passed via ibc core, and passes `nil` to the underlying app callback. The channel capability arg in `SendTx` is now ignored and looked up internally. +* (apps/27-interchain-accounts) [\#2177](https://github.com/cosmos/ibc-go/pull/2177) Adding `IsMiddlewareEnabled` flag to interchain accounts `ActiveChannel` genesis type. +* (apps/27-interchain-accounts) [\#2140](https://github.com/cosmos/ibc-go/pull/2140) Adding migration handler to ICS27 `controller` submodule to assert ownership of channel capabilities and set middleware enabled flag for existing channels. The ICS27 module consensus version has been bumped from 1 to 2. +* (core/04-channel) [\#2304](https://github.com/cosmos/ibc-go/pull/2304) Adding `GetAllChannelsWithPortPrefix` function which filters channels based on a provided port prefix. +* (apps/27-interchain-accounts) [\#2248](https://github.com/cosmos/ibc-go/pull/2248) Adding call to underlying app in `OnChanCloseConfirm` callback of the controller submodule and adding relevant unit tests. +* (apps/27-interchain-accounts) [\#2251](https://github.com/cosmos/ibc-go/pull/2251) Adding `msgServer` struct to controller submodule that embeds the `Keeper` struct. +* (apps/27-interchain-accounts) [\#2290](https://github.com/cosmos/ibc-go/pull/2290) Changed `DefaultParams` function in `host` submodule to allow all messages by default. Defined a constant named `AllowAllHostMsgs` for `host` module to keep wildcard "*" string which allows all messages. +* (apps/27-interchain-accounts) [\#2297](https://github.com/cosmos/ibc-go/pull/2297) Adding cli command to generate ICS27 packet data. +* (modules/core/keeper) [\#1728](https://github.com/cosmos/ibc-go/pull/2399) Updated channel callback errors to include portID & channelID for better identification of errors. +* (testing) [\#2657](https://github.com/cosmos/ibc-go/pull/2657) Carry `ProposerAddress` through commited blocks. Allow `DefaultGenTxGas` to be modified. +* (core/03-connection) [\#2745](https://github.com/cosmos/ibc-go/pull/2745) Adding `ConnectionParams` grpc query and CLI to 03-connection. +* (apps/29-fee) [\#2786](https://github.com/cosmos/ibc-go/pull/2786) Save gas by checking key existence with `KVStore`'s `Has` method. + +### Features + +* (apps/27-interchain-accounts) [\#2147](https://github.com/cosmos/ibc-go/pull/2147) Adding a `SubmitTx` gRPC endpoint for the ICS27 Controller module which allows owners of interchain accounts to submit transactions. This replaces the previously existing need for authentication modules to implement this standard functionality. +* (testing/simapp) [\#2190](https://github.com/cosmos/ibc-go/pull/2190) Adding the new `x/group` cosmos-sdk module to simapp. +* (apps/transfer) [\#2595](https://github.com/cosmos/ibc-go/pull/2595) Adding optional memo field to `FungibleTokenPacketData` and `MsgTransfer`. + +### Bug Fixes + +* (modules/core/keeper) [\#2403](https://github.com/cosmos/ibc-go/pull/2403) Added a function in keeper to cater for blank pointers. +* (apps/transfer) [\#2679](https://github.com/cosmos/ibc-go/pull/2679) Check `x/bank` send enabled. +* (modules/core/keeper) [\#2745](https://github.com/cosmos/ibc-go/pull/2745) Fix request wiring for `UpgradedConsensusState` in core query server. + +## [v5.1.0](https://github.com/cosmos/ibc-go/releases/tag/v5.1.0) - 2022-11-09 + +### Dependencies + +* [\#2647](https://github.com/cosmos/ibc-go/pull/2647) Bump Cosmos SDK to v0.46.4 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`. + +### Improvements + +* (testing) [\#2657](https://github.com/cosmos/ibc-go/pull/2657) Carry `ProposerAddress` through committed blocks. Allow `DefaultGenTxGas` to be modified. + +### 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. + +## [v5.0.1](https://github.com/cosmos/ibc-go/releases/tag/v5.0.1) - 2022-10-27 + +### Dependencies + +* [\#2623](https://github.com/cosmos/ibc-go/pull/2623) Bump SDK version to v0.46.3 and Tendermint version to v0.34.22. + +## [v5.0.0](https://github.com/cosmos/ibc-go/releases/tag/v5.0.0) - 2022-09-28 + +### Dependencies + +* [\#1653](https://github.com/cosmos/ibc-go/pull/1653) Bump SDK version to v0.46 +* [\#2124](https://github.com/cosmos/ibc-go/pull/2124) Bump SDK version to v0.46.1 + +### API Breaking + +* (testing)[\#2028](https://github.com/cosmos/ibc-go/pull/2028) New interface `ibctestingtypes.StakingKeeper` added and set for the testing app `StakingKeeper` setup. +* (core/04-channel) [\#1418](https://github.com/cosmos/ibc-go/pull/1418) `NewPacketId` has been renamed to `NewPacketID` to comply with go linting rules. +* (core/ante) [\#1418](https://github.com/cosmos/ibc-go/pull/1418) `AnteDecorator` has been renamed to `RedundancyDecorator` to comply with go linting rules and to give more clarity to the purpose of the Decorator. +* (core/ante) [\#1820](https://github.com/cosmos/ibc-go/pull/1418) `RedundancyDecorator` has been renamed to `RedundantRelayDecorator` to make the name for explicit. +* (testing) [\#1418](https://github.com/cosmos/ibc-go/pull/1418) `MockIBCApp` has been renamed to `IBCApp` and `MockEmptyAcknowledgement` has been renamed to `EmptyAcknowledgement` to comply with go linting rules +* (apps/27-interchain-accounts) [\#2058](https://github.com/cosmos/ibc-go/pull/2058) Added `MessageRouter` interface and replaced `*baseapp.MsgServiceRouter` with it. The controller and host keepers of apps/27-interchain-accounts have been updated to use it. +* (apps/27-interchain-accounts)[\#2302](https://github.com/cosmos/ibc-go/pull/2302) Handle unwrapping of channel version in interchain accounts channel reopening handshake flow. The `host` submodule `Keeper` now requires an `ICS4Wrapper` similarly to the `controller` submodule. + +### Improvements + +* (27-interchain-accounts) [\#1352](https://github.com/cosmos/ibc-go/issues/1352) Add support for Cosmos-SDK simulation to ics27 module. +* (linting) [\#1418](https://github.com/cosmos/ibc-go/pull/1418) Fix linting errors, resulting compatiblity with go1.18 linting style, golangci-lint 1.46.2 and the revivie linter. This caused breaking changes in core/04-channel, core/ante, and the testing library. + +### 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. +* (makefile) [\#1785](https://github.com/cosmos/ibc-go/pull/1785) Fetch the correct versions of protocol buffers dependencies from tendermint, cosmos-sdk, and ics23. +* (modules/core/04-channel)[\#1919](https://github.com/cosmos/ibc-go/pull/1919) Fixed formatting of sequence for packet "acknowledgement written" logs. + +## [v4.2.0](https://github.com/cosmos/ibc-go/releases/tag/v4.2.0) - 2022-11-07 + +### Dependencies + +* [\#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.1](https://github.com/cosmos/ibc-go/releases/tag/v4.1.1) - 2022-10-27 + +### Dependencies + +* [\#2624](https://github.com/cosmos/ibc-go/pull/2624) Bump SDK version to v0.45.10 and Tendermint to v0.34.22. + +## [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.1](https://github.com/cosmos/ibc-go/releases/tag/v4.0.1) - 2022-09-15 + +### Dependencies + +* [\#2287](https://github.com/cosmos/ibc-go/pull/2287) Bump SDK version to v0.45.8 and Tendermint to v0.34.21. + +## [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. @@ -57,42 +309,125 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### 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 * (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. +* (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 +## [v3.4.0](https://github.com/cosmos/ibc-go/releases/tag/v3.4.0) - 2022-11-07 ### Dependencies -* [\#1300](https://github.com/cosmos/ibc-go/pull/1300) Bump SDK version to v0.45.4 +* [\#2589](https://github.com/cosmos/ibc-go/pull/2589) Bump SDK version to v0.45.10 and Tendermint to v0.34.22. -### API Breaking +### 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. + +## [v3.3.1](https://github.com/cosmos/ibc-go/releases/tag/v3.3.1) - 2022-10-27 + +### Dependencies + +* [\#2621](https://github.com/cosmos/ibc-go/pull/2621) Bump SDK version to v0.45.10 and Tendermint to v0.34.22. + +## [v3.3.0](https://github.com/cosmos/ibc-go/releases/tag/v3.3.0) - 2022-09-20 + +### Dependencies + +* [\#2286](https://github.com/cosmos/ibc-go/pull/2286) 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. + +## [v3.2.1](https://github.com/cosmos/ibc-go/releases/tag/v3.2.1) - 2022-09-15 + +### Dependencies + +* [\#2285](https://github.com/cosmos/ibc-go/pull/2285) Bump SDK version to v0.45.8 and Tendermint to v0.34.21. + +## [v3.2.0](https://github.com/cosmos/ibc-go/releases/tag/v3.2.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 ### 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 + +* (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. +* (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. +* (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 + +### Bug Fixes + +* (modules/core/04-channel)[\#1919](https://github.com/cosmos/ibc-go/pull/1919) Fixed formatting of sequence for packet "acknowledgement written" logs. + +## [v3.1.1](https://github.com/cosmos/ibc-go/releases/tag/v3.1.1) - 2022-08-02 + +### Dependencies + +* [\#1525](https://github.com/cosmos/ibc-go/pull/1525) Bump SDK version to v0.45.5 + +### Improvements + +* (core/02-client) [\#1570](https://github.com/cosmos/ibc-go/pull/1570) Emitting an event when handling an upgrade client proposal. +* (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 + +## [v3.1.0](https://github.com/cosmos/ibc-go/releases/tag/v3.1.0) - 2022-06-14 + +### Dependencies + +* [\#1300](https://github.com/cosmos/ibc-go/pull/1300) Bump SDK version to v0.45.4 + ### 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`. @@ -106,7 +441,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 @@ -114,7 +449,14 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (modules/core/04-channel) [\#1130](https://github.com/cosmos/ibc-go/pull/1130) Call `packet.GetSequence()` rather than passing func in `WriteAcknowledgement` log output * (apps/transfer) [\#1451](https://github.com/cosmos/ibc-go/pull/1451) Fixing the support for base denoms that contain slashes. -## [v3.0.1](https://github.com/cosmos/ibc-go/releases/tag/v3.0.1) - 2022-04-16 +## [v3.0.2](https://github.com/cosmos/ibc-go/releases/tag/v3.0.2) - 2022-08-02 + +### Improvements + +* (core/02-client) [\#1570](https://github.com/cosmos/ibc-go/pull/1570) Emitting an event when handling an upgrade client proposal. +* (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 + +## [v3.0.1](https://github.com/cosmos/ibc-go/releases/tag/v3.0.1) - 2022-06-14 ### Dependencies @@ -154,7 +496,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. @@ -165,6 +507,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### Improvements +* (client) [\#888](https://github.com/cosmos/ibc-go/pull/888) Add `GetTimestampAtHeight` to `ClientState` * (interchain-accounts) [\#1037](https://github.com/cosmos/ibc-go/pull/1037) Add a function `InitModule` to the interchain accounts `AppModule`. This function should be called within the upgrade handler when adding the interchain accounts module to a chain. It should be called in place of InitGenesis (set the consensus version in the version map). * (testing) [\#942](https://github.com/cosmos/ibc-go/pull/942) `NewTestChain` will create 4 validators in validator set by default. A new constructor function `NewTestChainWithValSet` is provided for test writers who want custom control over the validator set of test chains. * (testing) [\#904](https://github.com/cosmos/ibc-go/pull/904) Add `ParsePacketFromEvents` function to the testing package. Useful when sending/relaying packets via the testing package. @@ -174,14 +517,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. +* (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. * (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 @@ -192,7 +535,72 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (client) [\#941](https://github.com/cosmos/ibc-go/pull/941) Classify client states without consensus states as expired * (channel) [\#995](https://github.com/cosmos/ibc-go/pull/995) Call `packet.GetSequence()` rather than passing func in `AcknowledgePacket` log output -## [v2.3.0](https://github.com/cosmos/ibc-go/releases/tag/v2.3.0) - 2022-04-16 +## [v2.5.0](https://github.com/cosmos/ibc-go/releases/tag/v2.5.0) - 2022-11-07 + +### Dependencies + +* [\#2578](https://github.com/cosmos/ibc-go/pull/2578) 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). +* (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. + +## [v2.4.2](https://github.com/cosmos/ibc-go/releases/tag/v2.4.2) - 2022-10-27 + +### Dependencies + +* [\#2622](https://github.com/cosmos/ibc-go/pull/2622) Bump SDK version to v0.45.10 and Tendermint to v0.34.22. + +## [v2.4.1](https://github.com/cosmos/ibc-go/releases/tag/v2.4.1) - 2022-09-15 + +### Dependencies + +* [\#2284](https://github.com/cosmos/ibc-go/pull/2284) Bump SDK version to v0.45.8 and Tendermint to v0.34.21. + +## [v2.4.0](https://github.com/cosmos/ibc-go/releases/tag/v2.4.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 + +### 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. + +### Improvements + +* (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/02-client) [\#1570](https://github.com/cosmos/ibc-go/pull/1570) Emitting an event when handling an upgrade client proposal. +* (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. +* (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 + +### Bug Fixes + +* (modules/core/04-channel)[\#1919](https://github.com/cosmos/ibc-go/pull/1919) Fixed formatting of sequence for packet "acknowledgement written" logs. + +## [v2.3.1](https://github.com/cosmos/ibc-go/releases/tag/v2.3.1) - 2022-08-02 + +### Dependencies + +* [\#1525](https://github.com/cosmos/ibc-go/pull/1525) Bump SDK version to v0.45.5 + +### Improvements + +* (core/02-client) [\#1570](https://github.com/cosmos/ibc-go/pull/1570) Emitting an event when handling an upgrade client proposal. +* (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 + +## [v2.3.0](https://github.com/cosmos/ibc-go/releases/tag/v2.3.0) - 2022-06-14 ### Dependencies @@ -218,7 +626,14 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (modules/core/04-channel) [\#1130](https://github.com/cosmos/ibc-go/pull/1130) Call `packet.GetSequence()` rather than passing func in `WriteAcknowledgement` log output * (apps/transfer) [\#1451](https://github.com/cosmos/ibc-go/pull/1451) Fixing the support for base denoms that contain slashes. -## [v2.2.1](https://github.com/cosmos/ibc-go/releases/tag/v2.2.1) - 2022-04-16 +## [v2.2.2](https://github.com/cosmos/ibc-go/releases/tag/v2.2.2) - 2022-08-02 + +### Improvements + +* (core/02-client) [\#1570](https://github.com/cosmos/ibc-go/pull/1570) Emitting an event when handling an upgrade client proposal. +* (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 + +## [v2.2.1](https://github.com/cosmos/ibc-go/releases/tag/v2.2.1) - 2022-06-14 ### Improvements @@ -238,7 +653,14 @@ Ref: https://keepachangelog.com/en/1.0.0/ * [\#851](https://github.com/cosmos/ibc-go/pull/851) Bump SDK version to v0.45.1 -## [v2.1.1](https://github.com/cosmos/ibc-go/releases/tag/v2.1.1) - 2022-04-16 +## [v2.1.2](https://github.com/cosmos/ibc-go/releases/tag/v2.1.2) - 2022-08-02 + +### Improvements + +* (core/02-client) [\#1570](https://github.com/cosmos/ibc-go/pull/1570) Emitting an event when handling an upgrade client proposal. +* (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 + +## [v2.1.1](https://github.com/cosmos/ibc-go/releases/tag/v2.1.1) - 2022-06-14 ### Dependencies @@ -317,7 +739,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 @@ -342,7 +764,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 @@ -507,7 +929,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 @@ -538,8 +960,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 @@ -548,19 +970,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..c471c6cb339 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,252 +1,65 @@ -# Contributing - -- [Contributing](#contributing) - - [Architecture Decision Records (ADR)](#architecture-decision-records-adr) - - [Pull Requests](#pull-requests) - - [Process for reviewing PRs](#process-for-reviewing-prs) - - [Updating Documentation](#updating-documentation) - - [Forking](#forking) - - [Dependencies](#dependencies) - - [Protobuf](#protobuf) - - [Testing](#testing) - - [Branching Model and Release](#branching-model-and-release) - - [PR Targeting](#pr-targeting) - - [Development Procedure](#development-procedure) - - [Pull Merge Procedure](#pull-merge-procedure) - - [Release Procedure](#release-procedure) - - [Point Release Procedure](#point-release-procedure) - -Thank you for considering making contributions to ibc-go! - -Contributing to this repo can mean many things such as participating in -discussion or proposing code changes. To ensure a smooth workflow for all -contributors, the general procedure for contributing has been established: - -1. Either [open](https://github.com/cosmos/ibc-go/issues/new/choose) or - [find](https://github.com/cosmos/ibc-go/issues) an issue you'd like to help with -2. Participate in thoughtful discussion on that issue -3. If you would like to contribute: - 1. If the issue is a proposal, ensure that the proposal has been accepted - 2. Ensure that nobody else has already begun working on this issue. If they have, - make sure to contact them to collaborate - 3. If nobody has been assigned for the issue and you would like to work on it, - make a comment on the issue to inform the community of your intentions - to begin work - 4. Follow standard Github best practices: fork the repo, branch from the - HEAD of `main`, make some commits, and submit a PR to `main` - - For core developers working within the ibc-go repo, to ensure a clear - ownership of branches, branches must be named with the convention - `{moniker}/{issue#}-branch-name` - 5. Be sure to submit the PR in `Draft` mode submit your PR early, even if - it's incomplete as this indicates to the community you're working on - something and allows them to provide comments early in the development process - 6. When the code is complete it can be marked `Ready for Review` - 7. Be sure to include a relevant change log entry in the `Unreleased` section - of `CHANGELOG.md` (see file for log format) +# Contributing to ibc-go -Note that for very small or blatantly obvious problems (such as typos) it is -not required to an open issue to submit a PR, but be aware that for more complex -problems/features, if a PR is opened before an adequate design discussion has -taken place in a github issue, that PR runs a high likelihood of being rejected. +Thank you for considering making contributions to ibc-go! 🎉👍 -Other notes: +## Code of conduct -- Looking for a good place to start contributing? How about checking out some - [good first issues](https://github.com/cosmos/ibc-go/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22) -- Please make sure to run `make format` before every commit - the easiest way - to do this is have your editor run it for you upon saving a file. Additionally - please ensure that your code is lint compliant by running `golangci-lint run`. +This project and everyone participating in it is governed by ibc-go's [code of conduct](./CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code -## Architecture Decision Records (ADR) +## How can I contribute? -When proposing an architecture decision for the ibc-go, please create an [ADR](./docs/architecture/README.md) -so further discussions can be made. We are following this process so all involved parties are in -agreement before any party begins coding the proposed implementation. If you would like to see some examples -of how these are written refer to [Cosmos SDK ADRs](https://github.com/cosmos/cosmos-sdk/tree/master/docs/architecture) +Contributing to this repository can mean many things such as participating in discussions or proposing code changes. To ensure a smooth workflow for all contributors, the general procedure for contributing has been established: -## Pull Requests +### Reporting bugs -To accommodate review process we suggest that PRs are categorically broken up. -Ideally each PR addresses only a single issue. Additionally, as much as possible -code refactoring and cleanup should be submitted as a separate PRs from bugfixes/feature-additions. +If you find that something is not working as expected, please open an issue using the [bug report template](https://github.com/cosmos/ibc-go/blob/main/.github/ISSUE_TEMPLATE/bug-report.md) and provide as much information possible: how can the bug be reproduced? What's the expected behavior? What version is affected? -### Process for reviewing PRs +### Proposing improvements or new features -All PRs require an approval from at least one CODEOWNER before merge. PRs which cause signficant changes require two approvals from CODEOWNERS. When reviewing PRs please use the following review explanations: +New features or improvements should be written in an issue using the [new feature template](https://github.com/cosmos/ibc-go/blob/main/.github/ISSUE_TEMPLATE/feature-request.md). Please include in the issue as many details as possible: what use case(s) would this new feature or improvement enable? Why are those use cases important or helpful? what user group would benefit? The team will evaluate and engage with you in a discussion of the proposal, which could have different outcomes: -- `LGTM` without an explicit approval means that the changes look good, but you haven't pulled down the code, run tests locally and thoroughly reviewed it. -- `Approval` through the GH UI means that you understand the code, documentation/spec is updated in the right places, you have pulled down and tested the code locally. In addition: - - You must also think through anything which ought to be included but is not - - You must think through whether any added code could be partially combined (DRYed) with existing code - - You must think through any potential security issues or incentive-compatibility flaws introduced by the changes - - Naming must be consistent with conventions and the rest of the codebase - - Code must live in a reasonable location, considering dependency structures (e.g. not importing testing modules in production code, or including example code modules in production code). - - if you approve of the PR, you are responsible for fixing any of the issues mentioned here and more -- 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. +- the core ibc-go team deciding to implement this feature and adding it to their planning, +- agreeing to support external contributors to implement it with the goal of merging it eventually in ibc-go, +- discarding the suggestion if deemed not aligned with the objectives of ibc-go; +- or proposing (in the case of applications or light clients) to be developed and maintained in a separate repository. -### Updating Documentation +Please check out also our [Request For Maintainership](./MAINTAINERSHIP.md) process, which contains information relevant to this. -If you open a PR on ibc-go, it is mandatory to update the relevant documentation in /docs. +### Architecture Decision Records (ADR) -## Forking +When proposing an architecture decision for the ibc-go, please create an [ADR](./docs/architecture/README.md) so further discussions can be made. We are following this process so all involved parties are in agreement before any party begins coding the proposed implementation. Please use the [ADR template](./docs/architecture/adr-template.md) to scaffold any new ADR. If you would like to see some examples of how these are written refer to ibc-go's [ADRs](./docs/architecture/). Solidified designs that will be implemented in ibc-go (and do not have a spec). They should document the architecture that will be built. Most design feedback should be gathered before the initial draft of the ADR. ADR's can/should be written for any design decisions we make which may be changed at some point in the future. -Please note that Go requires code to live under absolute paths, which complicates forking. -While my fork lives at `https://github.com/colin-axner/ibc-go`, -the code should never exist at `$GOPATH/src/github.com/colin-axner/ibc-go`. -Instead, we use `git remote` to add the fork as a new remote for the original repo, -`$GOPATH/src/github.com/cosmos/ibc-go`, and do all the work there. +### Participating in discussions -For instance, to create a fork and work on a branch of it, I would: +New features or improvements are sometimes also debated in [discussions](https://github.com/cosmos/ibc-go/discussions). Sharing feedback or ideas there is very helpful for us. high level discussions that may get a lot of comments on a variety of different aspects, design aspects still being considered. -- Create the fork on github, using the fork button. -- Go to the original repo checked out locally (i.e. `$GOPATH/src/github.com/cosmos/ibc-go`) -- `git remote add fork git@github.com:colin-axner/ibc-go.git` +### Submitting pull requests -Now `fork` refers to my fork and `origin` refers to the ibc-go version. -So I can `git push -u fork main` to update my fork, and make pull requests to ibc-go from there. -Of course, replace `colin-axner` with your git handle. +Unless you feel confident your change will be accepted (trivial bug fixes, code cleanup, etc) you should first create an issue to discuss your change with us. This lets us all discuss the design and proposed implementation of your change, which helps ensure your time is well spent and that your contribution will be accepted. -To pull in updates from the origin repo, run +Looking for a good place to start contributing? The issue tracker is always the first place to go. Issues are triaged to categorize them: -- `git fetch origin` -- `git rebase origin/main` (or whatever branch you want) +- Check out some [`good first issue`s](https://github.com/cosmos/ibc-go/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22). These are issues whose scope of work should be pretty clearly specified and they are best suited for developers new to ibc-go (i.e. no deep knowledge of Cosmos SDK or ibc-go is required). For example, some of these issues may involve improving the logging, emitting new events or removing unused code. +- Or pick up a [`help wanted`](https://github.com/cosmos/ibc-go/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22) issue. These issues should be a bit more involved than the good first issues and the developer working on them would benefit from some familiarity already with the codebase. These types of issues may involve adding new (or extending the functionality of existing) gRPC endpoints, bumping the version of Cosmos SDK or Tendermint or fixing bugs. -Please don't make Pull Requests from `main`. +If you would like to contribute, follow this process: -## Dependencies +1. If the issue is a proposal, ensure that the proposal has been accepted. +2. Ensure that nobody else has already begun working on this issue. If they have, make sure to contact them to collaborate. +3. If nobody has been assigned for the issue and you would like to work on it, comment on the issue to inform the community of your intentions to begin work. Then we will be able to assign the issue to you, making it visible for others that this issue is being tackled. If you end up not creating a pull request for this issue, please comment on the issue as well, so that it can be assigned to somebody else. +4. Follow standard GitHub best practices: fork the repo, branch from the HEAD of `main`, make some commits, and submit a PR to `main`. For core developers working within the ibc-go repo, branches must be named with the convention `{moniker}/{issue#}-branch-name` to ensure a clear ownership of branches. +5. Feel free to submit the pull request in `Draft` mode, even if the work is not complete, as this indicates to the community you are working on something and allows them to provide comments early in the development process. +6. When the code is complete it can be marked `Ready for Review`. +7. Be sure to include a relevant changelog entry in the [Commit Message / Changelog Entry section of pull request description](https://github.com/cosmos/ibc-go/blob/main/.github/PULL_REQUEST_TEMPLATE.md#commit-message--changelog-entry) so that we can add changelog entry when merging the pull request. Please follow the [Conventional Commits specification](https://www.conventionalcommits.org/en/v1.0.0/) and use one of the commit types mentioned in the [Commit messages section of the pull request guidelines](./docs/dev/pull-requests.md#commit-messages). -We use [Go 1.14 Modules](https://github.com/golang/go/wiki/Modules) to manage -dependency versions. +Please make sure to check out our [Pull request guidelines](./docs/dev/pull-requests.md) for more information. -The main branch of every Cosmos repository should just build with `go get`, -which means they should be kept up-to-date with their dependencies, so we can -get away with telling people they can just `go get` our software. +## Relevant development docs -Since some dependencies are not under our control, a third party may break our -build, in which case we can fall back on `go mod tidy -v`. - -## Protobuf - -We use [Protocol Buffers](https://developers.google.com/protocol-buffers) along with [gogoproto](https://github.com/gogo/protobuf) to generate code for use in ibc-go. - -For determinstic behavior around Protobuf tooling, everything is containerized using Docker. Make sure to have Docker installed on your machine, or head to [Docker's website](https://docs.docker.com/get-docker/) to install it. - -For formatting code in `.proto` files, you can run `make proto-format` command. - -For linting and checking breaking changes, we use [buf](https://buf.build/). You can use the commands `make proto-lint` and `make proto-check-breaking` to respectively lint your proto files and check for breaking changes. - -To generate the protobuf stubs, you can run `make proto-gen`. - -We also added the `make proto-all` command to run all the above commands sequentially. - -In order for imports to properly compile in your IDE, you may need to manually set your protobuf path in your IDE's workspace settings/config. - -For example, in vscode your `.vscode/settings.json` should look like: - -``` -{ - "protoc": { - "options": [ - "--proto_path=${workspaceRoot}/proto", - "--proto_path=${workspaceRoot}/third_party/proto" - ] - } -} -``` - -## Testing - -All go tests in ibc-go can be ran by running `make test`. - -When testing a function under a variety of different inputs, we prefer to use -[table driven tests](https://github.com/golang/go/wiki/TableDrivenTests). - -All tests should use the testing package. Please see the testing package [README](./testing/README.md) for more information. - - -## Branching Model and Release - -User-facing repos should adhere to the trunk based development branching model: https://trunkbaseddevelopment.com/. - -ibc-go utilizes [semantic versioning](https://semver.org/). - -### PR Targeting - -Ensure that you base and target your PR on the `main` branch. - -All development should be targeted against `main`. Bug fixes which are required for outstanding releases should be backported if the CODEOWNERS decide it is applicable. - -### Development Procedure - -- the latest state of development is on `main` -- `main` must never fail `make test` -- no `--force` onto `main` (except when reverting a broken commit, which should seldom happen) -- create a development branch either on github.com/cosmos/ibc-go, or your fork (using `git remote add fork`) -- before submitting a pull request, begin `git rebase` on top of `main` - -### Pull Merge Procedure - -- ensure all github requirements pass -- squash and merge pull request - -### Release Procedure - -- Start on `main` -- Create the release candidate branch `rc/v*` (going forward known as **RC**) - and ensure it's protected against pushing from anyone except the release - manager/coordinator - - **no PRs targeting this branch should be merged unless exceptional circumstances arise** -- On the `RC` branch, prepare a new version section in the `CHANGELOG.md` - - All links must be link-ified: `$ python ./scripts/linkify_changelog.py CHANGELOG.md` -- Run external relayer tests against the prepared RC -- If errors are found during the relayer testing, commit the fixes to `main` - and create a new `RC` branch (making sure to increment the `rcN`) -- After relayer testing has successfully completed, create the release branch - (`release/vX.XX.X`) from the `RC` branch -- Create a PR to `main` to incorporate the `CHANGELOG.md` updates -- Tag the release (use `git tag -a`) and create a release in Github -- Delete the `RC` branches - -### Point Release Procedure - -At the moment, only a single major release will be supported, so all point releases will be based -off of that release. - -In order to alleviate the burden for a single person to have to cherry-pick and handle merge conflicts -of all desired backporting PRs to a point release, we instead maintain a living backport branch, where -all desired features and bug fixes are merged into as separate PRs. - -Example: - -Current release is `v1.0.2`. We then maintain a (living) branch `release/v1.0.x`, given x as -the next patch release number (currently `1.0.3`) for the `1.0` release series. As bugs are fixed -and PRs are merged into `main`, if a contributor wishes the PR to be released into the -`v1.0.x` point release, the contributor must: - -1. Add `1.0.x-backport` label -2. Pull latest changes on the desired `release/v1.0.x` branch -3. Create a 2nd PR merging the respective PR into `release/v1.0.x` -4. Update the PR's description and ensure it contains the following information: - - **[Impact]** Explanation of how the bug affects users or developers. - - **[Test Case]** section with detailed instructions on how to reproduce the bug. - - **[Regression Potential]** section with a discussion how regressions are most likely to manifest, or might - manifest even if it's unlikely, as a result of the change. **It is assumed that any backport PR is - well-tested before it is merged in and has an overall low risk of regression**. This section should discuss - the potential for state breaking changes to occur such as through out-of-gas errors. - -It is the PR's author's responsibility to fix merge conflicts, update changelog entries, and -ensure CI passes. If a PR originates from an external contributor, it may be a core team member's -responsibility to perform this process instead of the original author. -Lastly, it is core team's responsibility to ensure that the PR meets all the backport criteria. - -Finally, when a point release is ready to be made: - -1. Checkout `release/v1.0.x` branch -2. Ensure changelog entries are verified -3. Add release version date to the changelog -4. Push release branch along with the annotated tag: **git tag -a** -5. Create a PR into `main` containing ONLY `CHANGELOG.md` updates - -Note, although we aim to support only a single release at a time, the process stated above could be -used for multiple previous versions. +- [Project structure](./docs/dev/project-structure.md) +- [Develoment setup](./docs/dev/development-setup.md) +- [Go style guide](./docs/dev/go-style-guide.md) +- [Documentation guidelines](./docs/DOCS_GUIDELINES.md) +- [Writing tests](./testing/README.md) +- [Pull request guidelines](./docs/dev/pull-requests.md) +- [Release process](./docs/dev/release-management.md) diff --git a/Dockerfile b/Dockerfile index a193f54906e..64f76ec298a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,23 +1,35 @@ -FROM golang:1.18 as builder +FROM golang:1.19 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 internal internal ADD testing testing ADD modules modules ADD LICENSE LICENSE +COPY contrib/devtools/Makefile contrib/devtools/Makefile COPY Makefile . + 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/MAINTAINERSHIP.md b/MAINTAINERSHIP.md new file mode 100644 index 00000000000..f3247c1b225 --- /dev/null +++ b/MAINTAINERSHIP.md @@ -0,0 +1,62 @@ +# Request For Maintainership + +This document details the acceptance process for requests from external contributors who require for the ibc-go core team to take over **maintainership** of a complex block of code (ie: an application module or a light client implementation). It is the process we also follow internally for features that we develop which will go into the `ibc-go` codebase. + +For projects who have created a custom IBC application and want us to list this application on the registry, please break out your module into its own repo for ease of import into chains! + +- Create a repo with the custom module in a folder `x/` or `modules/`. +- Please include an app that contains the custom module along with end-to-end tests that spins up the blockchain and runs the custom module. +- See [cosmos/interchain-security](https://github.com/cosmos/interchain-security) for an example of this setup. + +For contributors wishing to submit contributions to the codebase, please check our [Contributor Guidelines](https://github.com/cosmos/ibc-go/blob/main/CONTRIBUTING.md) :) + +

+ maintainership +

+ +## Step 1: Product check + +Reach out to the IBC product team through susannah@interchain.io to coordinate use-case walkthrough. + +Answer these questions in a requirements doc: + +- What problem does this feature solve? +- What are the current solutions or workarounds? +- Are there other versions or implementations? +- If there are other versions of this feature, why is this solution better? +- What are the use cases? +- Which users have confirmed they will use this? +- How urgent is it to implement this feature? +- How soon after being developed would this be adopted? +- What is the impact of this feature being adopted? +- Is there a specific need for this feature to be included in the `ibc-go` codebase, rather than in its own module repo? + +Answers to these questions should also be detailed in a **discussion** in the `ibc-go` repo to open up the discussion to a wider audience, this can be done before or after the walkthrough. + +The acceptance criteria is based on the answers to these questions and the results of this product check, as well as of course an acceptable spec should the module be deemed to need one. Please see Step #2 below for the spec considerations. + +In summary, the feature must solve a genuine problem, have users that would greatly benefit from the solution and be generic enough to benefit many users of the `ibc-go` implementation. + +## Step 2: Submit spec to the IBC protocol repo + +A detailed review of the specification can be expected **within 2 weeks** of submission of the specification to the repo. Please notify the specification team if this does not occur, so it can be corrected as soon as possible. Please note that this timeline may be subject to amendment based on complexity of the spec and team capacity considering other ongoing reviews, but we will strive to ensure a 2 week turnaround. + +Any IBC code that is expected to be implemented across different chains in order to function correctly must be specified in the IBC repo to be accepted unless exempted by the specification team. + +Unilateral software (ie. code that only needs to run on a single chain to be functional) need not be submitted. In these cases however, some sort of design document such as [ADR-008](https://github.com/cosmos/ibc-go/pull/1976/files) should be submitted. + +If the associated module to be developed is expected to be submitted to the `ibc-go` team for maintainership, this should already be flagged at this step so that we can start thinking about/preparing our own capacity for the engineering team. + +## Step 3: Prepare code for handover + +*(this step can be initiated in parallel w/ spec submission, subject to feature complexity)* + +Once the spec has been given initial approval, `ibc-go` engineering will coordinate a code walkthrough in preparation for taking the module into the repo. Any requested changes from the `ibc-go` engineering team after the code walkthrough should be discussed and/or addressed in a timely manner. + +The code that is presented should adhere to our [code contributor guidelines](https://github.com/cosmos/ibc-go/blob/main/CONTRIBUTING.md). + +More details on what code walkthrough should cover will be provided by the `ibc-go` engineering team on a case by case basis. However, the code should be sufficiently unit and [E2E tested](https://github.com/cosmos/ibc-go/blob/main/e2e/README.md). Think about preparing for this process similarly to submitting a codebase for audit :) + +Please indicate the expected contribution of your team maintainership, if any. This contribution should also include ideas about devrels support and support for product on social media. + +ETA for the actual handover will be subject to amendment based on feedback resulting from the code walkthrough. diff --git a/Makefile b/Makefile index eba4b5c2de2..73c89cb236b 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,7 @@ SIMAPP = ./testing/simapp MOCKS_DIR = $(CURDIR)/tests/mocks HTTPS_GIT := https://github.com/cosmos/ibc-go.git DOCKER := $(shell which docker) -DOCKER_BUF := $(DOCKER) run --rm -v $(CURDIR):/workspace --workdir /workspace bufbuild/buf:1.0.0-rc8 +PROJECT_NAME = $(shell git remote get-url origin | xargs basename -s .git) export GO111MODULE = on @@ -96,7 +96,7 @@ endif all: build lint test # The below include contains the tools and runsim targets. -#include contrib/devtools/Makefile +include contrib/devtools/Makefile ############################################################################### ### Build ### @@ -114,47 +114,7 @@ $(BUILD_TARGETS): go.sum $(BUILDDIR)/ $(BUILDDIR)/: mkdir -p $(BUILDDIR)/ -build-simd-all: go.sum - $(DOCKER) rm latest-build || true - $(DOCKER) run --volume=$(CURDIR):/sources:ro \ - --env TARGET_PLATFORMS='linux/amd64 darwin/amd64 linux/arm64 windows/amd64' \ - --env APP=simd \ - --env VERSION=$(VERSION) \ - --env COMMIT=$(COMMIT) \ - --env LEDGER_ENABLED=$(LEDGER_ENABLED) \ - --name latest-build cosmossdk/rbuilder:latest - $(DOCKER) cp -a latest-build:/home/builder/artifacts/ $(CURDIR)/ - -build-simd-linux: go.sum $(BUILDDIR)/ - $(DOCKER) rm latest-build || true - $(DOCKER) run --volume=$(CURDIR):/sources:ro \ - --env TARGET_PLATFORMS='linux/amd64' \ - --env APP=simd \ - --env VERSION=$(VERSION) \ - --env COMMIT=$(COMMIT) \ - --env LEDGER_ENABLED=false \ - --name latest-build cosmossdk/rbuilder:latest - $(DOCKER) cp -a latest-build:/home/builder/artifacts/ $(CURDIR)/ - cp artifacts/simd-*-linux-amd64 $(BUILDDIR)/simd - -cosmovisor: - $(MAKE) -C cosmovisor cosmovisor - -.PHONY: build build-linux build-simd-all build-simd-linux cosmovisor - -mocks: $(MOCKS_DIR) - mockgen -source=client/account_retriever.go -package mocks -destination tests/mocks/account_retriever.go - mockgen -package mocks -destination tests/mocks/tendermint_tm_db_DB.go github.com/tendermint/tm-db DB - mockgen -source=types/module/module.go -package mocks -destination tests/mocks/types_module_module.go - mockgen -source=types/invariant.go -package mocks -destination tests/mocks/types_invariant.go - mockgen -source=types/router.go -package mocks -destination tests/mocks/types_router.go - mockgen -source=types/handler.go -package mocks -destination tests/mocks/types_handler.go - mockgen -package mocks -destination tests/mocks/grpc_server.go github.com/gogo/protobuf/grpc Server - mockgen -package mocks -destination tests/mocks/tendermint_tendermint_libs_log_DB.go github.com/tendermint/tendermint/libs/log Logger -.PHONY: mocks - -$(MOCKS_DIR): - mkdir -p $(MOCKS_DIR) +.PHONY: build build-linux distclean: clean clean: @@ -179,7 +139,7 @@ go.sum: go.mod ############################################################################### update-swagger-docs: statik - $(BINDIR)/statik -src=client/docs/swagger-ui -dest=client/docs -f -m + $(BINDIR)/statik -src=docs/client/swagger-ui -dest=docs/client -f -m @if [ -n "$(git status --porcelain)" ]; then \ echo "\033[91mSwagger docs are out of sync!!!\033[0m";\ exit 1;\ @@ -203,12 +163,17 @@ build-docs: mkdir -p ~/output/$${path_prefix} ; \ cp -r .vuepress/dist/* ~/output/$${path_prefix}/ ; \ cp ~/output/$${path_prefix}/index.html ~/output ; \ + cp ~/output/$${path_prefix}/404.html ~/output ; \ done < versions ; 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 ############################################################################### @@ -317,11 +282,6 @@ test-cover: @export VERSION=$(VERSION); bash -x contrib/test_cover.sh .PHONY: test-cover -test-rosetta: - docker build -t rosetta-ci:latest -f contrib/rosetta/node/Dockerfile . - docker-compose -f contrib/rosetta/docker-compose.yaml up --abort-on-container-exit --exit-code-from test_rosetta --build -.PHONY: test-rosetta - benchmark: @go test -mod=readonly -bench=. $(PACKAGES_NOSIMULATION) .PHONY: benchmark @@ -335,166 +295,52 @@ lint: lint-fix: golangci-lint run --fix --out-format=tab --issues-exit-code=0 -.PHONY: lint lint-fix - -format: goimports - find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" -not -path "./client/docs/statik/statik.go" -not -path "./tests/mocks/*" -not -name '*.pb.go' | xargs gofmt -w -s - find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" -not -path "./client/docs/statik/statik.go" -not -path "./tests/mocks/*" -not -name '*.pb.go' | xargs misspell -w -.PHONY: format - -goimports: - $(DOCKER) run -v $(CURDIR):/ibc-go --rm -w "/ibc-go" cytopia/goimports -w -local 'github.com/cosmos/ibc-go' "$(CHANGED_GO_FILES)" &> /dev/null || echo "No changed go files to format" - -goimports-all: - $(DOCKER) run -v $(CURDIR):/ibc-go --rm -w "/ibc-go" cytopia/goimports -w -local 'github.com/cosmos/ibc-go' "$(ALL_GO_FILES)" - -############################################################################### -### Devdoc ### -############################################################################### -DEVDOC_SAVE = docker commit `docker ps -a -n 1 -q` devdoc:local +lint-fix-changed: + ./scripts/linting/lint-changed-go-files.sh -devdoc-init: - $(DOCKER) run -it -v "$(CURDIR):/go/src/github.com/cosmos/cosmos-sdk" -w "/go/src/github.com/cosmos/cosmos-sdk" tendermint/devdoc echo - # TODO make this safer - $(call DEVDOC_SAVE) - -devdoc: - $(DOCKER) run -it -v "$(CURDIR):/go/src/github.com/cosmos/cosmos-sdk" -w "/go/src/github.com/cosmos/cosmos-sdk" devdoc:local bash - -devdoc-save: - # TODO make this safer - $(call DEVDOC_SAVE) +format: + find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" -not -path "./docs/client/statik/statik.go" -not -path "./tests/mocks/*" -not -name '*.pb.go' -not -name '*.pb.gw.go' | xargs gofumpt -w + find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" -not -path "./docs/client/statik/statik.go" -not -path "./tests/mocks/*" -not -name '*.pb.go' -not -name '*.pb.gw.go' | xargs misspell -w +.PHONY: format -devdoc-clean: - docker rmi -f $$(docker images -f "dangling=true" -q) +docs-lint: + markdownlint . --fix -devdoc-update: - docker pull tendermint/devdoc +docs-lint-changed: + ./scripts/linting/lint-changed-md-files.sh -.PHONY: devdoc devdoc-clean devdoc-init devdoc-save devdoc-update +.PHONY: lint lint-fix lint-fix-changed docs-lint docs-lint-changed ############################################################################### ### Protobuf ### ############################################################################### -containerProtoVer=v0.2 -containerProtoImage=tendermintdev/sdk-proto-gen:$(containerProtoVer) -containerProtoGen=cosmos-sdk-proto-gen-$(containerProtoVer) -containerProtoGenSwagger=cosmos-sdk-proto-gen-swagger-$(containerProtoVer) -containerProtoFmt=cosmos-sdk-proto-fmt-$(containerProtoVer) +protoVer=0.11.5 +protoImageName=ghcr.io/cosmos/proto-builder:$(protoVer) +protoImage=$(DOCKER) run --rm -v $(CURDIR):/workspace --workdir /workspace $(protoImageName) proto-all: proto-format proto-lint proto-gen proto-gen: @echo "Generating Protobuf files" - @if docker ps -a --format '{{.Names}}' | grep -Eq "^${containerProtoGen}$$"; then docker start -a $(containerProtoGen); else docker run --name $(containerProtoGen) -v $(CURDIR):/workspace --workdir /workspace $(containerProtoImage) \ - sh ./scripts/protocgen.sh; fi - -proto-format: - @echo "Formatting Protobuf files" - @if docker ps -a --format '{{.Names}}' | grep -Eq "^${containerProtoFmt}$$"; then docker start -a $(containerProtoFmt); else docker run --name $(containerProtoFmt) -v $(CURDIR):/workspace --workdir /workspace tendermintdev/docker-build-proto \ - find ./ -not -path "./third_party/*" -name "*.proto" -exec clang-format -i {} \; ; fi + @$(protoImage) sh ./scripts/protocgen.sh proto-swagger-gen: @echo "Generating Protobuf Swagger" - @if docker ps -a --format '{{.Names}}' | grep -Eq "^${containerProtoGenSwagger}$$"; then docker start -a $(containerProtoGenSwagger); else docker run --name $(containerProtoGenSwagger) -v $(CURDIR):/workspace --workdir /workspace $(containerProtoImage) \ - sh ./scripts/protoc-swagger-gen.sh; fi + @$(protoImage) sh ./scripts/protoc-swagger-gen.sh + +proto-format: + @$(protoImage) find ./ -name "*.proto" -exec clang-format -i {} \; proto-lint: - @$(DOCKER_BUF) lint --error-format=json + @$(protoImage) buf lint --error-format=json proto-check-breaking: - @$(DOCKER_BUF) breaking --against $(HTTPS_GIT)#branch=main - -TM_URL = https://raw.githubusercontent.com/tendermint/tendermint/v0.34.0-rc6/proto/tendermint -GOGO_PROTO_URL = https://raw.githubusercontent.com/regen-network/protobuf/cosmos -CONFIO_URL = https://raw.githubusercontent.com/confio/ics23/v0.6.3 -SDK_PROTO_URL = https://raw.githubusercontent.com/cosmos/cosmos-sdk/v0.41.0/proto/cosmos - -TM_CRYPTO_TYPES = third_party/proto/tendermint/crypto -TM_ABCI_TYPES = third_party/proto/tendermint/abci -TM_TYPES = third_party/proto/tendermint/types -TM_VERSION = third_party/proto/tendermint/version -TM_LIBS = third_party/proto/tendermint/libs/bits -TM_P2P = third_party/proto/tendermint/p2p - -SDK_QUERY = third_party/proto/cosmos/base/query/v1beta1 -SDK_BASE = third_party/proto/cosmos/base/v1beta1 -SDK_UPGRADE = third_party/proto/cosmos/upgrade - -GOGO_PROTO_TYPES = third_party/proto/gogoproto -CONFIO_TYPES = third_party/proto/confio + @$(protoImage) buf breaking --against $(HTTPS_GIT)#branch=main proto-update-deps: - @mkdir -p $(GOGO_PROTO_TYPES) - @curl -sSL $(GOGO_PROTO_URL)/gogoproto/gogo.proto > $(GOGO_PROTO_TYPES)/gogo.proto - - @mkdir -p $(SDK_QUERY) - @curl -sSL $(SDK_PROTO_URL)/base/query/v1beta1/pagination.proto > $(SDK_QUERY)/pagination.proto - - @mkdir -p $(SDK_BASE) - @curl -sSL $(SDK_PROTO_URL)/base/v1beta1/coin.proto > $(SDK_BASE)/coin.proto - - @mkdir -p $(SDK_UPGRADE) - @curl -sSL $(SDK_PROTO_URL)/upgrade/v1beta1/upgrade.proto > $(SDK_UPGRADE)/v1beta1/upgrade.proto - -## Importing of tendermint protobuf definitions currently requires the -## use of `sed` in order to build properly with cosmos-sdk's proto file layout -## (which is the standard Buf.build FILE_LAYOUT) -## Issue link: https://github.com/tendermint/tendermint/issues/5021 - @mkdir -p $(TM_TYPES) - @curl -sSL $(TM_URL)/types/types.proto > $(TM_TYPES)/types.proto - @curl -sSL $(TM_URL)/types/validator.proto > $(TM_TYPES)/validator.proto - - @mkdir -p $(TM_VERSION) - @curl -sSL $(TM_URL)/version/types.proto > $(TM_VERSION)/types.proto - - @mkdir -p $(TM_LIBS) - @curl -sSL $(TM_URL)/libs/bits/types.proto > $(TM_LIBS)/types.proto - - @mkdir -p $(TM_CRYPTO_TYPES) - @curl -sSL $(TM_URL)/crypto/proof.proto > $(TM_CRYPTO_TYPES)/proof.proto - @curl -sSL $(TM_URL)/crypto/keys.proto > $(TM_CRYPTO_TYPES)/keys.proto - - @mkdir -p $(CONFIO_TYPES) - @curl -sSL $(CONFIO_URL)/proofs.proto > $(CONFIO_TYPES)/proofs.proto - -## insert go package option into proofs.proto file -## Issue link: https://github.com/confio/ics23/issues/32 - @sed -i '4ioption go_package = "github.com/confio/ics23/go";' $(CONFIO_TYPES)/proofs.proto + @echo "Updating Protobuf dependencies" + $(DOCKER) run --rm -v $(CURDIR)/proto:/workspace --workdir /workspace $(protoImageName) buf mod update .PHONY: proto-all proto-gen proto-gen-any proto-swagger-gen proto-format proto-lint proto-check-breaking proto-update-deps - -############################################################################### -### Localnet ### -############################################################################### - -# Run a 4-node testnet locally -localnet-start: build-linux localnet-stop - $(if $(shell $(DOCKER) inspect -f '{{ .Id }}' cosmossdk/simd-env 2>/dev/null),$(info found image cosmossdk/simd-env),$(MAKE) -C contrib/images simd-env) - if ! [ -f build/node0/simd/config/genesis.json ]; then $(DOCKER) run --rm \ - --user $(shell id -u):$(shell id -g) \ - -v $(BUILDDIR):/simd:Z \ - -v /etc/group:/etc/group:ro \ - -v /etc/passwd:/etc/passwd:ro \ - -v /etc/shadow:/etc/shadow:ro \ - cosmossdk/simd-env testnet --v 4 -o . --starting-ip-address 192.168.10.2 --keyring-backend=test ; fi - docker-compose up -d - -localnet-stop: - docker-compose down - -.PHONY: localnet-start localnet-stop - -############################################################################### -### rosetta ### -############################################################################### -# builds rosetta test data dir -rosetta-data: - -docker container rm data_dir_build - docker build -t rosetta-ci:latest -f contrib/rosetta/node/Dockerfile . - docker run --name data_dir_build -t rosetta-ci:latest sh /rosetta/data.sh - docker cp data_dir_build:/tmp/data.tar.gz "$(CURDIR)/contrib/rosetta/node/data.tar.gz" - docker container rm data_dir_build -.PHONY: rosetta-data diff --git a/README.md b/README.md index f4a830c5149..1469fcd021d 100644 --- a/README.md +++ b/README.md @@ -31,12 +31,88 @@ Imported by - Lint Status + Tests / Code Coverage Status + E2E Status -The Inter-Blockchain Communication protocol (IBC) allows blockchains to talk to each other. IBC handles transport across different sovereign blockchains. This end-to-end, connection-oriented, stateful protocol provides reliable, ordered, and authenticated communication between heterogeneous blockchains. This IBC implementation in Golang is built as a Cosmos SDK module. +The [Inter-Blockchain Communication protocol (IBC)](https://ibcprotocol.dev/) allows blockchains to talk to each other. This end-to-end, connection-oriented, stateful protocol provides reliable, ordered, and authenticated communication between heterogeneous blockchains. -## Contents +This IBC implementation in Golang is built as a Cosmos SDK module. To understand more about how to use the `ibc-go` module as well as about the IBC protocol, please check out the Interchain Developer Academy [section on IBC](https://tutorials.cosmos.network/academy/3-ibc/), or [our docs](https://ibc.cosmos.network/main/ibc/overview.html). + +## Roadmap + +For an overview of upcoming changes to ibc-go take a look at the [roadmap](./docs/roadmap/roadmap.md). + +This roadmap is also available as a [project board](https://github.com/orgs/cosmos/projects/7/views/25). + +For the latest expected release timelines, please check [here](https://github.com/cosmos/ibc-go/wiki/Release-timeline). + +For the latest information on the progress of the work or the decisions made that might influence the roadmap, please follow our [engineering updates](https://github.com/cosmos/ibc-go/wiki/Engineering-updates). + +## Releases + +The release lines currently supported are v3, v4, v5 and v6. + +Please refer to the [Stable Release Policy section of RELEASES.md](https://github.com/cosmos/ibc-go/blob/main/RELEASES.md#stable-release-policy) for more details. + +Please refer to our [versioning guide](https://github.com/cosmos/ibc-go/blob/main/RELEASES.md) for more information on how to understand our release versioning. + +## Ecosystem + +Discover the applications, middleware and light clients developed by other awesome teams in the ecosystem: + +In the table below +`app` refers to IBC application modules for custom use cases and +`middleware` refers to modules that wrap an IBC application enabling custom logic to be executed. + +|Description|Repository|Type| +|----------|----------|----| +|An application that enables on chain querying of another IBC enabled chain utilizing `baseapp.Query`. Both chains must have implemented the query application and ICA (for queries requiring consensus).|[async-icq](https://github.com/strangelove-ventures/async-icq)|`app`| +|An application that enables on chain querying of another IBC enabled chains state without the need for the chain being queried to implement the application.|[interchain-queries](https://github.com/ingenuity-build/interchain-queries)|`app`| +|An application that enables on chain querying of another IBC enabled chains state without the need for the chain being queried to implement the application. Similar to the interchain-queries application in the row above but without callbacks.|[query](https://github.com/defund-labs/defund/tree/main/x/query)|`app`| +|An application that enables cross chain NFT transfer.|[NFT Transfer (ICS 721)](https://github.com/bianjieai/nft-transfer)|`app`| +|Middleware enabling a packet to be sent to a destination chain via an intermediate chain, e.g. going from Juno to Osmosis via the Hub.|[packet-forward-middleware](https://github.com/strangelove-ventures/packet-forward-middleware)|`middleware`| +|Middleware enabling the recovery of tokens sent to unsupported addresses.|[recovery](https://github.com/evmos/evmos/tree/main/x/recovery)|`middleware`| +|Middleware that limits the in or out flow of an asset in a certain time period to minimise the risks of cross chain token transfers. This is implemented as a middleware wrapping ICS20 with the rate limiting logic implemented by cosmwasm contracts|[ibc-rate-limit](https://github.com/osmosis-labs/osmosis/tree/main/x/ibc-rate-limit)|`middleware`| + +## Support + +We have active, helpful communities on Discord and Telegram. + +For questions and support please use the `developers` channel in the [Cosmos Network Discord server](https://discord.com/channels/669268347736686612/1019978171367559208) or join the [IBC Gang Discord server](https://discord.gg/RdpdkaXKpZ). The issue list of this repo is exclusively for bug reports and feature requests. + +To receive announcements of new releases or other technical updates, please join the [Telegram group that we administer](https://t.me/ibc_is_expansive). + +## Contributing + +If you're interested in contributing to ibc-go, please take a look at the [contributing guidelines](./CONTRIBUTING.md). We welcome and appreciate community contributions! + +This project adheres to ibc-go's [code of conduct](./CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code. + +To help contributors understand which issues are good to pick up, we have the following two categories: + +- Issues with the label [`good first issue`](https://github.com/cosmos/ibc-go/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22) should be pretty well defined and are best suited for developers new to ibc-go. +- Issues with the label [`help wanted`](https://github.com/cosmos/ibc-go/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22) are a bit more involved and they usually require some familiarity already with the codebase. + +If you are interested in working on an issue, please comment on it; then we will be able to assign it to you. We will be happy to answer any questions you may have and help you out while you work on the issue. + +If you have any general questions or feedback, please reach out to us in the [IBC Gang Discord server](https://discord.com/channels/955868717269516318/955883113484013578). + +## Request for maintainership + +We have a document that describes the process for [submitting a feature for maintainership](./MAINTAINERSHIP.md) in the ibc-go codebase. + +## Security + +To report a security vulnerability, see our [bug bounty program](https://hackerone.com/cosmos). + +## Audits + +The following audits have been performed on the `ibc-go` source code: + +- [ICS27 Interchain Accounts](https://github.com/cosmos/ibc-go/tree/main/docs/apps/interchain-accounts/audits) by Trail of Bits + +## Quick Navigation 1. **[Core IBC Implementation](https://github.com/cosmos/ibc-go/tree/main/modules/core)** @@ -58,20 +134,21 @@ The Inter-Blockchain Communication protocol (IBC) allows blockchains to talk to 2.2 [ICS 27 Interchain Accounts](https://github.com/cosmos/ibc-go/tree/main/modules/apps/27-interchain-accounts) -3. **Light Clients** +3. **Middleware** - 3.1 [ICS 07 Tendermint](https://github.com/cosmos/ibc-go/tree/main/modules/light-clients/07-tendermint) + 3.1 [ICS 29 Fee Middleware](https://github.com/cosmos/ibc-go/tree/main/modules/apps/29-fee) - 3.2 [ICS 06 Solo Machine](https://github.com/cosmos/ibc-go/tree/main/modules/light-clients/06-solomachine) +4. **Light Clients** -Note: The localhost client is currently non-functional. + 4.1 [ICS 07 Tendermint](https://github.com/cosmos/ibc-go/tree/main/modules/light-clients/07-tendermint) -## Roadmap + 4.2 [ICS 06 Solo Machine](https://github.com/cosmos/ibc-go/tree/main/modules/light-clients/06-solomachine) -For an overview of upcoming changes to ibc-go take a look at the [roadmap](./docs/roadmap/roadmap.md). +5. **[E2E Integration Tests](https://github.com/cosmos/ibc-go/tree/main/e2e)** -## Resources +## Documentation and Resources -- [IBC Website](https://ibcprotocol.org/) -- [IBC Specification](https://github.com/cosmos/ibc) +- [IBC Website](https://ibcprotocol.dev/) +- [IBC Protocol Specification](https://github.com/cosmos/ibc) - [Documentation](https://ibc.cosmos.network/main/ibc/overview.html) +- [Interchain Developer Academy](https://tutorials.cosmos.network/academy/3-ibc/) diff --git a/RELEASES.md b/RELEASES.md index 9adabdc61f9..4c9cff29313 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -17,85 +17,86 @@ When bumping the dependencies of [Cosmos SDK](https://github.com/cosmos/cosmos-s [ibc-go](https://github.com/cosmos/ibc-go) and the [IBC protocol specification](https://github.com/cosmos/ibc) maintain different versions. Furthermore, ibc-go serves several different user groups (chains, IBC app developers, relayers, IBC light client developers). Each of these groups has different expectations of what *backwards compatible* means. It simply isn't possible to categorize a change as backwards or non backwards compatible for all user groups. We are primarily interested in when our API breaks and when changes are state machine breaking (thus requiring a coordinated upgrade). This is scoping the meaning of ibc-go to that of those interacting with the code (IBC app developers, relayers, IBC light client developers), not chains using IBC to communicate (that should be encapsulated by the IBC protocol specification versioning). -To summarize: **All our ibc-go releases allow chains to communicate successfully with any chain running any version of our code**. That is to say, we are still using IBC protocol specification v1.0. +To summarize: **All our ibc-go releases allow chains to communicate successfully with any chain running any version of our code**. That is to say, we are still using IBC protocol specification v1.0. We ensure all major releases are supported by relayers ([hermes](https://github.com/informalsystems/ibc-rs), [rly](https://github.com/strangelove-ventures/relayer) and [ts-relayer](https://github.com/confio/ts-relayer) at the moment) which can relay between the new major release and older releases. We have no plans of upgrading to an IBC protocol specification v2.0, as this would be very disruptive to the ecosystem. ## Release cycle -IBC-Go follows a traditional release cycle involving an alpha, beta, and rc (release candidate) releases before finalizing a new version. As ibc-go works in a non-traditional area, we apply our own interpretation to each release type. We reserve the right to make both go API breaking changes and state machine breaking changes throughout the entire release cycle. The stable release guarentees do not go into affect until a final release is performed. +IBC-Go follows a traditional release cycle involving an alpha, beta, and rc (release candidate) releases before finalizing a new version. As ibc-go works in a non-traditional area, we apply our own interpretation to each release type. We reserve the right to make both go API breaking changes and state machine breaking changes throughout the entire release cycle. The stable release guarantees do not go into affect until a final release is performed. -It is never advisable to use a non-final release in production. +It is never advisable to use a non-final release in production. ### Alpha -Alpha releases are intended to make available new features as soon as they are functional. No correctness guarentees are made and alpha releases **may** contain serious security vulnerabilities, bugs, and lack of user tooling, so long as they don't affect the core functionality. +Alpha releases are intended to make available new features as soon as they are functional. No correctness guarantees are made and alpha releases **may** contain serious security vulnerabilities, bugs, and lack of user tooling, so long as they don't affect the core functionality. Initial users of alpha releases are expected to be advanced, patient, and capable of handling unusual errors. Very basic integration testing will be performed by the ibc-go development team before alpha releases. -An internal audit is typically performed before the alpha release allowing the development team to guage the maturity and stability of changes included in the next release. +An internal audit is typically performed before the alpha release allowing the development team to guage the maturity and stability of changes included in the next release. ### Beta -Beta releases are intended to signal design stability. While the go API is still subject to change, the core design of the new features should not be. Developers integrating the new features should expect to handle breaking changes when upgrading to RC's. +Beta releases are intended to signal design stability. While the go API is still subject to change, the core design of the new features should not be. Developers integrating the new features should expect to handle breaking changes when upgrading to RC's. -Beta releases should not be made with known bugs or security vulnerabilities. Beta releases should focus on ironing out remaining bugs and filling out the UX functionality required by a final release. Beta releases should have a clearly defined scope of the features that will be included in the release. Only highly requested feature additions should be acted upon in this phase. +Beta releases should not be made with known bugs or security vulnerabilities. Beta releases should focus on ironing out remaining bugs and filling out the UX functionality required by a final release. Beta releases should have a clearly defined scope of the features that will be included in the release. Only highly requested feature additions should be acted upon in this phase. -When the development team has determined a release is ready to enter the RC phase, a final security audit should be performed. The security audit should be limited to looking for bugs and security vulnerabilities. Code improvements may be noted, but they should not be acted upon unless highly desirable. +When the development team has determined a release is ready to enter the RC phase, a final security audit should be performed. The security audit should be limited to looking for bugs and security vulnerabilities. Code improvements may be noted, but they should not be acted upon unless highly desirable. -### RC +### RC RC's are release candidates. Final releases should contain little to no changes in comparison to the latest RC. Changes included in between RC releases should be limited to: + - Improved testing - UX additions - Bug fixes - Highly requested changes by the community -A release should not be finalized until the development team and the external community have done sufficient integration tests on the targeted release. +A release should not be finalized until the development team and the external community have done sufficient integration tests on the targeted release. ## Stable Release Policy The beginning of a new major release series is marked by the release of a new major version. A major release series is comprised of all minor and patch releases made under the same major version number. The series continues to receive bug fixes (released as minor or patch releases) until it reaches end of life. The date when a major release series reaches end of life is determined by one of the two following methods: -- If the next major release is made within the first 6 months, then the end of life date of the major release series is 1 year after its initial release. + +- If the next major release is made within the first 6 months, then the end of life date of the major release series is 1 year after its initial release. - If the next major release is made 6 months after the initial release, then the end of life date of the major release series is 6 months after the release date of the next major release. -For example, if the current major release series is v1 and was released on January 1st, 2022, then v1 will be supported at least until January 1st, 2023. If v2 is published on August 1st 2022, then v1's end of life will be March 1st, 2023. +For example, if the current major release series is v1 and was released on January 1st, 2022, then v1 will be supported at least until January 1st, 2023. If v2 is published on August 1st 2022, then v1's end of life will be March 1st, 2023. Only the following major release series have a stable release status: |Release|End of Life Date| |-------|----------------| -|`v1.3.x`|July 01, 2022| -|`v1.4.x`|July 01, 2022| -|`v1.5.x`|July 01, 2022| -|`v2.1.x`|February 01, 2023| -|`v2.2.x`|February 01, 2023| -|`v2.3.x`|February 01, 2023| -|`v3.0.x`|March 15, 2023| -|`v3.1.x`|March 15, 2023| +|`v3.3.x`|March 15, 2023| +|`v3.4.x`|March 15, 2023| +|`v4.1.x`|August 12, 2023| +|`v4.2.x`|August 12, 2023| +|`v4.3.x`|August 12, 2023| +|`v5.2.x`|September 28, 2023| +|`v6.1.x`|December 09, 2023| -**Note**: The v1 major release series will reach end of life 6 months after merging this policy. v2 will reach end of life one year after merging this policy. +All missing minor release versions have been discontinued. ### What pull requests will be included in stable patch-releases? Pull requests that fix bugs and add features that fall in the following categories: -* **Severe regressions**. -* Bugs that may cause **client applications** to be **largely unusable**. -* Bugs that may cause **state corruption or data loss**. -* Bugs that may directly or indirectly cause a **security vulnerability**. -* Non-breaking features that are strongly requested by the community. -* Non-breaking CLI improvements that are strongly requested by the community. +- **Severe regressions**. +- Bugs that may cause **client applications** to be **largely unusable**. +- Bugs that may cause **state corruption or data loss**. +- Bugs that may directly or indirectly cause a **security vulnerability**. +- Non-breaking features that are strongly requested by the community. +- Non-breaking CLI improvements that are strongly requested by the community. ### What pull requests will NOT be automatically included in stable patch-releases? As rule of thumb, the following changes will **NOT** be automatically accepted into stable point-releases: -* **State machine changes**, unless the previous behaviour would result in a consensus halt. -* **Protobuf-breaking changes**. -* **Client-breaking changes**, i.e. changes that prevent gRPC, HTTP and RPC clients to continue interacting with the node without any change. -* **API-breaking changes**, i.e. changes that prevent client applications to *build without modifications* to the client application's source code. -* **CLI-breaking changes**, i.e. changes that require usage changes for CLI users. +- **State machine changes**, unless the previous behaviour would result in a consensus halt. +- **Protobuf-breaking changes**. +- **Client-breaking changes**, i.e. changes that prevent gRPC, HTTP and RPC clients to continue interacting with the node without any change. +- **API-breaking changes**, i.e. changes that prevent client applications to *build without modifications* to the client application's source code. +- **CLI-breaking changes**, i.e. changes that require usage changes for CLI users. ## Version matrix @@ -103,33 +104,32 @@ Versions of Golang, Cosmos SDK and Tendermint used by ibc-go in the currently ac | Go | ibc-go | Cosmos SDK | Tendermint | |----|--------|------------|------------| -| 1.15 | v2.1.0 | v0.44.6 | v0.34.14 | -| 1.15 | v2.1.1 | v0.44.8 | v0.34.19 | -| 1.15 | v2.2.0 | v0.45.1 | v0.34.14 | -| 1.15 | v2.2.1 | v0.45.1 | v0.34.14 | -| 1.17 | v2.3.0 | v0.45.4 | v0.34.19 | -| 1.17 | v3.0.0 | v0.45.1 | v0.34.14 | -| 1.17 | v3.0.1 | v0.45.4 | v0.34.19 | -| 1.17 | v3.1.0 | v0.45.4 | v0.34.19 | +| 1.18 | v3.3.1 | v0.45.10 | v0.34.22 | +| 1.18 | v3.4.0 | v0.45.10 | v0.34.22 | +| 1.18 | v4.1.1 | v0.45.10 | v0.34.22 | +| 1.18 | v4.2.0 | v0.45.10 | v0.34.22 | +| 1.18 | v4.3.0 | v0.45.12 | v0.34.24 | +| 1.18 | v5.2.0 | v0.46.7 | v0.34.24 | +| 1.18 | v6.1.0 | v0.46.7 | v0.34.24 | ## Graphics The decision tree above was generated with the following code: -``` +```text %%{init: - {'theme': 'default', - 'themeVariables': - {'fontFamily': 'verdana', 'fontSize': '13px'} - } + {'theme': 'default', + 'themeVariables': + {'fontFamily': 'verdana', 'fontSize': '13px'} + } }%% flowchart TD - A(Change):::c --> B{API breaking?} - B:::c --> |Yes| C(Increase major version):::c - B:::c --> |No| D{state-machine breaking?} - D:::c --> |Yes| G(Increase minor version):::c - D:::c --> |No| H(Increase patch version):::c - classDef c fill:#eee,stroke:#aaa + A(Change):::c --> B{API breaking?} + B:::c --> |Yes| C(Increase major version):::c + B:::c --> |No| D{state-machine breaking?} + D:::c --> |Yes| G(Increase minor version):::c + D:::c --> |No| H(Increase patch version):::c + classDef c fill:#eee,stroke:#aaa ``` using [Mermaid](https://mermaid-js.github.io)'s [live editor](https://mermaid.live). diff --git a/buf.work.yaml b/buf.work.yaml index 98094695ffe..1b4a0d95c29 100644 --- a/buf.work.yaml +++ b/buf.work.yaml @@ -6,4 +6,3 @@ version: v1 directories: - proto - - third_party/proto diff --git a/cliff.toml b/cliff.toml new file mode 100644 index 00000000000..c29518f5bbf --- /dev/null +++ b/cliff.toml @@ -0,0 +1,119 @@ +# 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 | trim_start_matches(pat="v") }}] - {{ 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/cmd/build_test_matrix/main.go b/cmd/build_test_matrix/main.go index c86324b31f8..a59e57d9e27 100644 --- a/cmd/build_test_matrix/main.go +++ b/cmd/build_test_matrix/main.go @@ -16,6 +16,13 @@ const ( testNamePrefix = "Test" testFileNameSuffix = "_test.go" e2eTestDirectory = "e2e" + // testEntryPointEnv specifes a single test function to run if provided. + testEntryPointEnv = "TEST_ENTRYPOINT" + // testExclusionsEnv is a comma separated list of test function names that will not be included + // in the results of this script. + testExclusionsEnv = "TEST_EXCLUSIONS" + // testNameEnv if provided returns a single test entry so that only one test is actually run. + testNameEnv = "TEST_NAME" ) // GithubActionTestMatrix represents @@ -24,12 +31,12 @@ type GithubActionTestMatrix struct { } type TestSuitePair struct { - Test string `json:"test"` - Suite string `json:"suite"` + Test string `json:"test"` + EntryPoint string `json:"entrypoint"` } func main() { - githubActionMatrix, err := getGithubActionMatrixForTests(e2eTestDirectory) + githubActionMatrix, err := getGithubActionMatrixForTests(e2eTestDirectory, getTestToRun(), getTestEntrypointToRun(), getExcludedTestFunctions()) if err != nil { fmt.Printf("error generating github action json: %s", err) os.Exit(1) @@ -43,13 +50,55 @@ func main() { fmt.Println(string(ghBytes)) } +// getTestEntrypointToRun returns the specified test function to run if present, otherwise +// it returns an empty string which will result in running all test suites. +func getTestEntrypointToRun() string { + testSuite, ok := os.LookupEnv(testEntryPointEnv) + if !ok { + return "" + } + return testSuite +} + +// getTestToRun returns the specified test function to run if present. +// If specified, only this test will be run. +func getTestToRun() string { + testName, ok := os.LookupEnv(testNameEnv) + if !ok { + return "" + } + return testName +} + +// getExcludedTestFunctions returns a list of test functions that we don't want to run. +func getExcludedTestFunctions() []string { + exclusions, ok := os.LookupEnv(testExclusionsEnv) + if !ok { + return nil + } + return strings.Split(exclusions, ",") +} + +func contains(s string, items []string) bool { + for _, elem := range items { + if elem == s { + return true + } + } + return false +} + // getGithubActionMatrixForTests returns a json string representing the contents that should go in the matrix // field in a github action workflow. This string can be used with `fromJSON(str)` to dynamically build // the workflow matrix to include all E2E tests under the e2eRootDirectory directory. -func getGithubActionMatrixForTests(e2eRootDirectory string) (GithubActionTestMatrix, error) { +func getGithubActionMatrixForTests(e2eRootDirectory, testName string, suite string, excludedItems []string) (GithubActionTestMatrix, error) { testSuiteMapping := map[string][]string{} fset := token.NewFileSet() err := filepath.Walk(e2eRootDirectory, func(path string, info fs.FileInfo, err error) error { + if err != nil { + return fmt.Errorf("error walking e2e directory: %s", err) + } + // only look at test files if !strings.HasSuffix(path, testFileNameSuffix) { return nil @@ -65,11 +114,20 @@ func getGithubActionMatrixForTests(e2eRootDirectory string) (GithubActionTestMat return fmt.Errorf("failed extracting test suite name and test cases: %s", err) } - testSuiteMapping[suiteNameForFile] = testCases + if testName != "" && contains(testName, testCases) { + testCases = []string{testName} + } + + if contains(suiteNameForFile, excludedItems) { + return nil + } + + if suite == "" || suiteNameForFile == suite { + testSuiteMapping[suiteNameForFile] = testCases + } return nil }) - if err != nil { return GithubActionTestMatrix{}, err } @@ -81,12 +139,16 @@ func getGithubActionMatrixForTests(e2eRootDirectory string) (GithubActionTestMat for testSuiteName, testCases := range testSuiteMapping { for _, testCaseName := range testCases { gh.Include = append(gh.Include, TestSuitePair{ - Test: testCaseName, - Suite: testSuiteName, + Test: testCaseName, + EntryPoint: testSuiteName, }) } } + if testName != "" && len(gh.Include) != 1 { + return GithubActionTestMatrix{}, fmt.Errorf("expected exactly 1 test in the output matrix but got %d", len(gh.Include)) + } + return gh, nil } diff --git a/cmd/build_test_matrix/main_test.go b/cmd/build_test_matrix/main_test.go index 47de4d981c2..732973efe0e 100644 --- a/cmd/build_test_matrix/main_test.go +++ b/cmd/build_test_matrix/main_test.go @@ -19,7 +19,7 @@ const ( func TestGetGithubActionMatrixForTests(t *testing.T) { t.Run("empty dir does not fail", func(t *testing.T) { testingDir := t.TempDir() - _, err := getGithubActionMatrixForTests(testingDir) + _, err := getGithubActionMatrixForTests(testingDir, "", "", nil) assert.NoError(t, err) }) @@ -27,18 +27,18 @@ func TestGetGithubActionMatrixForTests(t *testing.T) { testingDir := t.TempDir() createFileWithTestSuiteAndTests(t, "FeeMiddlewareTestSuite", "TestA", "TestB", testingDir, goTestFileNameOne) - gh, err := getGithubActionMatrixForTests(testingDir) + gh, err := getGithubActionMatrixForTests(testingDir, "", "", nil) assert.NoError(t, err) expected := GithubActionTestMatrix{ Include: []TestSuitePair{ { - Suite: "TestFeeMiddlewareTestSuite", - Test: "TestA", + EntryPoint: "TestFeeMiddlewareTestSuite", + Test: "TestA", }, { - Suite: "TestFeeMiddlewareTestSuite", - Test: "TestB", + EntryPoint: "TestFeeMiddlewareTestSuite", + Test: "TestB", }, }, } @@ -50,26 +50,26 @@ func TestGetGithubActionMatrixForTests(t *testing.T) { createFileWithTestSuiteAndTests(t, "FeeMiddlewareTestSuite", "TestA", "TestB", testingDir, goTestFileNameOne) createFileWithTestSuiteAndTests(t, "TransferTestSuite", "TestC", "TestD", testingDir, goTestFileNameTwo) - gh, err := getGithubActionMatrixForTests(testingDir) + gh, err := getGithubActionMatrixForTests(testingDir, "", "", nil) assert.NoError(t, err) expected := GithubActionTestMatrix{ Include: []TestSuitePair{ { - Suite: "TestTransferTestSuite", - Test: "TestC", + EntryPoint: "TestTransferTestSuite", + Test: "TestC", }, { - Suite: "TestFeeMiddlewareTestSuite", - Test: "TestA", + EntryPoint: "TestFeeMiddlewareTestSuite", + Test: "TestA", }, { - Suite: "TestFeeMiddlewareTestSuite", - Test: "TestB", + EntryPoint: "TestFeeMiddlewareTestSuite", + Test: "TestB", }, { - Suite: "TestTransferTestSuite", - Test: "TestD", + EntryPoint: "TestTransferTestSuite", + Test: "TestD", }, }, } @@ -77,11 +77,39 @@ func TestGetGithubActionMatrixForTests(t *testing.T) { assertGithubActionTestMatricesEqual(t, expected, gh) }) + t.Run("single test can be specified", func(t *testing.T) { + testingDir := t.TempDir() + createFileWithTestSuiteAndTests(t, "FeeMiddlewareTestSuite", "TestA", "TestB", testingDir, goTestFileNameOne) + createFileWithTestSuiteAndTests(t, "TransferTestSuite", "TestC", "TestD", testingDir, goTestFileNameTwo) + + gh, err := getGithubActionMatrixForTests(testingDir, "TestA", "TestFeeMiddlewareTestSuite", nil) + assert.NoError(t, err) + + expected := GithubActionTestMatrix{ + Include: []TestSuitePair{ + { + EntryPoint: "TestFeeMiddlewareTestSuite", + Test: "TestA", + }, + }, + } + + assertGithubActionTestMatricesEqual(t, expected, gh) + }) + + t.Run("error if single test doesn't exist", func(t *testing.T) { + testingDir := t.TempDir() + createFileWithTestSuiteAndTests(t, "FeeMiddlewareTestSuite", "TestA", "TestB", testingDir, goTestFileNameOne) + + _, err := getGithubActionMatrixForTests(testingDir, "TestThatDoesntExist", "TestFeeMiddlewareTestSuite", nil) + assert.Error(t, err) + }) + t.Run("non test files are not picked up", func(t *testing.T) { testingDir := t.TempDir() createFileWithTestSuiteAndTests(t, "FeeMiddlewareTestSuite", "TestA", "TestB", testingDir, nonTestFile) - gh, err := getGithubActionMatrixForTests(testingDir) + gh, err := getGithubActionMatrixForTests(testingDir, "", "", nil) assert.NoError(t, err) assert.Empty(t, gh.Include) }) @@ -102,10 +130,10 @@ func SuiteTwo(t *testing.T) { type FeeMiddlewareTestSuite struct {} ` - err := os.WriteFile(path.Join(testingDir, goTestFileNameOne), []byte(fileWithTwoSuites), os.FileMode(777)) + err := os.WriteFile(path.Join(testingDir, goTestFileNameOne), []byte(fileWithTwoSuites), os.FileMode(0o777)) assert.NoError(t, err) - _, err = getGithubActionMatrixForTests(testingDir) + _, err = getGithubActionMatrixForTests(testingDir, "", "", nil) assert.Error(t, err) }) } @@ -116,25 +144,24 @@ func assertGithubActionTestMatricesEqual(t *testing.T, expected, actual GithubAc sort.SliceStable(expected.Include, func(i, j int) bool { memberI := expected.Include[i] memberJ := expected.Include[j] - if memberI.Suite == memberJ.Suite { + if memberI.EntryPoint == memberJ.EntryPoint { return memberI.Test < memberJ.Test } - return memberI.Suite < memberJ.Suite + return memberI.EntryPoint < memberJ.EntryPoint }) sort.SliceStable(actual.Include, func(i, j int) bool { memberI := actual.Include[i] memberJ := actual.Include[j] - if memberI.Suite == memberJ.Suite { + if memberI.EntryPoint == memberJ.EntryPoint { return memberI.Test < memberJ.Test } - return memberI.Suite < memberJ.Suite + return memberI.EntryPoint < memberJ.EntryPoint }) assert.Equal(t, expected.Include, actual.Include) } func goTestFileContents(suiteName, fnName1, fnName2 string) string { - replacedSuiteName := strings.ReplaceAll(`package foo func TestSuiteName(t *testing.T) { @@ -157,6 +184,6 @@ func helper() {} func createFileWithTestSuiteAndTests(t *testing.T, suiteName, fn1Name, fn2Name, dir, filename string) { goFileContents := goTestFileContents(suiteName, fn1Name, fn2Name) - err := os.WriteFile(path.Join(dir, filename), []byte(goFileContents), os.FileMode(777)) + err := os.WriteFile(path.Join(dir, filename), []byte(goFileContents), os.FileMode(0o777)) assert.NoError(t, err) } diff --git a/cmd/determine_simd_tag/main.go b/cmd/determine_simd_tag/main.go deleted file mode 100644 index af3a6281268..00000000000 --- a/cmd/determine_simd_tag/main.go +++ /dev/null @@ -1,26 +0,0 @@ -package main - -import ( - "flag" - "fmt" -) - -var prNum string - -func init() { - flag.StringVar(&prNum, "pr", "", "the number of the pr") - flag.Parse() -} - -// in the context of a GithubAction workflow, the PR is non empty if it is a pr. When -// code is merged to main, it will be empty. In this case we just use the "main" tag. -func main() { - fmt.Printf(getSimdTag(prNum)) -} - -func getSimdTag(prNum string) string { - if prNum == "" { - return "main" - } - return fmt.Sprintf("pr-%s", prNum) -} diff --git a/contrib/devtools/Makefile b/contrib/devtools/Makefile new file mode 100644 index 00000000000..dc9eb0c8e3b --- /dev/null +++ b/contrib/devtools/Makefile @@ -0,0 +1,76 @@ +### +# Find OS and Go environment +# GO contains the Go binary +# FS contains the OS file separator +### +ifeq ($(OS),Windows_NT) + GO := $(shell where go.exe 2> NUL) + FS := "\\" +else + GO := $(shell command -v go 2> /dev/null) + FS := "/" +endif + +ifeq ($(GO),) + $(error could not find go. Is it in PATH? $(GO)) +endif + +############################################################################### +### Functions ### +############################################################################### + +go_get = $(if $(findstring Windows_NT,$(OS)),\ +IF NOT EXIST $(GITHUBDIR)$(FS)$(1)$(FS) ( mkdir $(GITHUBDIR)$(FS)$(1) ) else (cd .) &\ +IF NOT EXIST $(GITHUBDIR)$(FS)$(1)$(FS)$(2)$(FS) ( cd $(GITHUBDIR)$(FS)$(1) && git clone https://github.com/$(1)/$(2) ) else (cd .) &\ +,\ +mkdir -p $(GITHUBDIR)$(FS)$(1) &&\ +(test ! -d $(GITHUBDIR)$(FS)$(1)$(FS)$(2) && cd $(GITHUBDIR)$(FS)$(1) && git clone https://github.com/$(1)/$(2)) || true &&\ +)\ +cd $(GITHUBDIR)$(FS)$(1)$(FS)$(2) && git fetch origin && git checkout -q $(3) + +mkfile_path := $(abspath $(lastword $(MAKEFILE_LIST))) +mkfile_dir := $(shell cd $(shell dirname $(mkfile_path)); pwd) + + +############################################################################### +### Tools ### +############################################################################### + +PREFIX ?= /usr/local +BIN ?= $(PREFIX)/bin +UNAME_S ?= $(shell uname -s) +UNAME_M ?= $(shell uname -m) + +GOPATH ?= $(shell $(GO) env GOPATH) +GITHUBDIR := $(GOPATH)$(FS)src$(FS)github.com + +BUF_VERSION ?= 0.11.0 + +TOOLS_DESTDIR ?= $(GOPATH)/bin +STATIK = $(TOOLS_DESTDIR)/statik +RUNSIM = $(TOOLS_DESTDIR)/runsim + +tools: tools-stamp +tools-stamp: statik runsim + # Create dummy file to satisfy dependency and avoid + # rebuilding when this Makefile target is hit twice + # in a row. + touch $@ + +# Install the runsim binary +statik: $(STATIK) +$(STATIK): + @echo "Installing statik..." + @go install github.com/rakyll/statik@v0.1.6 + +# Install the runsim binary +runsim: $(RUNSIM) +$(RUNSIM): + @echo "Installing runsim..." + @go install github.com/cosmos/tools/cmd/runsim@v1.0.0 + +tools-clean: + rm -f $(STATIK) $(GOLANGCI_LINT) $(RUNSIM) + rm -f tools-stamp + +.PHONY: tools-clean statik runsim diff --git a/contrib/test_cover.sh b/contrib/test_cover.sh new file mode 100644 index 00000000000..24f7804b516 --- /dev/null +++ b/contrib/test_cover.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash +set -e + +PKGS=$(go list ./... | grep -v '/simapp') + +set -e +echo "mode: atomic" > coverage.txt +for pkg in ${PKGS[@]}; do + go test -v -timeout 30m -race -coverprofile=profile.out -covermode=atomic -tags='ledger test_ledger_mock' "$pkg" + if [ -f profile.out ]; then + tail -n +2 profile.out >> coverage.txt; + rm profile.out + fi +done diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js index f459a997da6..ba11c556e9e 100644 --- a/docs/.vuepress/config.js +++ b/docs/.vuepress/config.js @@ -100,6 +100,14 @@ module.exports = { label: "v2.3.0", key: "v2.3.0", }, + { + label: "v2.4.0", + key: "v2.4.0", + }, + { + label: "v2.5.0", + key: "v2.5.0", + }, { label: "v3.0.0", key: "v3.0.0", @@ -108,6 +116,50 @@ module.exports = { label: "v3.1.0", key: "v3.1.0", }, + { + label: "v3.2.0", + key: "v3.2.0", + }, + { + label: "v3.3.0", + key: "v3.3.0", + }, + { + label: "v3.4.0", + key: "v3.4.0", + }, + { + label: "v4.0.0", + key: "v4.0.0", + }, + { + label: "v4.1.0", + key: "v4.1.0", + }, + { + label: "v4.2.0", + key: "v4.2.0", + }, + { + label: "v4.3.0", + key: "v4.3.0", + }, + { + label: "v5.0.0", + key: "v5.0.0", + }, + { + label: "v5.1.0", + key: "v5.1.0", + }, + { + label: "v5.2.0", + key: "v5.2.0", + }, + { + label: "v6.1.0", + key: "v6.1.0", + }, ], topbar: { banner: true, @@ -179,29 +231,61 @@ module.exports = { path: "/apps/interchain-accounts/overview.html", }, { - title: "Authentication Modules", + title: "Development Use Cases", directory: false, - path: "/apps/interchain-accounts/auth-modules.html", + path: "/apps/interchain-accounts/development.html", }, { - title: "Active Channels", + title: "Authentication Modules", directory: false, - path: "/apps/interchain-accounts/active-channels.html", + path: "/apps/interchain-accounts/auth-modules.html", }, { title: "Integration", directory: false, path: "/apps/interchain-accounts/integration.html", }, + { + title: "Messages", + directory: false, + path: "/apps/interchain-accounts/messages.html", + }, { title: "Parameters", directory: false, path: "/apps/interchain-accounts/parameters.html", }, { - title: "Transactions", + title: "Client", directory: false, - path: "/apps/interchain-accounts/transactions.html", + path: "/apps/interchain-accounts/client.html", + }, + { + title: "Active Channels", + directory: false, + path: "/apps/interchain-accounts/active-channels.html", + }, + { + title: "Legacy", + directory: true, + path: "/apps/interchain-accounts", + children: [ + { + title: "Authentication Modules", + directory: false, + path: "/apps/interchain-accounts/legacy/auth-modules.html", + }, + { + title: "Integration", + directory: false, + path: "/apps/interchain-accounts/legacy/integration.html", + }, + { + title: "Keeper API", + directory: false, + path: "/apps/interchain-accounts/legacy/keeper-api.html", + }, + ] }, ], }, @@ -245,6 +329,95 @@ module.exports = { directory: false, path: "/apps/transfer/params.html", }, + { + title: "Authorizations", + directory: false, + path: "/apps/transfer/authorizations.html", + }, + ], + }, + ], + }, + { + title: "IBC Light Clients", + children: [ + { + title: "Developer Guide", + directory: true, + path: "/ibc/light-clients", + children: [ + { + title: "Overview", + directory: false, + path: "/ibc/light-clients/overview.html", + }, + { + title: "Client State interface", + directory: false, + path: "/ibc/light-clients/client-state.html", + }, + { + title: "Consensus State interface", + directory: false, + path: "/ibc/light-clients/consensus-state.html", + }, + { + title: "Handling Updates and Misbehaviour", + directory: false, + path: "/ibc/light-clients/updates-and-misbehaviour.html", + }, + { + title: "Handling Upgrades", + directory: false, + path: "/ibc/light-clients/upgrades.html", + }, + { + title: "Existence/Non-Existence Proofs", + directory: false, + path: "/ibc/light-clients/proofs.html", + }, + { + title: "Handling Proposals", + directory: false, + path: "/ibc/light-clients/proposals.html", + }, + { + title: "Handling Genesis", + directory: false, + path: "/ibc/light-clients/genesis.html", + }, + { + title: "Setup", + directory: false, + path: "/ibc/light-clients/setup.html", + }, + ] + }, + { + title: "Solomachine", + directory: true, + path: "/ibc/light-clients/solomachine", + children: [ + { + title: "Solomachine", + directory: false, + path: "/ibc/light-clients/solomachine/solomachine.html", + }, + { + title: "Concepts", + directory: false, + path: "/ibc/light-clients/solomachine/concepts.html", + }, + { + title: "State", + directory: false, + path: "/ibc/light-clients/solomachine/state.html", + }, + { + title: "State Transitions", + directory: false, + path: "/ibc/light-clients/solomachine/state_transitions.html", + }, ], }, ], @@ -320,6 +493,21 @@ module.exports = { directory: false, path: "/migrations/v3-to-v4.html", }, + { + title: "IBC-Go v4 to v5", + directory: false, + path: "/migrations/v4-to-v5.html", + }, + { + title: "IBC-Go v5 to v6", + directory: false, + path: "/migrations/v5-to-v6.html", + }, + { + title: "IBC-Go v6 to v7", + directory: false, + path: "/migrations/v6-to-v7.html", + }, ], }, { @@ -352,8 +540,8 @@ module.exports = { text: "Chat with IBC developers in Discord.", }, textLink: { - text: "ibcprotocol.org", - url: "https://ibcprotocol.org", + text: "ibcprotocol.dev", + url: "https://ibcprotocol.dev", }, services: [ { diff --git a/docs/DOCS_GUIDELINES.md b/docs/DOCS_GUIDELINES.md new file mode 100644 index 00000000000..3539d3e13aa --- /dev/null +++ b/docs/DOCS_GUIDELINES.md @@ -0,0 +1,41 @@ +# Documentation guidelines + +## Best practices + +- Check the spelling and grammar, even if you have to copy and paste from an external source. +- Use simple sentences. Easy-to-read sentences mean the reader can quickly use the guidance you share. +- Try to express your thoughts in a concise and clean way. +- Either Leave a space or use a `-` between the acronyms ADR and ICS and the corresponding number (e.g. ADR 008 or ADR-008, and ICS 27 or ICS-27). +- Don't overuse `code` format when writing in plain English. +- Follow Google developer documentation [style guide](https://developers.google.com/style). +- Check the meaning of words in Microsoft's [A-Z word list and term collections](https://docs.microsoft.com/en-us/style-guide/a-z-word-list-term-collections/term-collections/accessibility-terms) (use the search input!). +- We recommend using RFC keywords in user documentation (lowercase). The RFC keywords are: "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL. They are to be interpreted as described in [RFC 2119](https://datatracker.ietf.org/doc/html/rfc2119). +- Lint the markdown files for documentation with [markdownlint-cli](https://github.com/igorshubovych/markdownlint-cli). Run `make docs-lint` (you will need to have `markdownlint-cli` installed, so please follow the [installation instructions](https://github.com/igorshubovych/markdownlint-cli#installation)). + +### Links + +**NOTE:** Strongly consider the existing links (both within this directory and to the website docs) when moving or deleting files. + +Relative links should be used nearly everywhere, due to versioning. Note that in case of page reshuffling, you must update all links references. + +### Code snippets + +Code snippets can be included in the documentation using normal Markdown code blocks. For example: + +```md + ```go + func() {} + ``` +``` + +It is also possible to include code snippets from GitHub files by referencing the files directly (and the line numbers if needed). For example: + +```md + ```go reference + https://github.com/cosmos/ibc-go/blob/v5.0.0/modules/core/04-channel/keeper/handshake.go#L18-L65 + ``` +``` + +## Technical writing course + +Google provides a free [course](https://developers.google.com/tech-writing/overview) for technical writing. diff --git a/docs/DOCS_README.md b/docs/DOCS_README.md index fb6e4f2acc3..00a117f71b7 100644 --- a/docs/DOCS_README.md +++ b/docs/DOCS_README.md @@ -10,7 +10,7 @@ If you want to update the documentation please open a pr on ibc-go. ## Docs Build Workflow -The documentation for IBC-Go is hosted at https://ibc.cosmos.network. +The documentation for IBC-Go is hosted at . built from the files in this (`/docs`) directory for [main](https://github.com/cosmos/ibc-go/tree/main/docs). @@ -83,7 +83,7 @@ Install the theme and all dependencies. npm run serve ``` -Run `pre` and `post` hooks and start a hot-reloading web-server. See output of this command for the URL (it is often https://localhost:8080). +Run `pre` and `post` hooks and start a hot-reloading web-server. See output of this command for the URL (it is often ). To build documentation as a static website run `npm run build`. You will find the website in `.vuepress/dist` directory. @@ -96,19 +96,22 @@ 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 1. Execute the following command at the root directory to install the swagger-ui generate tool. + ```bash make tools ``` + 2. Edit API docs 1. Directly Edit API docs manually: `client/lcd/swagger-ui/swagger.yaml`. 2. Edit API docs within the [Swagger Editor](https://editor.swagger.io/). Please refer to this [document](https://swagger.io/docs/specification/2-0/basic-structure/) for the correct structure in `.yaml`. 3. Download `swagger.yaml` and replace the old `swagger.yaml` under fold `client/lcd/swagger-ui`. 4. Compile simd + ```bash make install ``` diff --git a/docs/OLD_README.md b/docs/OLD_README.md deleted file mode 100644 index 47bb2547732..00000000000 --- a/docs/OLD_README.md +++ /dev/null @@ -1,117 +0,0 @@ - - - -# `ibc` - -## Abstract - -This specification defines the implementation of the IBC protocol on the Cosmos SDK, the -changes made to the specification and where to find each specific ICS spec within -the module. - -For the general specification please refer to the [Interchain Standards](https://github.com/cosmos/ics). - -## Contents - -1. **Applications** - - 1.1. [Transfer](./../applications/transfer/spec/README.md) -2. **[Core](./../core/spec/README.md)** -3. **Light Clients** - - 3.1 [Solo Machine Client](./../light-clients/06-solomachine/spec/README.md) - - 3.2 [Tendermint Client](./../light-clients/07-tendermint/spec/README.md) - - 3.3 [Localhost Client](./../light-clients/09-localhost/spec/README.md) - -## Implementation Details - -As stated above, the IBC implementation on the Cosmos SDK introduces some changes -to the general specification, in order to avoid code duplication and to take -advantage of the SDK architectural components such as the transaction routing -through `Handlers`. - -### Interchain Standards reference - -The following list is a mapping from each Interchain Standard to their implementation -in the SDK's `x/ibc` module: - -* [ICS 002 - Client Semantics](https://github.com/cosmos/ics/tree/master/spec/ics-002-client-semantics): Implemented in [`x/ibc/core/02-client`](https://github.com/cosmos/tree/master/ibc/core/02-client) -* [ICS 003 - Connection Semantics](https://github.com/cosmos/ics/blob/master/spec/ics-003-connection-semantics): Implemented in [`x/ibc/core/03-connection`](https://github.com/cosmos/tree/master/ibc/core/03-connection) -* [ICS 004 - Channel and Packet Semantics](https://github.com/cosmos/ics/blob/master/spec/ics-004-channel-and-packet-semantics): Implemented in [`x/ibc/core/04-channel`](https://github.com/cosmos/tree/master/ibc/core/04-channel) -* [ICS 005 - Port Allocation](https://github.com/cosmos/ics/blob/master/spec/ics-005-port-allocation): Implemented in [`x/ibc/core/05-port`](https://github.com/cosmos/tree/master/ibc/core/05-port) -* [ICS 006 - Solo Machine Client](https://github.com/cosmos/ics/blob/master/spec/ics-006-solo-machine-client): Implemented in [`x/ibc/light-clients/06-solomachine`](https://github.com/cosmos/tree/master/ibc/solomachine) -* [ICS 007 - Tendermint Client](https://github.com/cosmos/ics/blob/master/spec/ics-007-tendermint-client): Implemented in [`x/ibc/light-clients/07-tendermint`](https://github.com/cosmos/tree/master/ibc/light-clients/07-tendermint) -* [ICS 009 - Loopback Client](https://github.com/cosmos/ics/blob/master/spec/ics-009-loopback-client): Implemented in [`x/ibc/light-clients/09-localhost`](https://github.com/cosmos/tree/master/ibc/light-clients/09-localhost) -* [ICS 018- Relayer Algorithms](https://github.com/cosmos/ics/tree/master/spec/ics-018-relayer-algorithms): Implemented in it's own [relayer repository](https://github.com/cosmos/relayer) -* [ICS 020 - Fungible Token Transfer](https://github.com/cosmos/ics/tree/master/spec/ics-020-fungible-token-transfer): Implemented in [`x/ibc/applications/transfer`](https://github.com/cosmos/tree/master/ibc/applications/transfer) -* [ICS 023 - Vector Commitments](https://github.com/cosmos/ics/tree/master/spec/ics-023-vector-commitments): Implemented in [`x/ibc/core/23-commitment`](https://github.com/cosmos/tree/master/ibc/core/23-commitment) -* [ICS 024 - Host Requirements](https://github.com/cosmos/ics/tree/master/spec/ics-024-host-requirements): Implemented in [`x/ibc/core/24-host`](https://github.com/cosmos/tree/master/ibc/core/24-host) -* [ICS 025 - Handler Interface](https://github.com/cosmos/ics/tree/master/spec/ics-025-handler-interface): `Handler` interfaces are implemented at the top level in `x/ibc/handler.go`, -which call each ICS submodule's handlers (i.e `x/ibc/*/{XX-ICS}/handler.go`). -* [ICS 026 - Routing Module](https://github.com/cosmos/ics/blob/master/spec/ics-026-routing-module): Replaced by [ADR 15 - IBC Packet Receiver](../../../docs/architecture/adr-015-ibc-packet-receiver.md). - -### Architecture Decision Records (ADR) - -The following ADR provide the design and architecture decision of IBC-related components. - -* [ADR 001 - Coin Source Tracing](../../../docs/architecture/adr-001-coin-source-tracing.md): standard to hash the ICS20's fungible token -denomination trace path in order to support special characters and limit the maximum denomination length. -* [ADR 17 - Historical Header Module](../../../docs/architecture/adr-017-historical-header-module.md): Introduces the ability to introspect past -consensus states in order to verify their membership in the counterparty clients. -* [ADR 19 - Protobuf State Encoding](../../../docs/architecture/adr-019-protobuf-state-encoding.md): Migration from Amino to Protobuf for state encoding. -* [ADR 020 - Protocol Buffer Transaction Encoding](./../../docs/architecture/adr-020-protobuf-transaction-encoding.md): Client side migration to Protobuf. -* [ADR 021 - Protocol Buffer Query Encoding](../../../docs/architecture/adr-020-protobuf-query-encoding.md): Queries migration to Protobuf. -* [ADR 026 - IBC Client Recovery Mechanisms](../../../docs/architecture/adr-026-ibc-client-recovery-mechanisms.md): Allows IBC Clients to be recovered after freezing or expiry. -* [ADR 027 - IBC WASM Client](../../../docs/architecture/adr-027-ibc-wasm.md) - -### SDK Modules - -* [`x/capability`](https://github.com/cosmos/tree/master/x/capability): The capability module provides object-capability keys support through scoped keepers in order to authenticate usage of ports or channels. Check [ADR 3 - Dynamic Capability Store](../../../docs/architecture/adr-003-dynamic-capability-store.md) for more details. - -## IBC module architecture - -> **NOTE for auditors**: If you're not familiar with the overall module structure from -the SDK modules, please check this [document](../../../docs/building-modules/structure.md) as -prerequisite reading. - -For ease of auditing, every Interchain Standard has been developed in its own -package. The development team separated the IBC TAO (Transport, Authentication, Ordering) ICS specifications from the IBC application level -specification. The following tree describes the architecture of the directories that -the `ibc` (TAO) and `ibc-transfer` ([ICS20](https://github.com/cosmos/ics/tree/master/spec/ics-020-fungible-token-transfer)) modules: - -```shell -x/ibc -├── applications/ -│ └──transfer/ -├── core/ -│   ├── 02-client/ -│   ├── 03-connection/ -│   ├── 04-channel/ -│   ├── 05-port/ -│   ├── 23-commitment/ -│   ├── 24-host/ -│  ├── client -│  │   └── cli -│ │       └── cli.go -│  ├── keeper -│  │ ├── keeper.go -│   │ └── querier.go -│ ├── types -│ │ ├── errors.go -│ │ └── keys.go -│ ├── handler.go -│ └── module.go -├── light-clients/ -│   ├── 06-solomachine/ -│   ├── 07-tendermint/ -│   └── 09-localhost/ -└── testing/ -``` - \ No newline at end of file diff --git a/docs/README.md b/docs/README.md index b1c4855520f..23f5ac5f341 100644 --- a/docs/README.md +++ b/docs/README.md @@ -8,11 +8,10 @@ footer: Welcome to the IBC-Go documentation! -The Inter-Blockchain Communication protocol (IBC) is an end-to-end, connection-oriented, stateful protocol for reliable, ordered, and authenticated communication between heterogeneous blockchains arranged in an unknown and dynamic topology. +The Inter-Blockchain Communication protocol (IBC) is an end-to-end, connection-oriented, stateful protocol for reliable, ordered, and authenticated communication between heterogeneous blockchains arranged in an unknown and dynamic topology. IBC is a protocol that allows blockchains to talk to each other. The protocol realizes this interoperability by specifying a set of data structures, abstractions, and semantics that can be implemented by any distributed ledger that satisfies a small set of requirements. IBC can be used to build a wide range of cross-chain applications that include token transfers, atomic swaps, multi-chain smart contracts (with or without mutually comprehensible VMs), and data and code sharding of various kinds. - diff --git a/docs/apps/interchain-accounts/active-channels.md b/docs/apps/interchain-accounts/active-channels.md index d387bdea038..f9d457f1a85 100644 --- a/docs/apps/interchain-accounts/active-channels.md +++ b/docs/apps/interchain-accounts/active-channels.md @@ -1,25 +1,36 @@ -# Understanding Active Channels +# Understanding Active Channels -The Interchain Accounts module uses [ORDERED channels](https://github.com/cosmos/ibc/tree/master/spec/core/ics-004-channel-and-packet-semantics#ordering) to maintain the order of transactions when sending packets from a controller to a host chain. A limitation when using ORDERED channels is that when a packet times out the channel will be closed. +The Interchain Accounts module uses [ORDERED channels](https://github.com/cosmos/ibc/tree/master/spec/core/ics-004-channel-and-packet-semantics#ordering) to maintain the order of transactions when sending packets from a controller to a host chain. A limitation when using ORDERED channels is that when a packet times out the channel will be closed. -In the case of a channel closing, a controller chain needs to be able to regain access to the interchain account registered on this channel. `Active Channels` enable this functionality. Future versions of the ICS-27 protocol and the Interchain Accounts module will likely use a new -channel type that provides ordering of packets without the channel closing on timing out, thus removing the need for `Active Channels` entirely. +In the case of a channel closing, a controller chain needs to be able to regain access to the interchain account registered on this channel. `Active Channels` enable this functionality. -When an Interchain Account is registered using the `RegisterInterchainAccount` API, a new channel is created on a particular port. During the `OnChanOpenAck` and `OnChanOpenConfirm` steps (controller & host chain) the `Active Channel` for this interchain account -is stored in state. +When an Interchain Account is registered using `MsgRegisterInterchainAccount`, a new channel is created on a particular port. During the `OnChanOpenAck` and `OnChanOpenConfirm` steps (on controller & host chain respectively) the `Active Channel` for this interchain account is stored in state. It is possible to create a new channel using the same controller chain portID if the previously set `Active Channel` is now in a `CLOSED` state. This channel creation can be initialized programatically by sending a new `MsgChannelOpenInit` message like so: ```go -msg := channeltypes.NewMsgChannelOpenInit(portID, string(versionBytes), channeltypes.ORDERED, []string{connectionID}, icatypes.PortID, icatypes.ModuleName) -handler := k.msgRouter.Handler(msg) +msg := channeltypes.NewMsgChannelOpenInit(portID, string(versionBytes), channeltypes.ORDERED, []string{connectionID}, icatypes.HostPortID, authtypes.NewModuleAddress(icatypes.ModuleName).String()) +handler := keeper.msgRouter.Handler(msg) +res, err := handler(ctx, msg) +if err != nil { + return err +} ``` Alternatively, any relayer operator may initiate a new channel handshake for this interchain account once the previously set `Active Channel` is in a `CLOSED` state. This is done by initiating the channel handshake on the controller chain using the same portID associated with the interchain account in question. -It is important to note that once a channel has been opened for a given Interchain Account, new channels can not be opened for this account until the currently set `Active Channel` is set to `CLOSED`. +It is important to note that once a channel has been opened for a given interchain account, new channels can not be opened for this account until the currently set `Active Channel` is set to `CLOSED`. +## Future improvements + +Future versions of the ICS-27 protocol and the Interchain Accounts module will likely use a new channel type that provides ordering of packets without the channel closing in the event of a packet timing out, thus removing the need for `Active Channels` entirely. +The following is a list of issues which will provide the infrastructure to make this possible: + +- [IBC Channel Upgrades](https://github.com/cosmos/ibc-go/issues/1599) +- [Implement ORDERED_ALLOW_TIMEOUT logic in 04-channel](https://github.com/cosmos/ibc-go/issues/1661) +- [Add ORDERED_ALLOW_TIMEOUT as supported ordering in 03-connection](https://github.com/cosmos/ibc-go/issues/1662) +- [Allow ICA channels to be opened as ORDERED_ALLOW_TIMEOUT](https://github.com/cosmos/ibc-go/issues/1663) diff --git a/docs/apps/interchain-accounts/auth-modules.md b/docs/apps/interchain-accounts/auth-modules.md index 5d75409b3c4..c53230f69d8 100644 --- a/docs/apps/interchain-accounts/auth-modules.md +++ b/docs/apps/interchain-accounts/auth-modules.md @@ -1,393 +1,21 @@ # Building an authentication module -Authentication modules play the role of the `Base Application` as described in [ICS30 IBC Middleware](https://github.com/cosmos/ibc/tree/master/spec/app/ics-030-middleware), and enable application developers to perform custom logic when working with the Interchain Accounts controller API. {synopsis} +Authentication modules enable application developers to perform custom logic when interacting with the Interchain Accounts controller sumbmodule's `MsgServer`. {synopsis} -The controller submodule is used for account registration and packet sending. -It executes only logic required of all controllers of interchain accounts. -The type of authentication used to manage the interchain accounts remains unspecified. -There may exist many different types of authentication which are desirable for different use cases. -Thus the purpose of the authentication module is to wrap the controller module with custom authentication logic. +The controller submodule is used for account registration and packet sending. It executes only logic required of all controllers of interchain accounts. The type of authentication used to manage the interchain accounts remains unspecified. There may exist many different types of authentication which are desirable for different use cases. Thus the purpose of the authentication module is to wrap the controller submodule with custom authentication logic. -In ibc-go, authentication modules are connected to the controller chain via a middleware stack. -The controller module is implemented as [middleware](https://github.com/cosmos/ibc/tree/master/spec/app/ics-030-middleware) and the authentication module is connected to the controller module as the base application of the middleware stack. -To implement an authentication module, the `IBCModule` interface must be fulfilled. -By implementing the controller module as middleware, any amount of authentication modules can be created and connected to the controller module without writing redundant code. +In ibc-go, authentication modules can communicate with the controller submodule by passing messages through `baseapp`'s `MsgServiceRouter`. To implement an authentication module, the `IBCModule` interface need not be fulfilled; it is only required to fulfill Cosmos SDK's `AppModuleBasic` interface, just like any regular Cosmos SDK application module. The authentication module must: -- Authenticate interchain account owners -- Track the associated interchain account address for an owner -- Claim the channel capability in `OnChanOpenInit` -- Send packets on behalf of an owner (after authentication) -### IBCModule implementation +- Authenticate interchain account owners. +- Track the associated interchain account address for an owner. +- Send packets on behalf of an owner (after authentication). -The following `IBCModule` callbacks must be implemented with appropriate custom logic: +## Integration into `app.go` file -```go -// OnChanOpenInit implements the IBCModule interface -func (im IBCModule) OnChanOpenInit( - ctx sdk.Context, - order channeltypes.Order, - connectionHops []string, - portID string, - channelID string, - chanCap *capabilitytypes.Capability, - counterparty channeltypes.Counterparty, - version string, -) (string, error) { - // the authentication module *must* claim the channel capability on OnChanOpenInit - if err := im.keeper.ClaimCapability(ctx, chanCap, host.ChannelCapabilityPath(portID, channelID)); err != nil { - return version, err - } - - // perform custom logic - - return version, nil -} - -// OnChanOpenAck implements the IBCModule interface -func (im IBCModule) OnChanOpenAck( - ctx sdk.Context, - portID, - channelID string, - counterpartyVersion string, -) error { - // perform custom logic - - return nil -} - -// OnChanCloseConfirm implements the IBCModule interface -func (im IBCModule) OnChanCloseConfirm( - ctx sdk.Context, - portID, - channelID string, -) error { - // perform custom logic - - return nil -} - -// OnAcknowledgementPacket implements the IBCModule interface -func (im IBCModule) OnAcknowledgementPacket( - ctx sdk.Context, - packet channeltypes.Packet, - acknowledgement []byte, - relayer sdk.AccAddress, -) error { - // perform custom logic - - return nil -} - -// OnTimeoutPacket implements the IBCModule interface. -func (im IBCModule) OnTimeoutPacket( - ctx sdk.Context, - packet channeltypes.Packet, - relayer sdk.AccAddress, -) error { - // perform custom logic - - return nil -} -``` - -**Note**: The channel capability must be claimed by the authentication module in `OnChanOpenInit` otherwise the authentication module will not be able to send packets on the channel created for the associated interchain account. - -The following functions must be defined to fulfill the `IBCModule` interface, but they will never be called by the controller module so they may error or panic. - -```go -// OnChanOpenTry implements the IBCModule interface -func (im IBCModule) OnChanOpenTry( - ctx sdk.Context, - order channeltypes.Order, - connectionHops []string, - portID, - channelID string, - chanCap *capabilitytypes.Capability, - counterparty channeltypes.Counterparty, - counterpartyVersion string, -) (string, error) { - panic("UNIMPLEMENTED") -} - -// OnChanOpenConfirm implements the IBCModule interface -func (im IBCModule) OnChanOpenConfirm( - ctx sdk.Context, - portID, - channelID string, -) error { - panic("UNIMPLEMENTED") -} - -// OnChanCloseInit implements the IBCModule interface -func (im IBCModule) OnChanCloseInit( - ctx sdk.Context, - portID, - channelID string, -) error { - panic("UNIMPLEMENTED") -} - -// OnRecvPacket implements the IBCModule interface. A successful acknowledgement -// is returned if the packet data is succesfully decoded and the receive application -// logic returns without error. -func (im IBCModule) OnRecvPacket( - ctx sdk.Context, - packet channeltypes.Packet, - relayer sdk.AccAddress, -) ibcexported.Acknowledgement { - panic("UNIMPLEMENTED") -} -``` - -## `RegisterInterchainAccount` - -The authentication module can begin registering interchain accounts by calling `RegisterInterchainAccount`: - -```go -if err := keeper.icaControllerKeeper.RegisterInterchainAccount(ctx, connectionID, owner.String(), version); err != nil { - return err -} - -return nil -``` - -The `version` argument is used to support ICS29 fee middleware for relayer incentivization of ICS27 packets. Consumers of the `RegisterInterchainAccount` are expected to build the appropriate JSON encoded version string themselves and pass it accordingly. If an empty string is passed in the `version` argument, then the version will be initialized to a default value in the `OnChanOpenInit` callback of the controller's handler, so that channel handshake can proceed. - -The following code snippet illustrates how to construct an appropriate interchain accounts `Metadata` and encode it as a JSON bytestring: - -```go -icaMetadata := icatypes.Metadata{ - Version: icatypes.Version, - ControllerConnectionId: controllerConnectionID, - HostConnectionId: hostConnectionID, - Encoding: icatypes.EncodingProtobuf, - TxType: icatypes.TxTypeSDKMultiMsg, -} - -appVersion, err := icatypes.ModuleCdc.MarshalJSON(&icaMetadata) -if err != nil { - return err -} - -if err := keeper.icaControllerKeeper.RegisterInterchainAccount(ctx, controllerConnectionID, owner.String(), string(appVersion)); err != nil { - return err -} -``` - -Similarly, if the application stack is configured to route through ICS29 fee middleware and a fee enabled channel is desired, construct the appropriate ICS29 `Metadata` type: - -```go -icaMetadata := icatypes.Metadata{ - Version: icatypes.Version, - ControllerConnectionId: controllerConnectionID, - HostConnectionId: hostConnectionID, - Encoding: icatypes.EncodingProtobuf, - TxType: icatypes.TxTypeSDKMultiMsg, -} - -appVersion, err := icatypes.ModuleCdc.MarshalJSON(&icaMetadata) -if err != nil { - return err -} - -feeMetadata := feetypes.Metadata{ - AppVersion: string(appVersion), - FeeVersion: feetypes.Version, -} - -feeEnabledVersion, err := feetypes.ModuleCdc.MarshalJSON(&feeMetadata) -if err != nil { - return err -} - -if err := keeper.icaControllerKeeper.RegisterInterchainAccount(ctx, controllerConnectionID, owner.String(), string(feeEnabledVersion)); err != nil { - return err -} -``` - -## `SendTx` - -The authentication module can attempt to send a packet by calling `SendTx`: -```go - -// Authenticate owner -// perform custom logic - -// Construct controller portID based on interchain account owner address -portID, err := icatypes.NewControllerPortID(owner.String()) -if err != nil { - return err -} - -channelID, found := keeper.icaControllerKeeper.GetActiveChannelID(ctx, portID) -if !found { - return sdkerrors.Wrapf(icatypes.ErrActiveChannelNotFound, "failed to retrieve active channel for port %s", portID) -} - -// Obtain the channel capability, claimed in OnChanOpenInit -chanCap, found := keeper.scopedKeeper.GetCapability(ctx, host.ChannelCapabilityPath(portID, channelID)) -if !found { - return sdkerrors.Wrap(channeltypes.ErrChannelCapabilityNotFound, "module does not own channel capability") -} - -// Obtain data to be sent to the host chain. -// In this example, the owner of the interchain account would like to send a bank MsgSend to the host chain. -// The appropriate serialization function should be called. The host chain must be able to deserialize the transaction. -// If the host chain is using the ibc-go host module, `SerializeCosmosTx` should be used. -msg := &banktypes.MsgSend{FromAddress: fromAddr, ToAddress: toAddr, Amount: amt} -data, err := icatypes.SerializeCosmosTx(keeper.cdc, []sdk.Msg{msg}) -if err != nil { - return err -} - -// Construct packet data -packetData := icatypes.InterchainAccountPacketData{ - Type: icatypes.EXECUTE_TX, - Data: data, -} - -// Obtain timeout timestamp -// An appropriate timeout timestamp must be determined based on the usage of the interchain account. -// If the packet times out, the channel will be closed requiring a new channel to be created -timeoutTimestamp := obtainTimeoutTimestamp() - -// Send the interchain accounts packet, returning the packet sequence -seq, err = keeper.icaControllerKeeper.SendTx(ctx, chanCap, portID, packetData, timeoutTimestamp) -``` - -The data within an `InterchainAccountPacketData` must be serialized using a format supported by the host chain. -If the host chain is using the ibc-go host chain submodule, `SerializeCosmosTx` should be used. If the `InterchainAccountPacketData.Data` is serialized using a format not support by the host chain, the packet will not be successfully received. - -## `OnAcknowledgementPacket` - -Controller chains will be able to access the acknowledgement written into the host chain state once a relayer relays the acknowledgement. -The acknowledgement bytes will be passed to the auth module via the `OnAcknowledgementPacket` callback. -Auth modules are expected to know how to decode the acknowledgement. - -If the controller chain is connected to a host chain using the host module on ibc-go, it may interpret the acknowledgement bytes as follows: - -Begin by unmarshaling the acknowledgement into sdk.TxMsgData: -```go -var ack channeltypes.Acknowledgement -if err := channeltypes.SubModuleCdc.UnmarshalJSON(acknowledgement, &ack); err != nil { - return err -} - -txMsgData := &sdk.TxMsgData{} -if err := proto.Unmarshal(ack.GetResult(), txMsgData); err != nil { - return err -} -``` - -If the txMsgData.Data field is non nil, the host chain is using SDK version <= v0.45. -The auth module should interpret the txMsgData.Data as follows: - -```go -switch len(txMsgData.Data) { -case 0: - // see documentation below for SDK 0.46.x or greater -default: - for _, msgData := range txMsgData.Data { - if err := handler(msgData); err != nil { - return err - } - } -... -} -``` - -A handler will be needed to interpret what actions to perform based on the message type sent. -A router could be used, or more simply a switch statement. - -```go -func handler(msgData sdk.MsgData) error { -switch msgData.MsgType { -case sdk.MsgTypeURL(&banktypes.MsgSend{}): - msgResponse := &banktypes.MsgSendResponse{} - if err := proto.Unmarshal(msgData.Data, msgResponse}; err != nil { - return err - } - - handleBankSendMsg(msgResponse) - -case sdk.MsgTypeURL(&stakingtypes.MsgDelegate{}): - msgResponse := &stakingtypes.MsgDelegateResponse{} - if err := proto.Unmarshal(msgData.Data, msgResponse}; err != nil { - return err - } - - handleStakingDelegateMsg(msgResponse) - -case sdk.MsgTypeURL(&transfertypes.MsgTransfer{}): - msgResponse := &transfertypes.MsgTransferResponse{} - if err := proto.Unmarshal(msgData.Data, msgResponse}; err != nil { - return err - } - - handleIBCTransferMsg(msgResponse) - -default: - return -} -``` - -If the txMsgData.Data is empty, the host chain is using SDK version > v0.45. -The auth module should interpret the txMsgData.Responses as follows: - -```go -... -// switch statement from above -case 0: - for _, any := range txMsgData.MsgResponses { - if err := handleAny(any); err != nil { - return err - } - } -} -``` - -A handler will be needed to interpret what actions to perform based on the type url of the Any. -A router could be used, or more simply a switch statement. -It may be possible to deduplicate logic between `handler` and `handleAny`. - -```go -func handleAny(any *codectypes.Any) error { -switch any.TypeURL { -case banktypes.MsgSend: - msgResponse, err := unpackBankMsgSendResponse(any) - if err != nil { - return err - } - - handleBankSendMsg(msgResponse) - -case stakingtypes.MsgDelegate: - msgResponse, err := unpackStakingDelegateResponse(any) - if err != nil { - return err - } - - handleStakingDelegateMsg(msgResponse) - - case transfertypes.MsgTransfer: - msgResponse, err := unpackIBCTransferMsgResponse(any) - if err != nil { - return err - } - - handleIBCTransferMsg(msgResponse) - -default: - return -} -``` - -### Integration into `app.go` file - -To integrate the authentication module into your chain, please follow the steps outlined above in [app.go integration](./integration.md#example-integration). +To integrate the authentication module into your chain, please follow the steps outlined in [`app.go` integration](./integration.md#example-integration). diff --git a/docs/apps/interchain-accounts/client.md b/docs/apps/interchain-accounts/client.md new file mode 100644 index 00000000000..c35d28f0df0 --- /dev/null +++ b/docs/apps/interchain-accounts/client.md @@ -0,0 +1,180 @@ + + +# Client + +## CLI + +A user can query and interact with the Interchain Accounts module using the CLI. Use the `--help` flag to discover the available commands: + +```shell +simd query interchain-accounts --help +``` + +> Please not that this section does not document all the available commands, but only the ones that deserved extra documentation that was not possible to fit in the command line documentation. + +### Controller + +A user can query and interact with the controller submodule. + +#### Query + +The `query` commands allow users to query the controller submodule. + +```shell +simd query interchain-accounts controller --help +``` + +#### Transactions + +The `tx` commands allow users to interact with the controller submodule. + +```shell +simd tx interchain-accounts controller --help +``` + +#### `send-tx` + +The `send-tx` command allows users to send a transaction on the provided connection to be executed using an interchain account on the host chain. + +```shell +simd tx interchain-accounts controller send-tx [connection-id] [path/to/packet_msg.json] +``` + +Example: + +```shell +simd tx interchain-accounts controller send-tx connection-0 packet-data.json --from cosmos1.. +``` + +See below for example contents of `packet-data.json`. The CLI handler will unmarshal the following into `InterchainAccountPacketData` appropriately. + +```json +{ + "type":"TYPE_EXECUTE_TX", + "data":"CqIBChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEoEBCkFjb3Ntb3MxNWNjc2hobXAwZ3N4MjlxcHFxNmc0em1sdG5udmdteXU5dWV1YWRoOXkybmM1emowc3psczVndGRkehItY29zbW9zMTBoOXN0YzV2Nm50Z2V5Z2Y1eGY5NDVuanFxNWgzMnI1M3VxdXZ3Gg0KBXN0YWtlEgQxMDAw", + "memo":"" +} +``` + +Note the `data` field is a base64 encoded byte string as per the [proto3 JSON encoding specification](https://developers.google.com/protocol-buffers/docs/proto3#json). + +A helper CLI is provided in the host submodule which can be used to generate the packet data JSON using the counterparty chain's binary. See the [`generate-packet-data` command](#generate-packet-data) for an example. + +### Host + +A user can query and interact with the host submodule. + +#### Query + +The `query` commands allow users to query the host submodule. + +```shell +simd query interchain-accounts host --help +``` + +#### Transactions + +The `tx` commands allow users to interact with the controller submodule. + +```shell +simd tx interchain-accounts host --help +``` + +##### `generate-packet-data` + +The `generate-packet-data` command allows users to generate interchain accounts packet data for input message(s). The packet data can then be used with the controller submodule's [`send-tx` command](#send-tx). + +```shell +simd tx interchain-accounts host generate-packet-data [message] +``` + +Example: + +```shell +simd tx interchain-accounts host generate-packet-data '[{ + "@type":"/cosmos.bank.v1beta1.MsgSend", + "from_address":"cosmos15ccshhmp0gsx29qpqq6g4zmltnnvgmyu9ueuadh9y2nc5zj0szls5gtddz", + "to_address":"cosmos10h9stc5v6ntgeygf5xf945njqq5h32r53uquvw", + "amount": [ + { + "denom": "stake", + "amount": "1000" + } + ] +}]' --memo memo +``` + +The command accepts a single `sdk.Msg` or a list of `sdk.Msg`s that will be encoded into the outputs `data` field. + +Example output: + +```json +{ + "type":"TYPE_EXECUTE_TX", + "data":"CqIBChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEoEBCkFjb3Ntb3MxNWNjc2hobXAwZ3N4MjlxcHFxNmc0em1sdG5udmdteXU5dWV1YWRoOXkybmM1emowc3psczVndGRkehItY29zbW9zMTBoOXN0YzV2Nm50Z2V5Z2Y1eGY5NDVuanFxNWgzMnI1M3VxdXZ3Gg0KBXN0YWtlEgQxMDAw", + "memo":"memo" +} +``` + +## gRPC + +A user can query the interchain account module using gRPC endpoints. + +### Controller + +A user can query the controller submodule using gRPC endpoints. + +#### `InterchainAccount` + +The `InterchainAccount` endpoint allows users to query the controller submodule for the interchain account address for a given owner on a particular connection. + +```shell +ibc.applications.interchain_accounts.controller.v1.Query/InterchainAccount +``` + +Example: + +```shell +grpcurl -plaintext \ + -d '{"owner":"cosmos1..","connection_id":"connection-0"}' \ + localhost:9090 \ + ibc.applications.interchain_accounts.controller.v1.Query/InterchainAccount +``` + +#### `Params` + +The `Params` endpoint users to query the current controller submodule parameters. + +```shell +ibc.applications.interchain_accounts.controller.v1.Query/Params +``` + +Example: + +```shell +grpcurl -plaintext \ + localhost:9090 \ + ibc.applications.interchain_accounts.controller.v1.Query/Params +``` + +### Host + +A user can query the host submodule using gRPC endpoints. + +#### `Params` + +The `Params` endpoint users to query the current host submodule parameters. + +```shell +ibc.applications.interchain_accounts.host.v1.Query/Params +``` + +Example: + +```shell +grpcurl -plaintext \ + localhost:9090 \ + ibc.applications.interchain_accounts.host.v1.Query/Params +``` diff --git a/docs/apps/interchain-accounts/development.md b/docs/apps/interchain-accounts/development.md new file mode 100644 index 00000000000..f2a463868a5 --- /dev/null +++ b/docs/apps/interchain-accounts/development.md @@ -0,0 +1,36 @@ + + +# Development use cases + +The initial version of Interchain Accounts allowed for the controller submodule to be extended by providing it with an underlying application which would handle all packet callbacks. +That functionality is now being deprecated in favor of alternative approaches. +This document will outline potential use cases and redirect each use case to the appropriate documentation. + +## Custom authentication + +Interchain accounts may be associated with alternative types of authentication relative to the traditional public/private key signing. +If you wish to develop or use Interchain Accounts with a custom authentication module and do not need to execute custom logic on the packet callbacks, we recommend you use ibc-go v6 or greater and that your custom authentication module interacts with the controller submodule via the [`MsgServer`](./messages.md). + +If you wish to consume and execute custom logic in the packet callbacks, then please read the section [Packet callbacks](#packet-callbacks) below. + +## Redirection to a smart contract + +It may be desirable to allow smart contracts to control an interchain account. +To faciliate such an action, the controller submodule may be provided an underlying application which redirects to smart contract callers. +An improved design has been suggested in [ADR 008](https://github.com/cosmos/ibc-go/pull/1976) which performs this action via middleware. + +Implementors of this use case are recommended to follow the ADR 008 approach. +The underlying application may continue to be used as a short term solution for ADR 008 and the [legacy API](./auth-modules.md#registerinterchainaccount) should continue to be utilized in such situations. + +## Packet callbacks + +If a developer requires access to packet callbacks for their use case, then they have the following options: + +1. Write a smart contract which is connected via an ADR 008 or equivalent IBC application (recommended). +2. Use the controller's underlying application to implement packet callback logic. + +In the first case, the smart contract should use the [`MsgServer`](./messages.md). + +In the second case, the underlying application should use the [legacy API](./legacy/keeper-api.md). diff --git a/docs/apps/interchain-accounts/integration.md b/docs/apps/interchain-accounts/integration.md index 14757ccca3c..4d56e3c1106 100644 --- a/docs/apps/interchain-accounts/integration.md +++ b/docs/apps/interchain-accounts/integration.md @@ -1,19 +1,20 @@ # Integration Learn how to integrate Interchain Accounts host and controller functionality to your chain. The following document only applies for Cosmos SDK chains. {synopsis} -The Interchain Accounts module contains two submodules. Each submodule has its own IBC application. The Interchain Accounts module should be registered as an `AppModule` in the same way all SDK modules are registered on a chain, but each submodule should create its own `IBCModule` as necessary. A route should be added to the IBC router for each submodule which will be used. +The Interchain Accounts module contains two submodules. Each submodule has its own IBC application. The Interchain Accounts module should be registered as an `AppModule` in the same way all SDK modules are registered on a chain, but each submodule should create its own `IBCModule` as necessary. A route should be added to the IBC router for each submodule which will be used. -Chains who wish to support ICS27 may elect to act as a host chain, a controller chain or both. Disabling host or controller functionality may be done statically by excluding the host or controller module entirely from the `app.go` file or it may be done dynamically by taking advantage of the on-chain parameters which enable or disable the host or controller submodules. +Chains who wish to support ICS-27 may elect to act as a host chain, a controller chain or both. Disabling host or controller functionality may be done statically by excluding the host or controller submodule entirely from the `app.go` file or it may be done dynamically by taking advantage of the on-chain parameters which enable or disable the host or controller submodules. -Interchain Account authentication modules are the base application of a middleware stack. The controller submodule is the middleware in this stack. +Interchain Account authentication modules (both custom or generic, such as the `x/gov`, `x/group` or `x/auth` Cosmos SDK modules) can send messages to the controller submodule's [`MsgServer`](./messages.md) to register interchain accounts and send packets to the interchain account. To accomplish this, the authentication module needs to be composed with `baseapp`'s `MsgServiceRouter`. +![ICAv6](../../assets/ica/ica-v6.png) -### Example integration +## Example integration ```go // app.go @@ -21,10 +22,10 @@ Interchain Account authentication modules are the base application of a middlewa // Register the AppModule for the Interchain Accounts module and the authentication module // Note: No `icaauth` exists, this must be substituted with an actual Interchain Accounts authentication module ModuleBasics = module.NewBasicManager( - ... - ica.AppModuleBasic{}, - icaauth.AppModuleBasic{}, - ... + ... + ica.AppModuleBasic{}, + icaauth.AppModuleBasic{}, + ... ) ... @@ -33,8 +34,8 @@ ModuleBasics = module.NewBasicManager( // Only necessary for host chain functionality // Each Interchain Account created on the host chain is derived from the module account created maccPerms = map[string][]string{ - ... - icatypes.ModuleName: nil, + ... + icatypes.ModuleName: nil, } ... @@ -42,24 +43,24 @@ maccPerms = map[string][]string{ // Add Interchain Accounts Keepers for each submodule used and the authentication module // If a submodule is being statically disabled, the associated Keeper does not need to be added. type App struct { - ... + ... - ICAControllerKeeper icacontrollerkeeper.Keeper - ICAHostKeeper icahostkeeper.Keeper - ICAAuthKeeper icaauthkeeper.Keeper + ICAControllerKeeper icacontrollerkeeper.Keeper + ICAHostKeeper icahostkeeper.Keeper + ICAAuthKeeper icaauthkeeper.Keeper - ... + ... } ... // Create store keys for each submodule Keeper and the authentication module keys := sdk.NewKVStoreKeys( - ... - icacontrollertypes.StoreKey, - icahosttypes.StoreKey, - icaauthtypes.StoreKey, - ... + ... + icacontrollertypes.StoreKey, + icahosttypes.StoreKey, + icaauthtypes.StoreKey, + ... ) ... @@ -73,55 +74,84 @@ scopedICAAuthKeeper := app.CapabilityKeeper.ScopeToModule(icaauthtypes.ModuleNam // Create the Keeper for each submodule 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(), + 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, + scopedICAControllerKeeper, app.MsgServiceRouter(), ) app.ICAHostKeeper = icahostkeeper.NewKeeper( - appCodec, keys[icahosttypes.StoreKey], app.GetSubspace(icahosttypes.SubModuleName), - app.IBCKeeper.ChannelKeeper, &app.IBCKeeper.PortKeeper, - app.AccountKeeper, scopedICAHostKeeper, app.MsgServiceRouter(), + appCodec, keys[icahosttypes.StoreKey], app.GetSubspace(icahosttypes.SubModuleName), + app.IBCKeeper.ChannelKeeper, // may be replaced with middleware such as ics29 fee + app.IBCKeeper.ChannelKeeper, &app.IBCKeeper.PortKeeper, + app.AccountKeeper, scopedICAHostKeeper, app.MsgServiceRouter(), ) // Create Interchain Accounts AppModule icaModule := ica.NewAppModule(&app.ICAControllerKeeper, &app.ICAHostKeeper) // Create your Interchain Accounts authentication module -app.ICAAuthKeeper = icaauthkeeper.NewKeeper(appCodec, keys[icaauthtypes.StoreKey], app.ICAControllerKeeper, scopedICAAuthKeeper) +app.ICAAuthKeeper = icaauthkeeper.NewKeeper(appCodec, keys[icaauthtypes.StoreKey], app.MsgServiceRouter()) // ICA auth AppModule 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(nil, 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) ... // Register Interchain Accounts and authentication module AppModule's app.moduleManager = module.NewManager( - ... - icaModule, - icaAuthModule, + ... + icaModule, + icaAuthModule, ) ... +// Add Interchain Accounts to begin blocker logic +app.moduleManager.SetOrderBeginBlockers( + ... + icatypes.ModuleName, + ... +) + +// Add Interchain Accounts to end blocker logic +app.moduleManager.SetOrderEndBlockers( + ... + icatypes.ModuleName, + ... +) + // Add Interchain Accounts module InitGenesis logic -app.mm.SetOrderInitGenesis( - ... - icatypes.ModuleName, - ... +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) + ... +} +``` + +If no custom athentication module is needed and a generic Cosmos SDK authentication module can be used, then from the sample integration code above all references to `ICAAuthKeeper` and `icaAuthModule` can be removed. That's it, the following code would not be needed: + +```go +// Create your Interchain Accounts authentication module +app.ICAAuthKeeper = icaauthkeeper.NewKeeper(appCodec, keys[icaauthtypes.StoreKey], app.MsgServiceRouter()) + +// ICA auth AppModule +icaAuthModule := icaauth.NewAppModule(appCodec, app.ICAAuthKeeper) ``` ### Using submodules exclusively @@ -151,15 +181,13 @@ ibcRouter.AddRoute(icahosttypes.SubModuleName, icaHostIBCModule) // Create Interchain Accounts AppModule omitting the host keeper icaModule := ica.NewAppModule(&app.ICAControllerKeeper, nil) -// Create your Interchain Accounts authentication module, setting up the Keeper, AppModule and IBCModule appropriately -app.ICAAuthKeeper = icaauthkeeper.NewKeeper(appCodec, keys[icaauthtypes.StoreKey], app.ICAControllerKeeper, scopedICAAuthKeeper) -icaAuthModule := icaauth.NewAppModule(appCodec, app.ICAAuthKeeper) -icaAuthIBCModule := icaauth.NewIBCModule(app.ICAAuthKeeper) -// Create controller IBC Module -icaControllerIBCModule := icacontroller.NewIBCModule(app.ICAControllerKeeper, icaAuthIBCModule) +// Optionally instantiate your custom authentication module if needed, or not otherwise +... + +// Create controller IBC application stack +icaControllerStack := icacontroller.NewIBCMiddleware(nil, 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 +// Register controller route +ibcRouter.AddRoute(icacontrollertypes.SubModuleName, icaControllerStack) ``` diff --git a/docs/apps/interchain-accounts/legacy/auth-modules.md b/docs/apps/interchain-accounts/legacy/auth-modules.md new file mode 100644 index 00000000000..3a2db58cfa0 --- /dev/null +++ b/docs/apps/interchain-accounts/legacy/auth-modules.md @@ -0,0 +1,268 @@ + + +# Building an authentication module + +## Deprecation Notice + +**This document is deprecated and will be removed in future releases**. + +Authentication modules play the role of the `Base Application` as described in [ICS-30 IBC Middleware](https://github.com/cosmos/ibc/tree/master/spec/app/ics-030-middleware), and enable application developers to perform custom logic when working with the Interchain Accounts controller API. {synopsis} + +The controller submodule is used for account registration and packet sending. It executes only logic required of all controllers of interchain accounts. The type of authentication used to manage the interchain accounts remains unspecified. There may exist many different types of authentication which are desirable for different use cases. Thus the purpose of the authentication module is to wrap the controller submodule with custom authentication logic. + +In ibc-go, authentication modules are connected to the controller chain via a middleware stack. The controller submodule is implemented as [middleware](https://github.com/cosmos/ibc/tree/master/spec/app/ics-030-middleware) and the authentication module is connected to the controller submodule as the base application of the middleware stack. To implement an authentication module, the `IBCModule` interface must be fulfilled. By implementing the controller submodule as middleware, any amount of authentication modules can be created and connected to the controller submodule without writing redundant code. + +The authentication module must: + +- Authenticate interchain account owners. +- Track the associated interchain account address for an owner. +- Send packets on behalf of an owner (after authentication). + +> Please note that since ibc-go v6 the channel capability is claimed by the controller submodule and therefore it is not required for authentication modules to claim the capability in the `OnChanOpenInit` callback. When the authentication module sends packets on the channel created for the associated interchain account it can pass a `nil` capability to the legacy function `SendTx` of the controller keeper (see section [`SendTx`](./keeper-api.md#sendtx) for more information). + +## `IBCModule` implementation + +The following `IBCModule` callbacks must be implemented with appropriate custom logic: + +```go +// OnChanOpenInit implements the IBCModule interface +func (im IBCModule) OnChanOpenInit( + ctx sdk.Context, + order channeltypes.Order, + connectionHops []string, + portID string, + channelID string, + chanCap *capabilitytypes.Capability, + counterparty channeltypes.Counterparty, + version string, +) (string, error) { + // since ibc-go v6 the authentication module *must not* claim the channel capability on OnChanOpenInit + + // perform custom logic + + return version, nil +} + +// OnChanOpenAck implements the IBCModule interface +func (im IBCModule) OnChanOpenAck( + ctx sdk.Context, + portID, + channelID string, + counterpartyVersion string, +) error { + // perform custom logic + + return nil +} + +// OnChanCloseConfirm implements the IBCModule interface +func (im IBCModule) OnChanCloseConfirm( + ctx sdk.Context, + portID, + channelID string, +) error { + // perform custom logic + + return nil +} + +// OnAcknowledgementPacket implements the IBCModule interface +func (im IBCModule) OnAcknowledgementPacket( + ctx sdk.Context, + packet channeltypes.Packet, + acknowledgement []byte, + relayer sdk.AccAddress, +) error { + // perform custom logic + + return nil +} + +// OnTimeoutPacket implements the IBCModule interface. +func (im IBCModule) OnTimeoutPacket( + ctx sdk.Context, + packet channeltypes.Packet, + relayer sdk.AccAddress, +) error { + // perform custom logic + + return nil +} +``` + +The following functions must be defined to fulfill the `IBCModule` interface, but they will never be called by the controller submodule so they may error or panic. That is because in Interchain Accounts, the channel handshake is always initiated on the controller chain and packets are always sent to the host chain and never to the controller chain. + +```go +// OnChanOpenTry implements the IBCModule interface +func (im IBCModule) OnChanOpenTry( + ctx sdk.Context, + order channeltypes.Order, + connectionHops []string, + portID, + channelID string, + chanCap *capabilitytypes.Capability, + counterparty channeltypes.Counterparty, + counterpartyVersion string, +) (string, error) { + panic("UNIMPLEMENTED") +} + +// OnChanOpenConfirm implements the IBCModule interface +func (im IBCModule) OnChanOpenConfirm( + ctx sdk.Context, + portID, + channelID string, +) error { + panic("UNIMPLEMENTED") +} + +// OnChanCloseInit implements the IBCModule interface +func (im IBCModule) OnChanCloseInit( + ctx sdk.Context, + portID, + channelID string, +) error { + panic("UNIMPLEMENTED") +} + +// OnRecvPacket implements the IBCModule interface. A successful acknowledgement +// is returned if the packet data is succesfully decoded and the receive application +// logic returns without error. +func (im IBCModule) OnRecvPacket( + ctx sdk.Context, + packet channeltypes.Packet, + relayer sdk.AccAddress, +) ibcexported.Acknowledgement { + panic("UNIMPLEMENTED") +} +``` + +## `OnAcknowledgementPacket` + +Controller chains will be able to access the acknowledgement written into the host chain state once a relayer relays the acknowledgement. +The acknowledgement bytes contain either the response of the execution of the message(s) on the host chain or an error. They will be passed to the auth module via the `OnAcknowledgementPacket` callback. Auth modules are expected to know how to decode the acknowledgement. + +If the controller chain is connected to a host chain using the host module on ibc-go, it may interpret the acknowledgement bytes as follows: + +Begin by unmarshaling the acknowledgement into `sdk.TxMsgData`: + +```go +var ack channeltypes.Acknowledgement +if err := channeltypes.SubModuleCdc.UnmarshalJSON(acknowledgement, &ack); err != nil { + return err +} + +txMsgData := &sdk.TxMsgData{} +if err := proto.Unmarshal(ack.GetResult(), txMsgData); err != nil { + return err +} +``` + +If the `txMsgData.Data` field is non nil, the host chain is using SDK version <= v0.45. +The auth module should interpret the `txMsgData.Data` as follows: + +```go +switch len(txMsgData.Data) { +case 0: + // see documentation below for SDK 0.46.x or greater +default: + for _, msgData := range txMsgData.Data { + if err := handler(msgData); err != nil { + return err + } + } +... +} +``` + +A handler will be needed to interpret what actions to perform based on the message type sent. +A router could be used, or more simply a switch statement. + +```go +func handler(msgData sdk.MsgData) error { +switch msgData.MsgType { +case sdk.MsgTypeURL(&banktypes.MsgSend{}): + msgResponse := &banktypes.MsgSendResponse{} + if err := proto.Unmarshal(msgData.Data, msgResponse}; err != nil { + return err + } + + handleBankSendMsg(msgResponse) + +case sdk.MsgTypeURL(&stakingtypes.MsgDelegate{}): + msgResponse := &stakingtypes.MsgDelegateResponse{} + if err := proto.Unmarshal(msgData.Data, msgResponse}; err != nil { + return err + } + + handleStakingDelegateMsg(msgResponse) + +case sdk.MsgTypeURL(&transfertypes.MsgTransfer{}): + msgResponse := &transfertypes.MsgTransferResponse{} + if err := proto.Unmarshal(msgData.Data, msgResponse}; err != nil { + return err + } + + handleIBCTransferMsg(msgResponse) + +default: + return +} +``` + +If the `txMsgData.Data` is empty, the host chain is using SDK version > v0.45. +The auth module should interpret the `txMsgData.Responses` as follows: + +```go +... +// switch statement from above +case 0: + for _, any := range txMsgData.MsgResponses { + if err := handleAny(any); err != nil { + return err + } + } +} +``` + +A handler will be needed to interpret what actions to perform based on the type URL of the Any. +A router could be used, or more simply a switch statement. +It may be possible to deduplicate logic between `handler` and `handleAny`. + +```go +func handleAny(any *codectypes.Any) error { +switch any.TypeURL { +case banktypes.MsgSend: + msgResponse, err := unpackBankMsgSendResponse(any) + if err != nil { + return err + } + + handleBankSendMsg(msgResponse) + +case stakingtypes.MsgDelegate: + msgResponse, err := unpackStakingDelegateResponse(any) + if err != nil { + return err + } + + handleStakingDelegateMsg(msgResponse) + + case transfertypes.MsgTransfer: + msgResponse, err := unpackIBCTransferMsgResponse(any) + if err != nil { + return err + } + + handleIBCTransferMsg(msgResponse) + +default: + return +} +``` + +## Integration into `app.go` file + +To integrate the authentication module into your chain, please follow the steps outlined in [`app.go` integration](./integration.md#example-integration). diff --git a/docs/apps/interchain-accounts/legacy/integration.md b/docs/apps/interchain-accounts/legacy/integration.md new file mode 100644 index 00000000000..5b683b801a6 --- /dev/null +++ b/docs/apps/interchain-accounts/legacy/integration.md @@ -0,0 +1,195 @@ + + +# Integration + +## Deprecation Notice + +**This document is deprecated and will be removed in future releases**. + +Learn how to integrate Interchain Accounts host and controller functionality to your chain. The following document only applies for Cosmos SDK chains. {synopsis} + +The Interchain Accounts module contains two submodules. Each submodule has its own IBC application. The Interchain Accounts module should be registered as an `AppModule` in the same way all SDK modules are registered on a chain, but each submodule should create its own `IBCModule` as necessary. A route should be added to the IBC router for each submodule which will be used. + +Chains who wish to support ICS-27 may elect to act as a host chain, a controller chain or both. Disabling host or controller functionality may be done statically by excluding the host or controller module entirely from the `app.go` file or it may be done dynamically by taking advantage of the on-chain parameters which enable or disable the host or controller submodules. + +Interchain Account authentication modules are the base application of a middleware stack. The controller submodule is the middleware in this stack. + +![ICApreV6](../../../assets/ica/ica-pre-v6.png) + +> Please note that since ibc-go v6 the channel capability is claimed by the controller submodule and therefore it is not required for authentication modules to claim the capability in the `OnChanOpenInit` callback. Therefore the custom authentication module does not need a scoped keeper anymore. + +## Example integration + +```go +// app.go + +// Register the AppModule for the Interchain Accounts module and the authentication module +// Note: No `icaauth` exists, this must be substituted with an actual Interchain Accounts authentication module +ModuleBasics = module.NewBasicManager( + ... + ica.AppModuleBasic{}, + icaauth.AppModuleBasic{}, + ... +) + +... + +// Add module account permissions for the Interchain Accounts module +// Only necessary for host chain functionality +// Each Interchain Account created on the host chain is derived from the module account created +maccPerms = map[string][]string{ + ... + icatypes.ModuleName: nil, +} + +... + +// Add Interchain Accounts Keepers for each submodule used and the authentication module +// If a submodule is being statically disabled, the associated Keeper does not need to be added. +type App struct { + ... + + ICAControllerKeeper icacontrollerkeeper.Keeper + ICAHostKeeper icahostkeeper.Keeper + ICAAuthKeeper icaauthkeeper.Keeper + + ... +} + +... + +// Create store keys for each submodule Keeper and the authentication module +keys := sdk.NewKVStoreKeys( + ... + icacontrollertypes.StoreKey, + icahosttypes.StoreKey, + icaauthtypes.StoreKey, + ... +) + +... + +// Create the scoped keepers for each submodule keeper and authentication keeper +scopedICAControllerKeeper := app.CapabilityKeeper.ScopeToModule(icacontrollertypes.SubModuleName) +scopedICAHostKeeper := app.CapabilityKeeper.ScopeToModule(icahosttypes.SubModuleName) + +... + +// Create the Keeper for each submodule +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, + scopedICAControllerKeeper, app.MsgServiceRouter(), +) +app.ICAHostKeeper = icahostkeeper.NewKeeper( + appCodec, keys[icahosttypes.StoreKey], app.GetSubspace(icahosttypes.SubModuleName), + app.IBCKeeper.ChannelKeeper, // may be replaced with middleware such as ics29 fee + app.IBCKeeper.ChannelKeeper, &app.IBCKeeper.PortKeeper, + app.AccountKeeper, scopedICAHostKeeper, app.MsgServiceRouter(), +) + +// Create Interchain Accounts AppModule +icaModule := ica.NewAppModule(&app.ICAControllerKeeper, &app.ICAHostKeeper) + +// Create your Interchain Accounts authentication module +app.ICAAuthKeeper = icaauthkeeper.NewKeeper(appCodec, keys[icaauthtypes.StoreKey], app.ICAControllerKeeper) + +// ICA auth AppModule +icaAuthModule := icaauth.NewAppModule(appCodec, app.ICAAuthKeeper) + +// ICA auth IBC Module +icaAuthIBCModule := icaauth.NewIBCModule(app.ICAAuthKeeper) + +// 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, icaControllerStack). + AddRoute(icahosttypes.SubModuleName, icaHostIBCModule). + AddRoute(icaauthtypes.ModuleName, icaControllerStack) // Note, the authentication module is routed to the top level of the middleware stack + +... + +// Register Interchain Accounts and authentication module AppModule's +app.moduleManager = module.NewManager( + ... + icaModule, + icaAuthModule, +) + +... + +// 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.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 + +As described above, the Interchain Accounts application module is structured to support the ability of exclusively enabling controller or host functionality. +This can be achieved by simply omitting either controller or host `Keeper` from the Interchain Accounts `NewAppModule` constructor function, and mounting only the desired submodule via the `IBCRouter`. +Alternatively, submodules can be enabled and disabled dynamically using [on-chain parameters](../parameters.md). + +The following snippets show basic examples of statically disabling submodules using `app.go`. + +### Disabling controller chain functionality + +```go +// Create Interchain Accounts AppModule omitting the controller keeper +icaModule := ica.NewAppModule(nil, &app.ICAHostKeeper) + +// Create host IBC Module +icaHostIBCModule := icahost.NewIBCModule(app.ICAHostKeeper) + +// Register host route +ibcRouter.AddRoute(icahosttypes.SubModuleName, icaHostIBCModule) +``` + +### Disabling host chain functionality + +```go +// Create Interchain Accounts AppModule omitting the host keeper +icaModule := ica.NewAppModule(&app.ICAControllerKeeper, nil) + +// Create your Interchain Accounts authentication module, setting up the Keeper, AppModule and IBCModule appropriately +app.ICAAuthKeeper = icaauthkeeper.NewKeeper(appCodec, keys[icaauthtypes.StoreKey], app.ICAControllerKeeper) +icaAuthModule := icaauth.NewAppModule(appCodec, app.ICAAuthKeeper) +icaAuthIBCModule := icaauth.NewIBCModule(app.ICAAuthKeeper) + +// Create controller IBC application stack +icaControllerStack := icacontroller.NewIBCMiddleware(icaAuthIBCModule, app.ICAControllerKeeper) + +// Register controller and authentication routes +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/legacy/keeper-api.md b/docs/apps/interchain-accounts/legacy/keeper-api.md new file mode 100644 index 00000000000..670879d02df --- /dev/null +++ b/docs/apps/interchain-accounts/legacy/keeper-api.md @@ -0,0 +1,121 @@ + + +# Keeper API + +## Deprecation Notice + +**This document is deprecated and will be removed in future releases**. + +The controller submodule keeper exposes two legacy functions that allow respectively for custom authentication modules to register interchain accounts and send packets to the interchain account. + +## `RegisterInterchainAccount` + +The authentication module can begin registering interchain accounts by calling `RegisterInterchainAccount`: + +```go +if err := keeper.icaControllerKeeper.RegisterInterchainAccount(ctx, connectionID, owner.String(), version); err != nil { + return err +} + +return nil +``` + +The `version` argument is used to support ICS-29 fee middleware for relayer incentivization of ICS-27 packets. Consumers of the `RegisterInterchainAccount` are expected to build the appropriate JSON encoded version string themselves and pass it accordingly. If an empty string is passed in the `version` argument, then the version will be initialized to a default value in the `OnChanOpenInit` callback of the controller's handler, so that channel handshake can proceed. + +The following code snippet illustrates how to construct an appropriate interchain accounts `Metadata` and encode it as a JSON bytestring: + +```go +icaMetadata := icatypes.Metadata{ + Version: icatypes.Version, + ControllerConnectionId: controllerConnectionID, + HostConnectionId: hostConnectionID, + Encoding: icatypes.EncodingProtobuf, + TxType: icatypes.TxTypeSDKMultiMsg, +} + +appVersion, err := icatypes.ModuleCdc.MarshalJSON(&icaMetadata) +if err != nil { + return err +} + +if err := keeper.icaControllerKeeper.RegisterInterchainAccount(ctx, controllerConnectionID, owner.String(), string(appVersion)); err != nil { + return err +} +``` + +Similarly, if the application stack is configured to route through ICS-29 fee middleware and a fee enabled channel is desired, construct the appropriate ICS-29 `Metadata` type: + +```go +icaMetadata := icatypes.Metadata{ + Version: icatypes.Version, + ControllerConnectionId: controllerConnectionID, + HostConnectionId: hostConnectionID, + Encoding: icatypes.EncodingProtobuf, + TxType: icatypes.TxTypeSDKMultiMsg, +} + +appVersion, err := icatypes.ModuleCdc.MarshalJSON(&icaMetadata) +if err != nil { + return err +} + +feeMetadata := feetypes.Metadata{ + AppVersion: string(appVersion), + FeeVersion: feetypes.Version, +} + +feeEnabledVersion, err := feetypes.ModuleCdc.MarshalJSON(&feeMetadata) +if err != nil { + return err +} + +if err := keeper.icaControllerKeeper.RegisterInterchainAccount(ctx, controllerConnectionID, owner.String(), string(feeEnabledVersion)); err != nil { + return err +} +``` + +## `SendTx` + +The authentication module can attempt to send a packet by calling `SendTx`: + +```go +// Authenticate owner +// perform custom logic + +// Construct controller portID based on interchain account owner address +portID, err := icatypes.NewControllerPortID(owner.String()) +if err != nil { + return err +} + +// Obtain data to be sent to the host chain. +// In this example, the owner of the interchain account would like to send a bank MsgSend to the host chain. +// The appropriate serialization function should be called. The host chain must be able to deserialize the transaction. +// If the host chain is using the ibc-go host module, `SerializeCosmosTx` should be used. +msg := &banktypes.MsgSend{FromAddress: fromAddr, ToAddress: toAddr, Amount: amt} +data, err := icatypes.SerializeCosmosTx(keeper.cdc, []proto.Message{msg}) +if err != nil { + return err +} + +// Construct packet data +packetData := icatypes.InterchainAccountPacketData{ + Type: icatypes.EXECUTE_TX, + Data: data, +} + +// Obtain timeout timestamp +// An appropriate timeout timestamp must be determined based on the usage of the interchain account. +// If the packet times out, the channel will be closed requiring a new channel to be created. +timeoutTimestamp := obtainTimeoutTimestamp() + +// Send the interchain accounts packet, returning the packet sequence +// A nil channel capability can be passed, since the controller submodule (and not the authentication module) +// claims the channel capability since ibc-go v6. +seq, err = keeper.icaControllerKeeper.SendTx(ctx, nil, portID, packetData, timeoutTimestamp) +``` + +The data within an `InterchainAccountPacketData` must be serialized using a format supported by the host chain. +If the host chain is using the ibc-go host chain submodule, `SerializeCosmosTx` should be used. If the `InterchainAccountPacketData.Data` is serialized using a format not supported by the host chain, the packet will not be successfully received. diff --git a/docs/apps/interchain-accounts/messages.md b/docs/apps/interchain-accounts/messages.md new file mode 100644 index 00000000000..0f4475818fe --- /dev/null +++ b/docs/apps/interchain-accounts/messages.md @@ -0,0 +1,74 @@ + + +# Messages + +## `MsgRegisterInterchainAccount` + +An Interchain Accounts channel handshake can be initated using `MsgRegisterInterchainAccount`: + +```go +type MsgRegisterInterchainAccount struct { + Owner string + ConnectionID string + Version string +} +``` + +This message is expected to fail if: + +- `Owner` is an empty string. +- `ConnectionID` 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)). + +This message will construct a new `MsgChannelOpenInit` on chain and route it to the core IBC message server to initiate the opening step of the channel handshake. + +The controller submodule will generate a new port identifier and claim the associated port capability. The caller is expected to provide an appropriate application version string. For example, this may be an ICS-27 JSON encoded [`Metadata`](https://github.com/cosmos/ibc-go/blob/v6.0.0/proto/ibc/applications/interchain_accounts/v1/metadata.proto#L11) type or an ICS-29 JSON encoded [`Metadata`](https://github.com/cosmos/ibc-go/blob/v6.0.0/proto/ibc/applications/fee/v1/metadata.proto#L11) type with a nested application version. +If the `Version` string is omitted, the controller submodule will construct a default version string in the `OnChanOpenInit` handshake callback. + +```go +type MsgRegisterInterchainAccountResponse struct { + ChannelID string + PortId string +} +``` + +The `ChannelID` and `PortID` are returned in the message response. + +## `MsgSendTx` + +An Interchain Accounts transaction can be executed on a remote host chain by sending a `MsgSendTx` from the corresponding controller chain: + +```go +type MsgSendTx struct { + Owner string + ConnectionID string + PacketData InterchainAccountPacketData + RelativeTimeout uint64 +} +``` + +This message is expected to fail if: + +- `Owner` is an empty string. +- `ConnectionID` 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)). +- `PacketData` contains an `UNSPECIFIED` type enum, the length of `Data` bytes is zero or the `Memo` field exceeds 256 characters in length. +- `RelativeTimeout` is zero. + +This message will create a new IBC packet with the provided `PacketData` and send it via the channel associated with the `Owner` and `ConnectionID`. +The `PacketData` is expected to contain a list of serialized `[]sdk.Msg` in the form of `CosmosTx`. Please note the signer field of each `sdk.Msg` must be the interchain account address. +When the packet is relayed to the host chain, the `PacketData` is unmarshalled and the messages are authenticated and executed. + +```go +type MsgSendTxResponse struct { + Sequence uint64 +} +``` + +The packet `Sequence` is returned in the message response. + +## Atomicity + +As the Interchain Accounts module supports the execution of multiple transactions using the Cosmos SDK `Msg` interface, it provides the same atomicity guarantees as Cosmos SDK-based applications, leveraging the [`CacheMultiStore`](https://docs.cosmos.network/main/core/store.html#cachemultistore) architecture provided by the [`Context`](https://docs.cosmos.network/main/core/context.html) type. + +This provides atomic execution of transactions when using Interchain Accounts, where state changes are only committed if all `Msg`s succeed. diff --git a/docs/apps/interchain-accounts/overview.md b/docs/apps/interchain-accounts/overview.md index dc015aaf1e3..f892ae31775 100644 --- a/docs/apps/interchain-accounts/overview.md +++ b/docs/apps/interchain-accounts/overview.md @@ -4,35 +4,30 @@ order: 1 # Overview -Learn about what the Interchain Accounts module is, and how to build custom modules that utilize Interchain Accounts functionality {synopsis} - +Learn about what the Interchain Accounts module is {synopsis} ## What is the Interchain Accounts module? -Interchain Accounts is the Cosmos SDK implementation of the ICS-27 protocol, which enables cross-chain account management built upon IBC. Chains using the Interchain Accounts module can programmatically create accounts on other chains and control these accounts via IBC transactions. - -Interchain Accounts exposes a simple-to-use API which means IBC application developers do not require an in-depth knowledge of the underlying low-level details of IBC or the ICS-27 protocol. +Interchain Accounts is the Cosmos SDK implementation of the ICS-27 protocol, which enables cross-chain account management built upon IBC. -Developers looking to build upon Interchain Accounts must write custom logic in their own IBC application module, called authentication modules. +- How does an interchain account differ from a regular account? -- How is an interchain account different than a regular account? +Regular accounts use a private key to sign transactions. Interchain Accounts are instead controlled programmatically by counterparty chains via IBC packets. -Regular accounts use a private key to sign transactions on-chain. Interchain Accounts are instead controlled programmatically by separate chains via IBC transactions. Interchain Accounts are implemented as sub-accounts of the interchain accounts module account. +## Concepts -## Concepts +`Host Chain`: The chain where the interchain account is registered. The host chain listens for IBC packets from a controller chain which should contain instructions (e.g. Cosmos SDK messages) for which the interchain account will execute. -`Host Chain`: The chain where the interchain account is registered. The host chain listens for IBC packets from a controller chain which should contain instructions (e.g. cosmos SDK messages) for which the interchain account will execute. +`Controller Chain`: The chain registering and controlling an account on a host chain. The controller chain sends IBC packets to the host chain to control the account. -`Controller Chain`: The chain registering and controlling an account on a host chain. The controller chain sends IBC packets to the host chain to control the account. A controller chain must have at least one interchain accounts authentication module in order to act as a controller chain. +`Interchain Account`: An account on a host chain created using the ICS-27 protocol. An interchain account has all the capabilities of a normal account. However, rather than signing transactions with a private key, a controller chain will send IBC packets to the host chain which signals what transactions the interchain account should execute. -`Authentication Module`: A custom IBC application module on the controller chain that uses the Interchain Accounts module API to build custom logic for the creation & management of interchain accounts. For a controller chain to utilize the interchain accounts module functionality, an authentication module is required. +`Authentication Module`: A custom application module on the controller chain that uses the Interchain Accounts module to build custom logic for the creation & management of interchain accounts. It can be either an IBC application module using the [legacy API](./legacy/keeper-api.md), or a regular Cosmos SDK application module sending messages to the controller submodule's `MsgServer` (this is the recommended approach from ibc-go v6 if access to packet callbacks is not needed). Please note that the legacy API will eventually be removed and IBC applications will not be able to use them in later releases. -`Interchain Account`: An account on a host chain. An interchain account has all the capabilities of a normal account. However, rather than signing transactions with a private key, a controller chain's authentication module will send IBC packets to the host chain which signals what transactions the interchain account should execute. - -## SDK Security Model +## SDK security model -SDK modules on a chain are assumed to be trustworthy. For example, there are no checks to prevent an untrustworthy module from accessing the bank keeper. +SDK modules on a chain are assumed to be trustworthy. For example, there are no checks to prevent an untrustworthy module from accessing the bank keeper. -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 of ICS-27 in ibc-go uses this assumption in its security considerations. -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 ICS-27 namespace. diff --git a/docs/apps/interchain-accounts/parameters.md b/docs/apps/interchain-accounts/parameters.md index 8f985b9293e..2d87927cc11 100644 --- a/docs/apps/interchain-accounts/parameters.md +++ b/docs/apps/interchain-accounts/parameters.md @@ -1,58 +1,61 @@ # Parameters The Interchain Accounts module contains the following on-chain parameters, logically separated for each distinct submodule: -### Controller Submodule Parameters +## Controller Submodule Parameters | Key | Type | Default Value | |------------------------|------|---------------| | `ControllerEnabled` | bool | `true` | -#### ControllerEnabled +### ControllerEnabled The `ControllerEnabled` parameter controls a chains ability to service ICS-27 controller specific logic. This includes the sending of Interchain Accounts packet data as well as the following ICS-26 callback handlers: + - `OnChanOpenInit` - `OnChanOpenAck` - `OnChanCloseConfirm` - `OnAcknowledgementPacket` - `OnTimeoutPacket` -### Host Submodule Parameters +## Host Submodule Parameters | Key | Type | Default Value | |------------------------|----------|---------------| | `HostEnabled` | bool | `true` | -| `AllowMessages` | []string | `[]` | +| `AllowMessages` | []string | `["*"]` | + +### HostEnabled -#### HostEnabled +The `HostEnabled` parameter controls a chains ability to service ICS-27 host specific logic. This includes the following ICS-26 callback handlers: -The `HostEnabled` parameter controls a chains ability to service ICS27 host specific logic. This includes the following ICS-26 callback handlers: - `OnChanOpenTry` - `OnChanOpenConfirm` - `OnChanCloseConfirm` - `OnRecvPacket` -#### AllowMessages +### AllowMessages -The `AllowMessages` parameter provides the ability for a chain to limit the types of messages or transactions that hosted interchain accounts are authorized to execute by defining an allowlist using the Protobuf message TypeURL format. +The `AllowMessages` parameter provides the ability for a chain to limit the types of messages or transactions that hosted interchain accounts are authorized to execute by defining an allowlist using the Protobuf message type URL format. -For example, a Cosmos SDK based chain that elects to provide hosted Interchain Accounts with the ability of governance voting and staking delegations will define its parameters as follows: +For example, a Cosmos SDK-based chain that elects to provide hosted Interchain Accounts with the ability of governance voting and staking delegations will define its parameters as follows: -``` +```json "params": { - "host_enabled": true, - "allow_messages": ["/cosmos.staking.v1beta1.MsgDelegate", "/cosmos.gov.v1beta1.MsgVote"] + "host_enabled": true, + "allow_messages": ["/cosmos.staking.v1beta1.MsgDelegate", "/cosmos.gov.v1beta1.MsgVote"] } ``` -There is also a special wildcard `"*"` message type which allows any type of message to be executed by the interchain account. This must be the only message in the `allow_messages` array. -``` +There is also a special wildcard `"*"` value which allows any type of message to be executed by the interchain account. This must be the only value in the `allow_messages` array. + +```json "params": { - "host_enabled": true, - "allow_messages": ["*"] + "host_enabled": true, + "allow_messages": ["*"] } -``` \ No newline at end of file +``` diff --git a/docs/apps/interchain-accounts/transactions.md b/docs/apps/interchain-accounts/transactions.md deleted file mode 100644 index e17571a6e00..00000000000 --- a/docs/apps/interchain-accounts/transactions.md +++ /dev/null @@ -1,21 +0,0 @@ - - -# Transactions - -Learn about Interchain Accounts transaction execution {synopsis} - -## Executing a transaction - -As described in [Authentication Modules](./auth-modules.md#trysendtx) transactions are executed using the interchain accounts controller API and require a `Base Application` as outlined in [ICS30 IBC Middleware](https://github.com/cosmos/ibc/tree/master/spec/app/ics-030-middleware) to facilitate authentication. The method of authentication remains unspecified to provide flexibility for the authentication module developer. - -Transactions are executed via the ICS27 [`SendTx` API](./auth-modules.md#trysendtx). This must be invoked through an Interchain Accounts authentication module and follows the outlined path of execution below. Packet relaying semantics provided by the IBC core transport, authentication, and ordering (IBC/TAO) layer are omitted for brevity. - -![send-tx-flow](../../assets/send-interchain-tx.png "Transaction Execution") - -## Atomicity - -As the Interchain Accounts module supports the execution of multiple transactions using the Cosmos SDK `Msg` interface, it provides the same atomicity guarantees as Cosmos SDK-based applications, leveraging the [`CacheMultiStore`](https://docs.cosmos.network/main/core/store.html#cachemultistore) architecture provided by the [`Context`](https://docs.cosmos.network/main/core/context.html) type. - -This provides atomic execution of transactions when using Interchain Accounts, where state changes are only committed if all `Msg`s succeed. diff --git a/docs/apps/transfer/authorizations.md b/docs/apps/transfer/authorizations.md new file mode 100644 index 00000000000..8835e3d807c --- /dev/null +++ b/docs/apps/transfer/authorizations.md @@ -0,0 +1,47 @@ +# `TransferAuthorization` + +`TransferAuthorization` implements the `Authorization` interface for `ibc.applications.transfer.v1.MsgTransfer`. It allows a granter to grant a grantee the privilege to submit MsgTransfer on its behalf. Please see the [Cosmos SDK docs](https://docs.cosmos.network/v0.47/modules/authz) for more details on granting privileges via the `x/authz` module. + +More specifically, the granter allows the grantee to transfer funds that belong to the granter over a specified channel. + +For the specified channel, the granter must be able to specify a spend limit of a specific denomination they wish to allow the grantee to be able to transfer. + +The granter may be able to specify the list of addresses that they allow to receive funds. If empty, then all addresses are allowed. + +It takes: + +- a `SourcePort` and a `SourceChannel` which together comprise the unique transfer channel identifier over which authorized funds can be transferred. + +- a `SpendLimit` that specifies the maximum amount of tokens the grantee can spend. The `SpendLimit` is updated as the tokens are spent. This `SpendLimit` may also be updated to increase or decrease the limit as the granter wishes. + +- an `AllowList` list that specifies the list of addresses that are allowed to receive funds. If this list is empty, then all addresses are allowed to receive funds from the `TransferAuthorization`. + +Setting a `TransferAuthorization` is expected to fail if: + +- the spend limit is nil +- the denomination of the spend limit is an invalid coin type +- the source port ID is invalid +- the source channel ID is invalid +- there are duplicate entries in the `AllowList` + +Below is the `TransferAuthorization` message: + +```go +func NewTransferAuthorization(allocations ...Allocation) *TransferAuthorization { + return &TransferAuthorization{ + Allocations: allocations, + } +} + +type Allocation struct { + // the port on which the packet will be sent + SourcePort string + // the channel by which the packet will be sent + SourceChannel string + // spend limitation on the channel + SpendLimit sdk.Coins + // allow list of receivers, an empty allow list permits any receiver address + AllowList []string +} + +``` 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/README.md b/docs/architecture/README.md index edfbea4f0c8..213cbc138b2 100644 --- a/docs/architecture/README.md +++ b/docs/architecture/README.md @@ -35,5 +35,3 @@ To suggest an ADR, please make use of the [ADR template](./adr-template.md) prov | [025](./adr-025-ibc-passive-channels.md) | IBC passive channels | Deprecated | | [026](./adr-026-ibc-client-recovery-mechanisms.md) | IBC client recovery mechansisms | Accepted | | [027](./adr-027-ibc-wasm.md) | Wasm based light clients | Accepted | - - diff --git a/docs/architecture/adr-001-coin-source-tracing.md b/docs/architecture/adr-001-coin-source-tracing.md index 4aeb59aecab..bc0c42efd2a 100644 --- a/docs/architecture/adr-001-coin-source-tracing.md +++ b/docs/architecture/adr-001-coin-source-tracing.md @@ -114,7 +114,7 @@ trace the token back to the originating chain, as specified on ICS20. The new proposed format will be the following: -```golang +```go ibcDenom = "ibc/" + hash(trace path + "/" + base denom) ``` @@ -133,7 +133,7 @@ message DenomTrace { The `IBCDenom` function constructs the `Coin` denomination used when creating the ICS20 fungible token packet data: -```golang +```go // Hash returns the hex bytes of the SHA256 hash of the DenomTrace fields using the following formula: // // hash = sha256(tracePath + "/" + baseDenom) @@ -157,7 +157,7 @@ In order to retrieve the trace information from an IBC denomination, a lookup ta added to the `ibc-transfer` module. These values need to also be persisted between upgrades, meaning that a new `[]DenomTrace` `GenesisState` field state needs to be added to the module: -```golang +```go // GetDenomTrace retrieves the full identifiers trace and base denomination from the store. func (k Keeper) GetDenomTrace(ctx Context, denomTraceHash []byte) (DenomTrace, bool) { store := ctx.KVStore(k.storeKey) @@ -188,14 +188,14 @@ func (k Keeper) SetDenomTrace(ctx Context, denomTrace DenomTrace) { The `MsgTransfer` will validate that the `Coin` denomination from the `Token` field contains a valid hash, if the trace info is provided, or that the base denominations matches: -```golang +```go func (msg MsgTransfer) ValidateBasic() error { // ... return ValidateIBCDenom(msg.Token.Denom) } ``` -```golang +```go // ValidateIBCDenom validates that the given denomination is either: // // - A valid base denomination (eg: 'uatom') @@ -226,7 +226,7 @@ The denomination trace info only needs to be updated when token is received: - Receiver is **source** chain: The receiver created the token and must have the trace lookup already stored (if necessary _ie_ native token case wouldn't need a lookup). - Receiver is **not source** chain: Store the received info. For example, during step 1, when chain `B` receives `transfer/channelToA/denom`. -```golang +```go // SendTransfer // ... @@ -245,7 +245,7 @@ if types.SenderChainIsSource(sourcePort, sourceChannel, fullDenomPath) { //... ``` -```golang +```go // DenomPathFromHash returns the full denomination path prefix from an ibc denom with a hash // component. func (k Keeper) DenomPathFromHash(ctx sdk.Context, denom string) (string, error) { @@ -265,8 +265,7 @@ func (k Keeper) DenomPathFromHash(ctx sdk.Context, denom string) (string, error) } ``` - -```golang +```go // OnRecvPacket // ... @@ -322,7 +321,7 @@ return k.bankKeeper.SendCoinsFromModuleToAccount( ) ``` -```golang +```go func NewDenomTraceFromRawDenom(denom string) DenomTrace{ denomSplit := strings.Split(denom, "/") trace := "" diff --git a/docs/architecture/adr-002-go-module-versioning.md b/docs/architecture/adr-002-go-module-versioning.md index a046792b142..aa2c74f1ad4 100644 --- a/docs/architecture/adr-002-go-module-versioning.md +++ b/docs/architecture/adr-002-go-module-versioning.md @@ -1,6 +1,7 @@ # ADR 002: Go module versioning ## Changelog + * 05/01/2022: initial draft ## Status @@ -11,18 +12,19 @@ Accepted The IBC module was originally developed in the Cosmos SDK and released during with the Stargate release series (v0.42). It was subsequently migrated to its own repository, ibc-go. -The first official release on ibc-go was v1.0.0. +The first official release on ibc-go was v1.0.0. v1.0.0 was decided to be used instead of v0.1.0 primarily for the following reasons: -- Maintaining compatibility with the IBC specification v1 requires stronger support/guarantees. -- Using the major, minor, and patch numbers allows for easier communication of what breaking changes are included in a release. -- The IBC module is being used by numerous high value projects which require stability. + +* Maintaining compatibility with the IBC specification v1 requires stronger support/guarantees. +* Using the major, minor, and patch numbers allows for easier communication of what breaking changes are included in a release. +* The IBC module is being used by numerous high value projects which require stability. ### Problems #### Go module version must be incremented When a Go module is released under v1.0.0, all following releases must follow Go semantic versioning. -Thus when the go API is broken, the Go module major version **must** be incremented. +Thus when the go API is broken, the Go module major version **must** be incremented. For example, changing the go package version from `v2` to `v3` bumps the import from `github.com/cosmos/ibc-go/v2` to `github.com/cosmos/ibc-go/v3`. If the Go module version is not incremented then attempting to go get a module @v3.0.0 without the suffix results in: @@ -33,15 +35,15 @@ Not including a go.mod in our release is not a viable option. #### Attempting to import multiple go module versions for ibc-go -Attempting to import two versions of ibc-go, such as `github.com/cosmos/ibc-go/v2` and `github.com/cosmos/ibc-go/v3`, will result in multiple issues. +Attempting to import two versions of ibc-go, such as `github.com/cosmos/ibc-go/v2` and `github.com/cosmos/ibc-go/v3`, will result in multiple issues. -The Cosmos SDK does global registration of error and governance proposal types. +The Cosmos SDK does global registration of error and governance proposal types. The errors and proposals used in ibc-go would need to now register their naming based on the go module version. The more concerning problem is that protobuf definitions will also reach a namespace collision. ibc-go and the Cosmos SDK in general rely heavily on using extended functions for go structs generated from protobuf definitions. -This requires the go structs to be defined in the same package as the extended functions. -Thus, bumping the import versioning causes the protobuf definitions to be generated in two places (in v2 and v3). +This requires the go structs to be defined in the same package as the extended functions. +Thus, bumping the import versioning causes the protobuf definitions to be generated in two places (in v2 and v3). When registering these types at compile time, the go compiler will panic. The generated types need to be registered against the proto codec, but there exist two definitions for the same name. @@ -52,11 +54,11 @@ More information [here](https://developers.google.com/protocol-buffers/docs/refe #### Changing the protobuf definition version -The protobuf definitions all have a type URL containing the protobuf version for this type. -Changing the protobuf version would solve the namespace collision which arise from importing multiple versions of ibc-go, but it leads to new issues. +The protobuf definitions all have a type URL containing the protobuf version for this type. +Changing the protobuf version would solve the namespace collision which arise from importing multiple versions of ibc-go, but it leads to new issues. In the Cosmos SDK, `Any`s are unpacked and decoded using the type URL. -Changing the type URL thus is creating a distinctly different type. +Changing the type URL thus is creating a distinctly different type. The same registration on the proto codec cannot be used to unpack the new type. For example: @@ -85,12 +87,12 @@ For example, lets say this solution is implemented in v3. Then ## Decision Supporting importing multiple versions of ibc-go requires a non-trivial amount of complexity. -It is unclear when a user of the ibc-go code would need multiple versions of ibc-go. +It is unclear when a user of the ibc-go code would need multiple versions of ibc-go. Until there is an overwhelming reason to support importing multiple versions of ibc-go: **Major releases cannot be imported simultaneously**. -Releases should focus on keeping backwards compatibility for go code clients, within reason. -Old functionality should be marked as deprecated and there should exist upgrade paths between major versions. +Releases should focus on keeping backwards compatibility for go code clients, within reason. +Old functionality should be marked as deprecated and there should exist upgrade paths between major versions. Deprecated functionality may be removed when no clients rely on that functionality. How this is determined is to be decided. @@ -99,7 +101,7 @@ This explicitly stops external clients from trying to import two major versions ## Consequences -This only affects clients relying directly on the go code. +This only affects clients relying directly on the go code. ### Positive @@ -108,4 +110,3 @@ This only affects clients relying directly on the go code. Multiple ibc-go versions cannot be imported. ### Neutral - diff --git a/docs/architecture/adr-003-ics27-acknowledgement.md b/docs/architecture/adr-003-ics27-acknowledgement.md index dc2e3256ba6..5704af0d98c 100644 --- a/docs/architecture/adr-003-ics27-acknowledgement.md +++ b/docs/architecture/adr-003-ics27-acknowledgement.md @@ -1,6 +1,7 @@ # ADR 003: ICS27 Acknowledgement Format ## Changelog + * January 28th, 2022: Initial Draft ## Status @@ -9,92 +10,94 @@ Accepted ## Context -Upon receiving an IBC packet, an IBC application can optionally return an acknowledgement. -This acknowledgement will be hashed and written into state. Thus any changes to the information included in an acknowledgement are state machine breaking. +Upon receiving an IBC packet, an IBC application can optionally return an acknowledgement. +This acknowledgement will be hashed and written into state. Thus any changes to the information included in an acknowledgement are state machine breaking. -ICS27 executes transactions on behalf of a controller chain. Information such as the message result or message error may be returned from other SDK modules outside the control of the ICS27 module. -It might be very valuable to return message execution information inside the ICS27 acknowledgement so that controller chain interchain account auth modules can act upon this information. -Only determinstic information returned from the message execution is allowed to be returned in the packet acknowledgement otherwise the network will halt due to a fork in the expected app hash. +ICS27 executes transactions on behalf of a controller chain. Information such as the message result or message error may be returned from other SDK modules outside the control of the ICS27 module. +It might be very valuable to return message execution information inside the ICS27 acknowledgement so that controller chain interchain account auth modules can act upon this information. +Only determinstic information returned from the message execution is allowed to be returned in the packet acknowledgement otherwise the network will halt due to a fork in the expected app hash. ## Decision At the time of this writing, Tendermint includes the following information in the [ABCI.ResponseDeliverTx](https://github.com/tendermint/tendermint/blob/release/v0.34.13/types/results.go#L47-#L53): + ```go // deterministicResponseDeliverTx strips non-deterministic fields from // ResponseDeliverTx and returns another ResponseDeliverTx. func deterministicResponseDeliverTx(response *abci.ResponseDeliverTx) *abci.ResponseDeliverTx { - return &abci.ResponseDeliverTx{ - Code: response.Code, - Data: response.Data, - GasWanted: response.GasWanted, - GasUsed: response.GasUsed, - } + return &abci.ResponseDeliverTx{ + Code: response.Code, + Data: response.Data, + GasWanted: response.GasWanted, + GasUsed: response.GasUsed, + } } ``` ### Successful acknowledgements -Successful acknowledgements should return information about the transaction execution. -Given the determinstic fields in the `abci.ResponseDeliverTx`, the transaction `Data` can be used to indicate information about the transaction execution. +Successful acknowledgements should return information about the transaction execution. +Given the determinstic fields in the `abci.ResponseDeliverTx`, the transaction `Data` can be used to indicate information about the transaction execution. The `abci.ResponseDeliverTx.Data` will be set in the ICS27 packet acknowledgement upon successful transaction execution. -The format for the `abci.ResponseDeliverTx.Data` is constructed by the SDK. +The format for the `abci.ResponseDeliverTx.Data` is constructed by the SDK. -At the time of this writing, the next major release of the SDK will change the format for constructing the transaction response data. +At the time of this writing, the next major release of the SDK will change the format for constructing the transaction response data. #### v0.45 format The current version, v0.45 constructs the transaction response as follows: + ```go - proto.Marshal(&sdk.TxMsgData{ - Data: []*sdk.MsgData{msgResponses...}, - } +proto.Marshal(&sdk.TxMsgData{ + Data: []*sdk.MsgData{msgResponses...}, +} ``` -Where `msgResponses` is a slice of `*sdk.MsgData`. +Where `msgResponses` is a slice of `*sdk.MsgData`. The `MsgData.MsgType` contains the `sdk.MsgTypeURL` of the `sdk.Msg` being executed. -The `MsgData.Data` contains the proto marshaled `MsgResponse` for the associated message executed. +The `MsgData.Data` contains the proto marshaled `MsgResponse` for the associated message executed. #### Next major version format The next major version will construct the transaction response as follows: -```go - proto.Marshal(&sdk.TxMsgData{ - MsgResponses: []*codectypes.Any{msgResponses...}, - } + +```go +proto.Marshal(&sdk.TxMsgData{ + MsgResponses: []*codectypes.Any{msgResponses...}, +} ``` Where `msgResponses` is a slice of the `MsgResponse`s packed into `Any`s. #### Forwards compatible approach -A forwards compatible approach was deemed infeasible. -The `handler` provided by the `MsgServiceRouter` will only include the `*sdk.Result` and an error (if one occurred). -In v0.45 of the SDK, the `*sdk.Result.Data` will contain the MsgResponse marshaled data. -However, the MsgResponse is not packed and marshaled as a `*codectypes.Any`, thus making it impossible from a generalized point of view to unmarshal the bytes. -If the bytes could be unmarshaled, then they could be packed into an `*codectypes.Any` in antcipation of the upcoming format. +A forwards compatible approach was deemed infeasible. +The `handler` provided by the `MsgServiceRouter` will only include the `*sdk.Result` and an error (if one occurred). +In v0.45 of the SDK, the `*sdk.Result.Data` will contain the MsgResponse marshaled data. +However, the MsgResponse is not packed and marshaled as a `*codectypes.Any`, thus making it impossible from a generalized point of view to unmarshal the bytes. +If the bytes could be unmarshaled, then they could be packed into an `*codectypes.Any` in anticipation of the upcoming format. -Intercepting the MsgResponse before it becomes marshaled requires replicating this [code](https://github.com/cosmos/cosmos-sdk/blob/dfd47f5b449f558a855da284a9a7eabbfbad435d/baseapp/msg_service_router.go#L109-#L128). +Intercepting the MsgResponse before it becomes marshaled requires replicating this [code](https://github.com/cosmos/cosmos-sdk/blob/dfd47f5b449f558a855da284a9a7eabbfbad435d/baseapp/msg_service_router.go#L109-#L128). It may not even be possible to replicate the linked code. The method handler would need to be accessed somehow. -For these reasons it is deemed infeasible to attempt a fowards compatible approach. +For these reasons it is deemed infeasible to attempt a fowards compatible approach. -ICA auth developers can interpret which format was used when constructing the transaction response by checking if the `sdk.TxMsgData.Data` field is non-empty. +ICA auth developers can interpret which format was used when constructing the transaction response by checking if the `sdk.TxMsgData.Data` field is non-empty. If the `sdk.TxMsgData.Data` field is not empty then the format for v0.45 was used, otherwise ICA auth developers can assume the transaction response uses the newer format. - #### Decision -Replicate the transaction response format as provided by the current SDK verison. -When the SDK version changes, adjust the transaction response format to use the updated transaction response format. -Include the transaction response bytes in the result channel acknowledgement. +Replicate the transaction response format as provided by the current SDK verison. +When the SDK version changes, adjust the transaction response format to use the updated transaction response format. +Include the transaction response bytes in the result channel acknowledgement. A test has been [written](https://github.com/cosmos/ibc-go/blob/v3.0.0-beta1/modules/apps/27-interchain-accounts/host/ibc_module_test.go#L716-#L774) to fail if the `MsgResponse` is no longer included in consensus. ### Error acknowledgements -As indicated above, the `abci.ResponseDeliverTx.Code` is determinstic. -Upon transaction execution errors, an error acknowledgement should be returned including the abci code. +As indicated above, the `abci.ResponseDeliverTx.Code` is determinstic. +Upon transaction execution errors, an error acknowledgement should be returned including the abci code. A test has been [written](https://github.com/cosmos/ibc-go/blob/v3.0.0-beta1/modules/apps/27-interchain-accounts/host/types/ack_test.go#L41-#L82) to fail if the ABCI code is no longer determinstic. @@ -104,15 +107,14 @@ A test has been [written](https://github.com/cosmos/ibc-go/blob/v3.0.0-beta1/mod ### Positive -- interchain account auth modules can act upon transaction results without requiring a query module -- transaction results align with those returned by execution of a normal SDK message. +* interchain account auth modules can act upon transaction results without requiring a query module +* transaction results align with those returned by execution of a normal SDK message. ### Negative -- the security assumptions of this decision rest on the inclusion of the ABCI error code and the Msg response in the ResponseDeliverTx hash created by Tendermint -- events are non-determinstic and cannot be included in the packet acknowledgement +* the security assumptions of this decision rest on the inclusion of the ABCI error code and the Msg response in the ResponseDeliverTx hash created by Tendermint +* events are non-determinstic and cannot be included in the packet acknowledgement ### Neutral No neutral consequences. - diff --git a/docs/architecture/adr-004-ics29-lock-fee-module.md b/docs/architecture/adr-004-ics29-lock-fee-module.md index 5b17717e669..27fc2319637 100644 --- a/docs/architecture/adr-004-ics29-lock-fee-module.md +++ b/docs/architecture/adr-004-ics29-lock-fee-module.md @@ -1,6 +1,7 @@ # ADR 004: Lock fee module upon escrow out of balance ## Changelog + * 03/03/2022: initial draft ## Status @@ -9,27 +10,27 @@ Accepted ## Context -The fee module maintains an escrow account for all fees escrowed to incentivize packet relays. -It also tracks each packet fee escrowed separately from the escrow account. This is because the escrow account only maintains a total balance. It has no reference for which coins belonged to which packet fee. +The fee module maintains an escrow account for all fees escrowed to incentivize packet relays. +It also tracks each packet fee escrowed separately from the escrow account. This is because the escrow account only maintains a total balance. It has no reference for which coins belonged to which packet fee. In the presence of a severe bug, it is possible the escrow balance will become out of sync with the packet fees marked as escrowed. The ICS29 module should be capable of elegantly handling such a scenario. ## Decision -We will allow for the ICS29 module to become "locked" if the escrow balance is determined to be out of sync with the packet fees marked as escrowed. -A "locked" fee module will not allow for packet escrows to occur nor will it distribute fees. All IBC callbacks will skip performing fee logic, similar to fee disabled channels. +We will allow for the ICS29 module to become "locked" if the escrow balance is determined to be out of sync with the packet fees marked as escrowed. +A "locked" fee module will not allow for packet escrows to occur nor will it distribute fees. All IBC callbacks will skip performing fee logic, similar to fee disabled channels. -Manual intervention will be needed to unlock the fee module. +Manual intervention will be needed to unlock the fee module. ### Sending side -Special behaviour will have to be accounted for in `OnAcknowledgementPacket`. Since the counterparty will continue to send incentivized acknowledgements for fee enabled channels, the acknowledgement will still need to be unmarshalled into an incentivized acknowledgement before calling the underlying application `OnAcknowledgePacket` callback. +Special behaviour will have to be accounted for in `OnAcknowledgementPacket`. Since the counterparty will continue to send incentivized acknowledgements for fee enabled channels, the acknowledgement will still need to be unmarshalled into an incentivized acknowledgement before calling the underlying application `OnAcknowledgePacket` callback. When distributing fees, a cached context should be used. If the escrow account balance would become negative, the current state changes should be discarded and the fee module should be locked using the uncached context. This prevents fees from being partially distributed for a given packetID. ### Receiving side -`OnRecvPacket` should remain unaffected by the fee module becoming locked since escrow accounts only affect the sending side. +`OnRecvPacket` should remain unaffected by the fee module becoming locked since escrow accounts only affect the sending side. ## Consequences @@ -39,17 +40,19 @@ The fee module can be elegantly disabled in the presence of severe bugs. ### Negative -Extra logic is added to account for edge cases which are only possible in the presence of bugs. +Extra logic is added to account for edge cases which are only possible in the presence of bugs. ### Neutral ## References Issues: -- [#821](https://github.com/cosmos/ibc-go/issues/821) -- [#860](https://github.com/cosmos/ibc-go/issues/860) + +* [#821](https://github.com/cosmos/ibc-go/issues/821) +* [#860](https://github.com/cosmos/ibc-go/issues/860) PR's: -- [#1031](https://github.com/cosmos/ibc-go/pull/1031) -- [#1029](https://github.com/cosmos/ibc-go/pull/1029) -- [#1056](https://github.com/cosmos/ibc-go/pull/1056) + +* [#1031](https://github.com/cosmos/ibc-go/pull/1031) +* [#1029](https://github.com/cosmos/ibc-go/pull/1029) +* [#1056](https://github.com/cosmos/ibc-go/pull/1056) diff --git a/docs/architecture/adr-005-consensus-height-events.md b/docs/architecture/adr-005-consensus-height-events.md new file mode 100644 index 00000000000..5df21dac7cd --- /dev/null +++ b/docs/architecture/adr-005-consensus-height-events.md @@ -0,0 +1,92 @@ +# ADR 005: UpdateClient Events - ClientState Consensus Heights + +## Changelog + +* 25/04/2022: initial draft + +## Status + +Accepted + +## Context + +The `ibc-go` implementation leverages the [Cosmos-SDK's EventManager](https://github.com/cosmos/cosmos-sdk/blob/v0.45.4/docs/core/events.md#EventManager) to provide subscribers a method of reacting to application specific events. +Some IBC relayers depend on the [`consensus_height`](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/core/02-client/keeper/events.go#L33) attribute emitted as part of `UpdateClient` events in order to run `07-tendermint` misbehaviour detection by cross-checking the details of the *Header* emitted at a given consensus height against those of the *Header* from the originating chain. This includes such details as: + +* The `SignedHeader` containing the commitment root. +* The `ValidatorSet` that signed the *Header*. +* The `TrustedHeight` seen by the client at less than or equal to the height of *Header*. +* The last `TrustedValidatorSet` at the trusted height. + +Following the refactor of the `02-client` submodule and associated `ClientState` interfaces, it will now be possible for +light client implementations to perform such actions as batch updates, inserting `N` number of `ConsensusState`s into the application state tree with a single `UpdateClient` message. This flexibility is provided in `ibc-go` by the usage of the [Protobuf `Any`](https://developers.google.com/protocol-buffers/docs/proto3#any) field contained within the [`UpdateClient`](https://github.com/cosmos/ibc-go/blob/v3.0.0/proto/ibc/core/client/v1/tx.proto#L44) message. +For example, a batched client update message serialized as a Protobuf `Any` type for the `07-tendermint` lightclient implementation could be defined as follows: + +```protobuf +message BatchedHeaders { + repeated Header headers = 1; +} +``` + +To complement this flexibility, the `UpdateClient` handler will now support the submission of [client misbehaviour](https://github.com/cosmos/ibc/tree/master/spec/core/ics-002-client-semantics#misbehaviour) by consolidating the `Header` and `Misbehaviour` interfaces into a single `ClientMessage` interface type: + +```go +// ClientMessage is an interface used to update an IBC client. +// The update may be done by a single header, a batch of headers, misbehaviour, or any type which when verified produces +// a change to state of the IBC client +type ClientMessage interface { + proto.Message + + ClientType() string + ValidateBasic() error +} +``` + +To support this functionality the `GetHeight()` method has been omitted from the new `ClientMessage` interface. +Emission of standardised events from the `02-client` submodule now becomes problematic and is two-fold: + +1. The `02-client` submodule previously depended upon the `GetHeight()` method of `Header` types in order to [retrieve the updated consensus height](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/core/02-client/keeper/client.go#L90). +2. Emitting a single `consensus_height` event attribute is not sufficient in the case of a batched client update containing multiple *Headers*. + +## Decision + +The following decisions have been made in order to provide flexibility to consumers of `UpdateClient` events in a non-breaking fashion: + +1. Return a list of updated consensus heights `[]exported.Height` from the new `UpdateState` method of the `ClientState` interface. + +```go +// UpdateState updates and stores as necessary any associated information for an IBC client, such as the ClientState and corresponding ConsensusState. +// Upon successful update, a list of consensus heights is returned. It assumes the ClientMessage has already been verified. +UpdateState(sdk.Context, codec.BinaryCodec, sdk.KVStore, ClientMessage) []Height +``` + +2. Maintain the `consensus_height` event attribute emitted from the `02-client` update handler, but mark as deprecated for future removal. For example, with tendermint lightclients this will simply be `consensusHeights[0]` following a successful update using a single *Header*. + +3. Add an additional `consensus_heights` event attribute, containing a comma separated list of updated heights. This provides flexibility for emitting a single consensus height or multiple consensus heights in the example use-case of batched header updates. + +## Consequences + +### Positive + +* Subscribers of IBC core events can act upon `UpdateClient` events containing one or more consensus heights. +* Deprecation of the existing `consensus_height` attribute allows consumers to continue to process `UpdateClient` events as normal, with a path to upgrade to using the `consensus_heights` attribute moving forward. + +### Negative + +* Consumers of IBC core `UpdateClient` events are forced to make future code changes. + +### Neutral + +## References + +Discussions: + +* [#1208](https://github.com/cosmos/ibc-go/pull/1208#discussion_r839691927) + +Issues: + +* [#594](https://github.com/cosmos/ibc-go/issues/594) + +PRs: + +* [#1285](https://github.com/cosmos/ibc-go/pull/1285) diff --git a/docs/architecture/adr-006-02-client-refactor.md b/docs/architecture/adr-006-02-client-refactor.md new file mode 100644 index 00000000000..4f955672f31 --- /dev/null +++ b/docs/architecture/adr-006-02-client-refactor.md @@ -0,0 +1,203 @@ +# ADR 006: ICS-02 client refactor + +## Changelog + +* 2022-08-01: Initial Draft + +## Status + +Accepted and applied in v7 of ibc-go + +## Context + +During the initial development of the 02-client submodule, each light client supported (06-solomachine, 07-tendermint, 09-localhost) was referenced through hardcoding. +Here is an example of the [code](https://github.com/cosmos/cosmos-sdk/commit/b93300288e3a04faef9c0774b75c13b24450ba1c#diff-c5f6b956947375f28d611f18d0e670cf28f8f305300a89c5a9b239b0eeec5064R83) that existed in the 02-client submodule: + +```go +func (k Keeper) UpdateClient(ctx sdk.Context, clientID string, header exported.Header) (exported.ClientState, error) { + ... + + switch clientType { + case exported.Tendermint: + clientState, consensusState, err = tendermint.CheckValidityAndUpdateState( + clientState, header, ctx.BlockTime(), + ) + case exported.Localhost: + // override client state and update the block height + clientState = localhosttypes.NewClientState( + ctx.ChainID(), // use the chain ID from context since the client is from the running chain (i.e self). + ctx.BlockHeight(), + ) + default: + err = types.ErrInvalidClientType + } +``` + +To add additional light clients, code would need to be added directly to the 02-client submodule. +Evidently, this would likely become problematic as IBC scaled to many chains using consensus mechanisms beyond the initial supported light clients. +Issue [#6064](https://github.com/cosmos/cosmos-sdk/issues/6064) on the SDK addressed this problem by creating a more modular 02-client submodule. +The 02-client submodule would now interact with each light client via an interface. +While, this change was positive in development, increasing the flexibility and adoptability of IBC, it also opened the door to new problems. + +The difficulty of generalizing light clients became apparent once changes to those light clients were required. +Each light client represents a different consensus algorithm which may contain a host of complexity and nuances. +Here are some examples of issues which arose for light clients that are not applicable to all the light clients supported (06-solomachine, 07-tendermint, 09-localhost): + +### Tendermint non-zero height upgrades + +Before the launch of IBC, it was determined that the golang implementation of [tendermint](https://github.com/tendermint/tendermint) would not be capable of supporting non-zero height upgrades. +This implies that any upgrade would require changing of the chain ID and resetting the height to 0. +A chain is uniquely identified by its chain-id and validator set. +Two different chain ID's can be viewed as different chains and thus a normal update produced by a validator set cannot change the chain ID. +To work around the lack of support for non-zero height upgrades, an abstract height type was created along with an upgrade mechanism. +This type would indicate the revision number (the number of times the chain ID has been changed) and revision height (the current height of the blockchain). + +Refs: + +* Issue [#439](https://github.com/cosmos/ibc/issues/439) on IBC specification repository. +* Specification changes in [#447](https://github.com/cosmos/ibc/pull/447) +* Implementation changes for the abstract height type, [SDK#7211](https://github.com/cosmos/cosmos-sdk/pull/7211) + +### Tendermint requires misbehaviour detection during updates + +The initial release of the IBC module and the 07-tendermint light client implementation did not support misbehaviour detection during update nor did it prevent overwriting of previous updates. +Despite the fact that we designed the `ClientState` interface and developed the 07-tendermint client, we failed to detect even a duplicate update that constituted misbehaviour and thus should freeze the client. +This was fixed in PR [#141](https://github.com/cosmos/ibc-go/pull/141) which required light client implementations to be aware that they must handle duplicate updates and misbehaviour detection. +Misbehaviour detection during updates is not applicable to the solomachine nor localhost. +It is also not obvious that `CheckHeaderAndUpdateState` should be performing this functionality. + +### Localhost requires access to the entire client store + +The localhost has been broken since the initial version of the IBC module. +The localhost tried to be developed underneath the 02-client interfaces without special exception, but this proved to be impossible. +The issues were outlined in [#27](https://github.com/cosmos/ibc-go/issues/27) and further discussed in the attempted ADR in [#75](https://github.com/cosmos/ibc-go/pull/75). +Unlike all other clients, the localhost requires access to the entire IBC store and not just the prefixed client store. + +### Solomachine doesn't set consensus states + +The 06-solomachine does not set the consensus states within the prefixed client store. +It has a single consensus state that is stored within the client state. +This causes setting of the consensus state at the 02-client level to use unnecessary storage. +It also causes timeouts to fail with solo machines. +Previously, the timeout logic within IBC would obtain the consensus state at the height a timeout is being proved. +This is problematic for the solo machine as no consensus state is set. +See issue [#562](https://github.com/cosmos/ibc/issues/562) on the IBC specification repo. + +### New clients may want to do batch updates + +New light clients may not function in a similar fashion to 06-solomachine and 07-tendermint. +They may require setting many consensus states in a single update. +As @seunlanlege [states](https://github.com/cosmos/ibc-go/issues/284#issuecomment-1005583679): + +> I'm in support of these changes for 2 reasons: +> +> * This would allow light clients handle batch header updates in CheckHeaderAndUpdateState, for the special case of 11-beefy proving the finality for a batch of headers is much more space and time efficient than the space/time complexity of proving each individual headers in that batch, combined. +> +> * This also allows for a single light client instance of 11-beefy be used to prove finality for every parachain connected to the relay chain (Polkadot/Kusama). We achieve this by setting the appropriate ConsensusState for individual parachain headers in CheckHeaderAndUpdateState + +## Decision + +### Require light clients to set client and consensus states + +The IBC specification states: + +> If the provided header was valid, the client MUST also mutate internal state to store now-finalised consensus roots and update any necessary signature authority tracking (e.g. changes to the validator set) for future calls to the validity predicate. + +The initial version of the IBC go SDK based module did not fulfill this requirement. +Instead, the 02-client submodule required each light client to return the client and consensus state which should be updated in the client prefixed store. +This decision lead to the issues "Solomachine doesn't set consensus states" and "New clients may want to do batch updates". + +Each light client should be required to set its own client and consensus states on any update necessary. +The go implementation should be changed to match the specification requirements. +This will allow more flexibility for light clients to manage their own internal storage and do batch updates. + +### Merge `Header`/`Misbehaviour` interface and rename to `ClientMessage` + +Remove `GetHeight()` from the header interface (as light clients now set the client/consensus states). +This results in the `Header`/`Misbehaviour` interfaces being the same. +To reduce complexity of the codebase, the `Header`/`Misbehaviour` interfaces should be merged into `ClientMessage`. +`ClientMessage` will provide the client with some authenticated information which may result in regular updates, misbehaviour detection, batch updates, or other custom functionality a light client requires. + +### Split `CheckHeaderAndUpdateState` into 4 functions + +See [#668](https://github.com/cosmos/ibc-go/issues/668). + +Split `CheckHeaderAndUpdateState` into 4 functions: + +* `VerifyClientMessage` +* `CheckForMisbehaviour` +* `UpdateStateOnMisbehaviour` +* `UpdateState` + +`VerifyClientMessage` checks the that the structure of a `ClientMessage` is correct and that all authentication data provided is valid. + +`CheckForMisbehaviour` checks to see if a `ClientMessage` is evidence of misbehaviour. + +`UpdateStateOnMisbehaviour` freezes the client and updates its state accordingly. + +`UpdateState` performs a regular update or a no-op on duplicate updates. + +The code roughly looks like: + +```go +func (k Keeper) UpdateClient(ctx sdk.Context, clientID string, header exported.Header) error { + ... + + if err := clientState.VerifyClientMessage(clientMessage); err != nil { + return err + } + + foundMisbehaviour := clientState.CheckForMisbehaviour(clientMessage) + if foundMisbehaviour { + clientState.UpdateStateOnMisbehaviour(header) + // emit misbehaviour event + return + } + + clientState.UpdateState(clientMessage) // expects no-op on duplicate header + // emit update event + return +} +``` + +### Add `GetTimestampAtHeight` to the client state interface + +By adding `GetTimestampAtHeight` to the ClientState interface, we allow light clients which do non-traditional consensus state/timestamp storage to process timeouts correctly. +This fixes the issues outlined for the solo machine client. + +### Add generic verification functions + +As the complexity and the functionality grows, new verification functions will be required for additional paths. +This was explained in [#684](https://github.com/cosmos/ibc/issues/684) on the specification repo. +These generic verification functions would be immediately useful for the new paths added in connection/channel upgradability as well as for custom paths defined by IBC applications such as Interchain Queries. +The old verification functions (`VerifyClientState`, `VerifyConnection`, etc) should be removed in favor of the generic verification functions. + +## Consequences + +### Positive + +* Flexibility for light client implementations +* Well defined interfaces and their required functionality +* Generic verification functions +* Applies changes necessary for future client/connection/channel upgrabability features +* Timeout processing for solo machines +* Reduced code complexity + +### Negative + +* The refactor touches on sensitive areas of the ibc-go codebase +* Changing of established naming (`Header`/`Misbehaviour` to `ClientMessage`) + +### Neutral + +No notable consequences + +## References + +Issues: + +* [#284](https://github.com/cosmos/ibc-go/issues/284) + +PRs: + +* [#1871](https://github.com/cosmos/ibc-go/pull/1871) diff --git a/docs/architecture/adr-007-solomachine-signbytes.md b/docs/architecture/adr-007-solomachine-signbytes.md new file mode 100644 index 00000000000..23be1322e2e --- /dev/null +++ b/docs/architecture/adr-007-solomachine-signbytes.md @@ -0,0 +1,52 @@ +# ADR 007: Solo machine sign bytes + +## Changelog + +* 2022-08-02: Initial draft + +## Status + +Accepted, applied in v7 + +## Context + +The `06-solomachine` implemention up until ibc-go v7 constructed sign bytes using a `DataType` which described what type of data was being signed. +This design decision arose from a misunderstanding of the security implications. +It was noted that the proto definitions do not [provide uniqueness](https://github.com/cosmos/cosmos-sdk/pull/7237#discussion_r484264573) which is a necessity for ensuring two signatures over different data types can never be the same. +What was missed is that the uniqueness is not provided by the proto definition, but by the usage of the proto definition. +The path provided by core IBC will be unique and is already encoded into the signature data. +Thus two different paths with the same data values will encode differently which provides signature uniqueness. + +Furthermore, the current construction does not support the proposed changes in the spec repo to support [Generic Verification functions](https://github.com/cosmos/ibc/issues/684). +This is because in order to verify a new path, a new `DataType` must be added for that path. + +## Decision + +Remove `DataType` and change the `DataType` in the `SignBytes` and `SignatureAndData` to be `Path`. +The new `Path` field should be bytes. +Remove all `...Data` proto definitions except for `HeaderData` +These `...Data` definitions were created previously for each `DataType`. +The proto version of the solo machine proto definitions should be bumped to `v3`. + +This removes an extra layer of complexity from signature construction and allows for support of generic verification. + +## Consequences + +### Positive + +* Simplification of solo machine signature construction +* Support for generic verification + +### Negative + +* Breaks existing signature construction in a non-backwards compatible way +* Solo machines must update to handle the new format +* Migration required for solo machine client and consensus states + +### Neutral + +No notable consequences + +## References + +* [#1141](https://github.com/cosmos/ibc-go/issues/1141) diff --git a/docs/architecture/adr-009-v6-ics27-msgserver.md b/docs/architecture/adr-009-v6-ics27-msgserver.md new file mode 100644 index 00000000000..9fe6708a4b1 --- /dev/null +++ b/docs/architecture/adr-009-v6-ics27-msgserver.md @@ -0,0 +1,115 @@ +# ADR 009: ICS27 message server addition + +## Changelog + +* 2022/09/07: Initial draft + +## Status + +Accepted, implemented in v6 of ibc-go + +## Context + +ICS 27 (Interchain Accounts) brought a cross-chain account management protocol built upon IBC. +It enabled chains to programatically create accounts on behalf of counterparty chains which may enable a variety of authentication methods for this interchain account. +The initial release of ICS 27 focused on enabling authentication schemes which may not require signing with a private key, such as via on-chain mechanisms like governance. + +Following the initial release of ICS 27 it became evident that: + +* a default authentication module would enable more usage of ICS 27 +* generic authentication modules should be capable of authenticating an interchain account registration +* application logic which wraps ICS 27 packet sends do not need to be associated with the authentication logic + +## Decision + +The controller module should be simplified to remove the correlation between the authentication logic for an interchain account and the application logic for an interchain account. +To minimize disruption to developers working on the original design of the ICS 27 controller module, all changes will be made in a backwards compatible fashion. + +### Msg server + +To acheive this, as stated by [@damiannolan](https://github.com/cosmos/ibc-go/issues/2026#issue-1341640594), it was proposed to: + +> Add a new `MsgServer` to `27-interchain-accounts` which exposes two distinct rpc endpoints: +> +> * `RegisterInterchainAccount` +> * `SendTx` + +This will enable any SDK (authentication) module to register interchain accounts and send transactions on their behalf. +Examples of existing SDK modules which would benefit from this change include: + +* x/auth +* x/gov +* x/group + +The existing go functions: `RegisterInterchainAccount()` and `SendTx()` will remain to operate as they did in previous release versions. + +This will be possible for SDK v0.46.x and above. + +### Allow `nil` underlying applications + +Authentication modules should interact with the controller module via the message server and should not be associated with application logic. +For now, it will be allowed to set a `nil` underlying application. +A future version may remove the underlying application entirely. + +See issue [#2040](https://github.com/cosmos/ibc-go/issues/2040) + +### Channel capability claiming + +The controller module will now claim the channel capability in `OnChanOpenInit`. +Underlying applications will be passed a `nil` capability in `OnChanOpenInit`. + +Channel capability migrations will be added in two steps: + +* Upgrade handler migration which modifies the channel capability owner from the underlying app to the controller module +* ICS 27 module automatic migration which asserts the upgrade handler channel capability migration has been performed successfully + +See issue [#2033](https://github.com/cosmos/ibc-go/issues/2033) + +### Middleware enabled channels + +In order to maintain backwards compatibility and avoid requiring underlying application developers to account for interchain accounts they did not register, a boolean mapping has been added to track the behaviour of how an account was created. + +If the account was created via the legacy API, then the underlying application callbacks will be executed. + +If the account was created with the new API (message server), then the underlying application callbacks will not be executed. + +See issue [#2145](https://github.com/cosmos/ibc-go/issues/2145) + +### Future considerations + +[ADR 008](https://github.com/cosmos/ibc-go/pull/1976) proposes the creation of a middleware which enables callers of an IBC packet send to perform application logic in conjunction with the IBC application. +The underlying application can be removed at the availablity of such a middleware as that will be the preferred method for executing application logic upon a ICS 27 packet send. + +### Miscellanous + +In order to avoid import cycles, the genesis types have been moved to their own directory. +A new protobuf package has been created for the genesis types. + +See PR [#2133](https://github.com/cosmos/ibc-go/pull/2133) + +An additional field has been added to the `ActiveChannel` type to store the `IsMiddlewareEnabled` field upon genesis import/export. + +See issue [#2165](https://github.com/cosmos/ibc-go/issues/2165) + +## Consequences + +### Positive + +* default authentication modules are provided (x/auth, x/group, x/gov) +* any SDK authentication module may now be used with ICS 27 +* separation of authentication from application logic in relation to ICS 27 +* minimized disruption to existing development around ICS 27 controller module +* underlying applications no longer have to handle capabilities +* removal of the underlying application upon the creation of ADR 008 may be done in a minimally disruptive fashion +* only underlying applications which registered the interchain account will perform application logic for that account (underlying applications do not need to be aware of accounts they did not register) + +### Negative + +* the security model has been reduced to that of the SDK. SDK modules may send packets for any interchain account. +* additional maintenance of the messages added and the middleware enabled flag +* underlying applications which will become ADR 008 modules are not required to be aware of accounts they did not register +* calling legacy API vs the new API results in different behaviour for ICS 27 application stacks which have an underlying application + +### Neutral + +* A major release is required diff --git a/docs/architecture/adr-010-light-clients-as-sdk-modules.md b/docs/architecture/adr-010-light-clients-as-sdk-modules.md new file mode 100644 index 00000000000..62186debaf3 --- /dev/null +++ b/docs/architecture/adr-010-light-clients-as-sdk-modules.md @@ -0,0 +1,100 @@ +# ADR 010: IBC light clients as SDK modules + +## Changelog +* 12/12/2022: initial draft + +## Status + +Proposed + +## Context + +ibc-go has 3 main consumers: +- IBC light clients +- IBC applications +- relayers + +Relayers listen and respond to events emitted by ibc-go while IBC light clients and applications are invoked by core IBC. +Currently there exists two different approaches to callbacks being invoked by core IBC. + +IBC light clients currently are invoked by a `ClientState` and `ConsensusState` interface as defined by [core IBC](https://github.com/cosmos/ibc-go/blob/fb9dedd706e5835cdd607392320df79fcea7ca7f/modules/core/exported/client.go#L36). +The 02-client submodule will retrieve the `ClientState` or `ConsensusState` from the IBC store in order to perform callbacks to the light client. +This design requires all required information for the light client to function to be stored in the `ClientState` or `ConsensusState` or potentially under metadata keys for a specific client instance. +Additional information may be provided by core IBC via the defined interface arguments if that information is generic enough to be useful to all IBC light clients. +This constraint has proved problematic as pass through clients (such as wasm) cannot maintain easy access to a VM instance. +In addition, without increasing the size of the defined `ClientState` interface, light clients are unable to take advantage of basic built-in SDK functionality such as genesis import/export and migrations. + +The other approach used to perform callback logic is via registered SDK modules. +This approach is used by core IBC to interact with IBC applications. +IBC applications will register their callbacks on the IBC router at compile time. +When a packet comes in, core IBC will use the IBC router to lookup the registered callback functions for the provided packet. +The benefit of registered callbacks opposed to interface functions is that additional information may be accessed via external keepers. +Because the IBC applications are also SDK modules, they additionally get access to a host of functionality provided by the SDK. +This includes: genesis import/export, migrations, query/transaction CLI commands, type registration, gRPC query registration, and message server registration. + +As described in [ADR 006](./adr-006-02-client-refactor.md), generalizing light client behaviour is difficult. +IBC light clients will obtain greater flexibility and control via the registered SDK module approach. + +## Decision + +Instead of using two different approaches to invoking callbacks, IBC light clients should be invoked as SDK modules. +Over time and as necessary, core IBC should adjust its interactions with light clients such that they are SDK modules as opposed to interfaces. + +One immediate decision that has already been applied is to formalize light client type registration via the inclusion of an `AppModuleBasic` within the `ModuleManager` for a chain. +The [tendermint](https://github.com/cosmos/ibc-go/pull/2825) and [solo machine](https://github.com/cosmos/ibc-go/pull/2826) clients were refactored to include this `AppModuleBasic` implementation and core IBC will no longer include either type as registered by default. + +Longer term solutions include using internal module communication as described in [ADR 033](https://github.com/cosmos/cosmos-sdk/blob/main/docs/architecture/adr-033-protobuf-inter-module-comm.md) on the SDK. +The following functions should become callbacks invoked via intermodule communication: +- `Status` +- `GetTimestampAtHeight` +- `VerifyMembership` +- `VerifyNonMembership` +- `Initialize` +- `VerifyClientMessage` +- `CheckForMisbehaviour` +- `UpdateStateOnMisbehaviour` +- `UpdateState` +- `CheckSubstituteAndUpdateState` +- `VerifyUpgradeAndUpdateState` + +The ClientState interface should eventually be trimmed down to something along the lines of: +```go +type ClientState interface { + proto.Message + + ClientType() string + GetLatestHeight() Height + Validate() error + + ZeroCustomFields() ClientState + + // ADDITION + Route() string // route used for intermodule communication +} +``` + +For the most part, any functions which require access to the client store should likely not be an interface function of the `ClientState`. + +`ExportMetadata` should eventually be replaced by a light client's ability to import/export it's own genesis information. + +### Intermodule communication + +To keep the transition from interface callbacks to SDK module callbacks as simple as possible, intermodule communication (when available) should be used to route to light client modules. +Without intermodule communication, a routing system would need to be developed/maintained to register callbacks. +This functionality of routing to another SDK module should and will be provided by the SDK. +Once it is possible to route to SDK modules, a `ClientState` type could expose the function `Route` which returns the callback route used to call the light client module. + +## Consequences + +### Positive +- use a single approach for interacting with callbacks +- greater flexibilty and control for IBC light clients +- does not require developing another routing system + +### Negative +- requires breaking changes +- requires waiting for intermodule communication + +### Neutral +N/A + diff --git a/docs/architecture/adr-015-ibc-packet-receiver.md b/docs/architecture/adr-015-ibc-packet-receiver.md index 26f9bdecc88..08264c6d2ee 100644 --- a/docs/architecture/adr-015-ibc-packet-receiver.md +++ b/docs/architecture/adr-015-ibc-packet-receiver.md @@ -5,7 +5,7 @@ - 2019 Oct 22: Initial Draft ## Context - + [ICS 26 - Routing Module](https://github.com/cosmos/ibc/tree/master/spec/core/ics-026-routing-module) defines a function [`handlePacketRecv`](https://github.com/cosmos/ibc/tree/master/spec/core/ics-026-routing-module#packet-relay). In ICS 26, the routing module is defined as a layer above each application module @@ -154,16 +154,16 @@ func (keeper ChannelKeeper) DeleteCommitmentTimeout(ctx Context, packet Packet) Each application handler should call respective finalization methods on the `PortKeeper` in order to increase sequence (in case of packet) or remove the commitment (in case of acknowledgement and timeout). -Calling those functions implies that the application logic has successfully executed. +Calling those functions implies that the application logic has successfully executed. However, the handlers can return `Result` with `CodeTxBreak` after calling those methods -which will persist the state changes that has been already done but prevent any further +which will persist the state changes that has been already done but prevent any further messages to be executed in case of semantically invalid packet. This will keep the sequence -increased in the previous IBC packets(thus preventing double execution) without +increased in the previous IBC packets(thus preventing double execution) without proceeding to the following messages. -In any case the application modules should never return state reverting result, +In any case the application modules should never return state reverting result, which will make the channel unable to proceed. -`ChannelKeeper.CheckOpen` method will be introduced. This will replace `onChanOpen*` defined +`ChannelKeeper.CheckOpen` method will be introduced. This will replace `onChanOpen*` defined under the routing module specification. Instead of define each channel handshake callback functions, application modules can provide `ChannelChecker` function with the `AppModule` which will be injected to `ChannelKeeper.Port()` at the top level application. diff --git a/docs/architecture/adr-025-ibc-passive-channels.md b/docs/architecture/adr-025-ibc-passive-channels.md index 9b39da98152..35449185a1b 100644 --- a/docs/architecture/adr-025-ibc-passive-channels.md +++ b/docs/architecture/adr-025-ibc-passive-channels.md @@ -34,26 +34,26 @@ const ( AttributeKeyDstVersion = "dst_version" ) // ... - // Emit Event with Channel metadata for the relayer to pick up and - // relay to the other chain - // This appears immediately before the successful return statement. - ctx.EventManager().EmitEvents(sdk.Events{ - sdk.NewEvent( - types.EventTypeChannelMeta, - sdk.NewAttribute(types.AttributeKeyAction, "open_init"), - sdk.NewAttribute(types.AttributeKeySrcConnection, connectionHops[0]), - sdk.NewAttribute(types.AttributeKeyHops, strings.Join(connectionHops, ",")), - sdk.NewAttribute(types.AttributeKeyOrder, order.String()), - sdk.NewAttribute(types.AttributeKeySrcPort, portID), - sdk.NewAttribute(types.AttributeKeySrcChannel, chanenlID), - sdk.NewAttribute(types.AttributeKeySrcVersion, version), - sdk.NewAttribute(types.AttributeKeyDstPort, counterparty.GetPortID()), - sdk.NewAttribute(types.AttributeKeyDstChannel, counterparty.GetChannelID()), - // The destination version is not yet known, but a value is necessary to pad - // the event attribute offsets - sdk.NewAttribute(types.AttributeKeyDstVersion, ""), - ), - }) +// Emit Event with Channel metadata for the relayer to pick up and +// relay to the other chain +// This appears immediately before the successful return statement. +ctx.EventManager().EmitEvents(sdk.Events{ + sdk.NewEvent( + types.EventTypeChannelMeta, + sdk.NewAttribute(types.AttributeKeyAction, "open_init"), + sdk.NewAttribute(types.AttributeKeySrcConnection, connectionHops[0]), + sdk.NewAttribute(types.AttributeKeyHops, strings.Join(connectionHops, ",")), + sdk.NewAttribute(types.AttributeKeyOrder, order.String()), + sdk.NewAttribute(types.AttributeKeySrcPort, portID), + sdk.NewAttribute(types.AttributeKeySrcChannel, chanenlID), + sdk.NewAttribute(types.AttributeKeySrcVersion, version), + sdk.NewAttribute(types.AttributeKeyDstPort, counterparty.GetPortID()), + sdk.NewAttribute(types.AttributeKeyDstChannel, counterparty.GetChannelID()), + // The destination version is not yet known, but a value is necessary to pad + // the event attribute offsets + sdk.NewAttribute(types.AttributeKeyDstVersion, ""), + ), +}) ``` These metadata events capture all the "header" information needed to route IBC channel handshake transactions without requiring the client to query any data except that of the connection ID that it is willing to relay. It is intended that `channel_meta.src_connection` is the only event key that needs to be indexed for a passive relayer to function. @@ -95,19 +95,19 @@ Here is how this callback would be used, in the implementation of `x/ibc/handler ```go // ... - case channel.MsgChannelOpenTry: - // Lookup module by port capability - module, portCap, err := k.PortKeeper.LookupModuleByPort(ctx, msg.PortID) - if err != nil { - return nil, sdkerrors.Wrap(err, "could not retrieve module from port-id") - } - // Retrieve callbacks from router - cbs, ok := k.Router.GetRoute(module) - if !ok { - return nil, sdkerrors.Wrapf(port.ErrInvalidRoute, "route not found to module: %s", module) - } - // Delegate to the module's OnAttemptChanOpenTry. - return cbs.OnAttemptChanOpenTry(ctx, k.ChannelKeeper, portCap, msg) +case channel.MsgChannelOpenTry: + // Lookup module by port capability + module, portCap, err := k.PortKeeper.LookupModuleByPort(ctx, msg.PortID) + if err != nil { + return nil, sdkerrors.Wrap(err, "could not retrieve module from port-id") + } + // Retrieve callbacks from router + cbs, ok := k.Router.GetRoute(module) + if !ok { + return nil, sdkerrors.Wrapf(port.ErrInvalidRoute, "route not found to module: %s", module) + } + // Delegate to the module's OnAttemptChanOpenTry. + return cbs.OnAttemptChanOpenTry(ctx, k.ChannelKeeper, portCap, msg) ``` The reason we do not have a more structured interaction between `x/ibc/handler.go` and the port's module (to explicitly negotiate versions, etc) is that we do not wish to constrain the app module to have to finish handling the `MsgChannelOpenTry` during this transaction or even this block. diff --git a/docs/architecture/adr-026-ibc-client-recovery-mechanisms.md b/docs/architecture/adr-026-ibc-client-recovery-mechanisms.md index bec25a3aad9..e34244f1d6b 100644 --- a/docs/architecture/adr-026-ibc-client-recovery-mechanisms.md +++ b/docs/architecture/adr-026-ibc-client-recovery-mechanisms.md @@ -45,18 +45,18 @@ We elect not to deal with chains which have actually halted, which is necessaril 1. Require Tendermint light clients (ICS 07) to expose the following additional state mutation functions 1. `Unfreeze()`, which unfreezes a light client after misbehaviour and clears any frozen height previously set 1. Add a new governance proposal type, `ClientUpdateProposal`, in the `x/ibc` module - 1. Extend the base `Proposal` with two client identifiers (`string`). + 1. Extend the base `Proposal` with two client identifiers (`string`). 1. The first client identifier is the proposed client to be updated. This client must be either frozen or expired. - 1. The second client is a substitute client. It carries all the state for the client which may be updated. It must have identitical client and chain parameters to the client which may be updated (except for latest height, frozen height, and chain-id). It should be continually updated during the voting period. + 1. The second client is a substitute client. It carries all the state for the client which may be updated. It must have identitical client and chain parameters to the client which may be updated (except for latest height, frozen height, and chain-id). It should be continually updated during the voting period. 1. If this governance proposal passes, the client on trial will be updated to the latest state of the substitute. 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. - + 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. +Note that clients frozen due to misbehaviour must wait for the evidence to expire to avoid becoming refrozen. This ADR does not address planned upgrades, which are handled separately as per the [specification](https://github.com/cosmos/ibc/tree/master/spec/client/ics-007-tendermint-client#upgrades). diff --git a/docs/architecture/adr-027-ibc-wasm.md b/docs/architecture/adr-027-ibc-wasm.md index d105c9854b4..3e1f155f3b6 100644 --- a/docs/architecture/adr-027-ibc-wasm.md +++ b/docs/architecture/adr-027-ibc-wasm.md @@ -6,7 +6,7 @@ ## Status -*Draft* +*Draft, needs updates* ## Abstract @@ -18,6 +18,7 @@ existing IBC light clients as well as adding support for new IBC light clients w hard-fork event. ## Context + Currently in the SDK, light clients are defined as part of the codebase and are implemented as submodules under `ibc-go/core/modules/light-clients/`. @@ -48,7 +49,6 @@ With WASM light client module, anybody can add new IBC light client in the form as well as instantiate clients using any created client type. This allows any chain to update its own light client in other chains without going through steps outlined above. - ## Decision We decided to use WASM light client module as a light client proxy which will interface with the actual light client @@ -58,17 +58,17 @@ has prefix of `wasm/`. ```go // IsAllowedClient checks if the given client type is registered on the allowlist. func (p Params) IsAllowedClient(clientType string) bool { - if p.AreWASMClientsAllowed && isWASMClient(clientType) { - return true - } - - for _, allowedClient := range p.AllowedClients { - if allowedClient == clientType { - return true - } - } - - return false + if p.AreWASMClientsAllowed && isWASMClient(clientType) { + return true + } + + for _, allowedClient := range p.AllowedClients { + if allowedClient == clientType { + return true + } + } + + return false } ``` @@ -77,10 +77,10 @@ processed by IBC Wasm module. ```go func (k Keeper) UploadLightClient (wasmCode: []byte, description: String) { - wasmRegistry = getWASMRegistry() - id := hex.EncodeToString(sha256.Sum256(wasmCode)) - assert(!wasmRegistry.Exists(id)) - assert(wasmRegistry.ValidateAndStoreCode(id, description, wasmCode, false)) + wasmRegistry = getWASMRegistry() + id := hex.EncodeToString(sha256.Sum256(wasmCode)) + assert(!wasmRegistry.Exists(id)) + assert(wasmRegistry.ValidateAndStoreCode(id, description, wasmCode, false)) } ``` @@ -100,48 +100,50 @@ packaged inside a payload which is json serialized and passed to `callContract` array of bytes returned by the smart contract. This data is deserialized and passed as return argument. ```go -func (c *ClientState) CheckProposedHeaderAndUpdateState(context sdk.Context, marshaler codec.BinaryMarshaler, store sdk.KVStore, header exported.Header) (exported.ClientState, exported.ConsensusState, error) { - // get consensus state corresponding to client state to check if the client is expired - consensusState, err := GetConsensusState(store, marshaler, c.LatestHeight) - if err != nil { - return nil, nil, sdkerrors.Wrapf( - err, "could not get consensus state from clientstore at height: %d", c.LatestHeight, - ) - } - - payload := make(map[string]map[string]interface{}) - payload[CheckProposedHeaderAndUpdateState] = make(map[string]interface{}) - inner := payload[CheckProposedHeaderAndUpdateState] - inner["me"] = c - inner["header"] = header - inner["consensus_state"] = consensusState - - encodedData, err := json.Marshal(payload) - if err != nil { - return nil, nil, sdkerrors.Wrapf(ErrUnableToMarshalPayload, fmt.Sprintf("underlying error: %s", err.Error())) - } - out, err := callContract(c.CodeId, context, store, encodedData) - if err != nil { - return nil, nil, sdkerrors.Wrapf(ErrUnableToCall, fmt.Sprintf("underlying error: %s", err.Error())) - } - output := clientStateCallResponse{} - if err := json.Unmarshal(out.Data, &output); err != nil { - return nil, nil, sdkerrors.Wrapf(ErrUnableToUnmarshalPayload, fmt.Sprintf("underlying error: %s", err.Error())) - } - if !output.Result.IsValid { - return nil, nil, fmt.Errorf("%s error ocurred while updating client state", output.Result.ErrorMsg) - } - output.resetImmutables(c) - return output.NewClientState, output.NewConsensusState, nil +func (c *ClientState) CheckProposedHeaderAndUpdateState(context sdk.Context, marshaler codec.BinaryMarshaler, store sdk.KVStore, header exported.ClientMessage) (exported.ClientState, exported.ConsensusState, error) { + // get consensus state corresponding to client state to check if the client is expired + consensusState, err := GetConsensusState(store, marshaler, c.LatestHeight) + if err != nil { + return nil, nil, sdkerrors.Wrapf( + err, "could not get consensus state from clientstore at height: %d", c.LatestHeight, + ) + } + + payload := make(map[string]map[string]interface{}) + payload[CheckProposedHeaderAndUpdateState] = make(map[string]interface{}) + inner := payload[CheckProposedHeaderAndUpdateState] + inner["me"] = c + inner["header"] = header + inner["consensus_state"] = consensusState + + encodedData, err := json.Marshal(payload) + if err != nil { + return nil, nil, sdkerrors.Wrapf(ErrUnableToMarshalPayload, fmt.Sprintf("underlying error: %s", err.Error())) + } + out, err := callContract(c.CodeId, context, store, encodedData) + if err != nil { + return nil, nil, sdkerrors.Wrapf(ErrUnableToCall, fmt.Sprintf("underlying error: %s", err.Error())) + } + output := clientStateCallResponse{} + if err := json.Unmarshal(out.Data, &output); err != nil { + return nil, nil, sdkerrors.Wrapf(ErrUnableToUnmarshalPayload, fmt.Sprintf("underlying error: %s", err.Error())) + } + if !output.Result.IsValid { + return nil, nil, fmt.Errorf("%s error ocurred while updating client state", output.Result.ErrorMsg) + } + output.resetImmutables(c) + return output.NewClientState, output.NewConsensusState, nil } ``` ## Consequences ### Positive + - Adding support for new light client or upgrading existing light client is way easier than before and only requires single transaction. - Improves maintainability of Cosmos SDK, since no change in codebase is required to support new client or upgrade it. ### Negative + - Light clients need to be written in subset of rust which could compile in Wasm. - Introspecting light client code is difficult as only compiled bytecode exists in the blockchain. diff --git a/docs/architecture/adr-template.md b/docs/architecture/adr-template.md index 129ddfa8f94..95ef45037de 100644 --- a/docs/architecture/adr-template.md +++ b/docs/architecture/adr-template.md @@ -1,6 +1,7 @@ # ADR {ADR-NUMBER}: {TITLE} ## Changelog + * {date}: {changelog} ## Status @@ -11,7 +12,7 @@ ## Context -> This section contains all the context one needs to understand the current state, and why there is a problem. It should be as succinct as possible and introduce the high level idea behind the solution. +> This section contains all the context one needs to understand the current state, and why there is a problem. It should be as succinct as possible and introduce the high level idea behind the solution. ## Decision diff --git a/docs/assets/auth-module-decision-tree.png b/docs/assets/auth-module-decision-tree.png new file mode 100644 index 0000000000000000000000000000000000000000..dc2c98e6618328d7b0d9e49f0f41fee310cdd873 GIT binary patch literal 80522 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

D+XCvL^C9DzEhQiAH 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 z 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

-X0lt-%*Y^>T_m~>6d~+yx&*Qo+&8Y7S|QQ1 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 Lt8i2_F8RWS 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�6fslqOEhRSQJK6rLluuhu+>zDy@VbCsxkb|NX~ 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 EscrowAddress returns the escrow address for a particular port and @@ -23,7 +23,7 @@ paths: QueryEscrowAddressResponse is the response type of the EscrowAddress RPC method. default: - description: An unexpected error response + description: An unexpected error response. schema: type: object properties: @@ -57,7 +57,7 @@ paths: type: string tags: - Query - '/ibc/apps/transfer/v1/denom_hashes/{trace}': + /ibc/apps/transfer/v1/denom_hashes/{trace}: get: summary: DenomHash queries a denomination hash information. operationId: DenomHash @@ -76,7 +76,7 @@ paths: method. default: - description: An unexpected error response + description: An unexpected error response. schema: type: object properties: @@ -99,7 +99,7 @@ paths: format: byte parameters: - name: trace - description: 'The denomination trace ([port_id]/[channel_id])+/[denom]' + description: The denomination trace ([port_id]/[channel_id])+/[denom] in: path required: true type: string @@ -143,9 +143,10 @@ paths: next_key: type: string format: byte - title: |- + description: |- next_key is the key to be passed to PageRequest.key to - query the next page most efficiently + query the next page most efficiently. It will be empty if + there are no more results. total: type: string format: uint64 @@ -160,7 +161,7 @@ paths: method. default: - description: An unexpected error response + description: An unexpected error response. schema: type: object properties: @@ -228,10 +229,19 @@ paths: in: query required: false type: boolean - format: boolean + - name: pagination.reverse + description: >- + reverse is set to true if results are to be returned in the + descending order. + + + Since: cosmos-sdk 0.43 + in: query + required: false + type: boolean tags: - Query - '/ibc/apps/transfer/v1/denom_traces/{hash}': + /ibc/apps/transfer/v1/denom_traces/{hash}: get: summary: DenomTrace queries a denomination trace information. operationId: DenomTrace @@ -265,7 +275,7 @@ paths: method. default: - description: An unexpected error response + description: An unexpected error response. schema: type: object properties: @@ -312,7 +322,6 @@ paths: properties: send_enabled: type: boolean - format: boolean description: >- send_enabled enables or disables all cross-chain token transfers from this @@ -320,7 +329,6 @@ paths: chain. receive_enabled: type: boolean - format: boolean description: >- receive_enabled enables or disables all cross-chain token transfers to this @@ -330,7 +338,48 @@ paths: QueryParamsResponse is the response type for the Query/Params RPC method. default: - description: An unexpected error response + 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 + 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: @@ -351,6 +400,15 @@ paths: 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: @@ -369,7 +427,6 @@ paths: properties: controller_enabled: type: boolean - format: boolean description: >- controller_enabled enables or disables the controller submodule. @@ -377,7 +434,7 @@ paths: QueryParamsResponse is the response type for the Query/Params RPC method. default: - description: An unexpected error response + description: An unexpected error response. schema: type: object properties: @@ -416,7 +473,6 @@ paths: properties: host_enabled: type: boolean - format: boolean description: host_enabled enables or disables the host submodule. allow_messages: type: array @@ -429,7 +485,7 @@ paths: QueryParamsResponse is the response type for the Query/Params RPC method. default: - description: An unexpected error response + description: An unexpected error response. schema: type: object properties: @@ -452,7 +508,7 @@ paths: format: byte tags: - Query - '/ibc/apps/fee/v1/channels/{channel_id}/ports/{port_id}/fee_enabled': + /ibc/apps/fee/v1/channels/{channel_id}/ports/{port_id}/fee_enabled: get: summary: >- FeeEnabledChannel returns true if the provided port and channel @@ -466,13 +522,12 @@ paths: properties: fee_enabled: type: boolean - format: boolean title: boolean flag representing the fee enabled channel status title: >- QueryFeeEnabledChannelResponse defines the response type for the FeeEnabledChannel rpc default: - description: An unexpected error response + description: An unexpected error response. schema: type: object properties: @@ -598,10 +653,13 @@ paths: Example 4: Pack and unpack a message in Go foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) + any, err := anypb.New(foo) + if err != nil { + ... + } ... foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { + if err := any.UnmarshalTo(foo); err != nil { ... } @@ -673,7 +731,7 @@ paths: type: string tags: - Query - '/ibc/apps/fee/v1/channels/{channel_id}/ports/{port_id}/incentivized_packets': + /ibc/apps/fee/v1/channels/{channel_id}/ports/{port_id}/incentivized_packets: get: summary: Gets all incentivized packets for a specific channel operationId: IncentivizedPacketsForChannel @@ -794,7 +852,7 @@ paths: QueryIncentivizedPacketsResponse defines the response type for the incentivized packets RPC default: - description: An unexpected error response + description: An unexpected error response. schema: type: object properties: @@ -920,10 +978,13 @@ paths: Example 4: Pack and unpack a message in Go foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) + any, err := anypb.New(foo) + if err != nil { + ... + } ... foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { + if err := any.UnmarshalTo(foo); err != nil { ... } @@ -1037,7 +1098,16 @@ paths: in: query required: false type: boolean - format: boolean + - name: pagination.reverse + description: >- + reverse is set to true if results are to be returned in the + descending order. + + + Since: cosmos-sdk 0.43 + in: query + required: false + type: boolean - name: query_height description: Height to query at. in: query @@ -1046,7 +1116,7 @@ paths: format: uint64 tags: - Query - '/ibc/apps/fee/v1/channels/{channel_id}/relayers/{relayer}/counterparty_payee': + /ibc/apps/fee/v1/channels/{channel_id}/relayers/{relayer}/counterparty_payee: get: summary: >- CounterpartyPayee returns the registered counterparty payee for forward @@ -1067,7 +1137,7 @@ paths: QueryCounterpartyPayeeResponse defines the response type for the CounterpartyPayee rpc default: - description: An unexpected error response + description: An unexpected error response. schema: type: object properties: @@ -1193,10 +1263,13 @@ paths: Example 4: Pack and unpack a message in Go foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) + any, err := anypb.New(foo) + if err != nil { + ... + } ... foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { + if err := any.UnmarshalTo(foo); err != nil { ... } @@ -1268,7 +1341,7 @@ paths: type: string tags: - Query - '/ibc/apps/fee/v1/channels/{channel_id}/relayers/{relayer}/payee': + /ibc/apps/fee/v1/channels/{channel_id}/relayers/{relayer}/payee: get: summary: >- Payee returns the registered payee address for a specific channel given @@ -1285,7 +1358,7 @@ paths: title: the payee address to which packet fees are paid out title: QueryPayeeResponse defines the response type for the Payee rpc default: - description: An unexpected error response + description: An unexpected error response. schema: type: object properties: @@ -1411,10 +1484,13 @@ paths: Example 4: Pack and unpack a message in Go foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) + any, err := anypb.New(foo) + if err != nil { + ... + } ... foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { + if err := any.UnmarshalTo(foo); err != nil { ... } @@ -1486,7 +1562,7 @@ paths: type: string tags: - Query - '/ibc/apps/fee/v1/channels/{packet_id.channel_id}/ports/{packet_id.port_id}/sequences/{packet_id.sequence}/incentivized_packet': + /ibc/apps/fee/v1/channels/{packet_id.channel_id}/ports/{packet_id.port_id}/sequences/{packet_id.sequence}/incentivized_packet: get: summary: >- IncentivizedPacket returns all packet fees for a packet given its @@ -1604,7 +1680,7 @@ paths: QueryIncentivizedPacketsResponse defines the response type for the IncentivizedPacket rpc default: - description: An unexpected error response + description: An unexpected error response. schema: type: object properties: @@ -1730,10 +1806,13 @@ paths: Example 4: Pack and unpack a message in Go foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) + any, err := anypb.New(foo) + if err != nil { + ... + } ... foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { + if err := any.UnmarshalTo(foo); err != nil { ... } @@ -1817,7 +1896,7 @@ paths: format: uint64 tags: - Query - '/ibc/apps/fee/v1/channels/{packet_id.channel_id}/ports/{packet_id.port_id}/sequences/{packet_id.sequence}/total_ack_fees': + /ibc/apps/fee/v1/channels/{packet_id.channel_id}/ports/{packet_id.port_id}/sequences/{packet_id.sequence}/total_ack_fees: get: summary: >- TotalAckFees returns the total acknowledgement fees for a packet given @@ -1851,7 +1930,7 @@ paths: QueryTotalAckFeesResponse defines the response type for the TotalAckFees rpc default: - description: An unexpected error response + description: An unexpected error response. schema: type: object properties: @@ -1977,10 +2056,13 @@ paths: Example 4: Pack and unpack a message in Go foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) + any, err := anypb.New(foo) + if err != nil { + ... + } ... foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { + if err := any.UnmarshalTo(foo); err != nil { ... } @@ -2058,7 +2140,7 @@ paths: format: uint64 tags: - Query - '/ibc/apps/fee/v1/channels/{packet_id.channel_id}/ports/{packet_id.port_id}/sequences/{packet_id.sequence}/total_recv_fees': + /ibc/apps/fee/v1/channels/{packet_id.channel_id}/ports/{packet_id.port_id}/sequences/{packet_id.sequence}/total_recv_fees: get: summary: >- TotalRecvFees returns the total receive fees for a packet given its @@ -2092,7 +2174,7 @@ paths: QueryTotalRecvFeesResponse defines the response type for the TotalRecvFees rpc default: - description: An unexpected error response + description: An unexpected error response. schema: type: object properties: @@ -2218,10 +2300,13 @@ paths: Example 4: Pack and unpack a message in Go foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) + any, err := anypb.New(foo) + if err != nil { + ... + } ... foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { + if err := any.UnmarshalTo(foo); err != nil { ... } @@ -2299,7 +2384,7 @@ paths: format: uint64 tags: - Query - '/ibc/apps/fee/v1/channels/{packet_id.channel_id}/ports/{packet_id.port_id}/sequences/{packet_id.sequence}/total_timeout_fees': + /ibc/apps/fee/v1/channels/{packet_id.channel_id}/ports/{packet_id.port_id}/sequences/{packet_id.sequence}/total_timeout_fees: get: summary: >- TotalTimeoutFees returns the total timeout fees for a packet given its @@ -2333,7 +2418,7 @@ paths: QueryTotalTimeoutFeesResponse defines the response type for the TotalTimeoutFees rpc default: - description: An unexpected error response + description: An unexpected error response. schema: type: object properties: @@ -2459,10 +2544,13 @@ paths: Example 4: Pack and unpack a message in Go foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) + any, err := anypb.New(foo) + if err != nil { + ... + } ... foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { + if err := any.UnmarshalTo(foo); err != nil { ... } @@ -2569,7 +2657,7 @@ paths: QueryFeeEnabledChannelsResponse defines the response type for the FeeEnabledChannels rpc default: - description: An unexpected error response + description: An unexpected error response. schema: type: object properties: @@ -2695,10 +2783,13 @@ paths: Example 4: Pack and unpack a message in Go foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) + any, err := anypb.New(foo) + if err != nil { + ... + } ... foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { + if err := any.UnmarshalTo(foo); err != nil { ... } @@ -2804,7 +2895,16 @@ paths: in: query required: false type: boolean - format: boolean + - name: pagination.reverse + description: >- + reverse is set to true if results are to be returned in the + descending order. + + + Since: cosmos-sdk 0.43 + in: query + required: false + type: boolean - name: query_height description: block height at which to query. in: query @@ -2936,7 +3036,7 @@ paths: QueryIncentivizedPacketsResponse defines the response type for the IncentivizedPackets rpc default: - description: An unexpected error response + description: An unexpected error response. schema: type: object properties: @@ -3062,10 +3162,13 @@ paths: Example 4: Pack and unpack a message in Go foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) + any, err := anypb.New(foo) + if err != nil { + ... + } ... foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { + if err := any.UnmarshalTo(foo); err != nil { ... } @@ -3171,7 +3274,16 @@ paths: in: query required: false type: boolean - format: boolean + - name: pagination.reverse + description: >- + reverse is set to true if results are to be returned in the + descending order. + + + Since: cosmos-sdk 0.43 + in: query + required: false + type: boolean - name: query_height description: block height at which to query. in: query @@ -3180,333 +3292,117 @@ paths: format: uint64 tags: - Query - /ibc/client/v1/params: + /ibc/core/client/v1/client_states: get: - summary: ClientParams queries all parameters of the ibc client. - operationId: ClientParams + summary: ClientStates queries all the IBC light clients of a chain. + operationId: ClientStates responses: '200': description: A successful response. schema: type: object properties: - params: - description: params defines the parameters of the module. - type: object - properties: - allowed_clients: - type: array - items: - type: string - description: >- - allowed_clients defines the list of allowed client state - types. - description: >- - QueryClientParamsResponse is the response type for the - Query/ClientParams RPC - - method. - default: - description: An unexpected error response - schema: - type: object - properties: - error: - type: string - code: - type: integer - format: int32 - message: - type: string - details: + client_states: type: array items: type: object properties: - type_url: + client_id: type: string - description: >- - A URL/resource name that uniquely identifies the type of - the serialized + title: client identifier + client_state: + title: client state + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the + type of the serialized - protocol buffer message. This string must contain at - least + protocol buffer message. This string must contain at + least - one "/" character. The last segment of the URL's path - must represent + one "/" character. The last segment of the URL's + path must represent - the fully qualified name of the type (as in + the fully qualified name of the type (as in - `path/google.protobuf.Duration`). The name should be in - a canonical form + `path/google.protobuf.Duration`). The name should be + in a canonical form - (e.g., leading "." is not accepted). + (e.g., leading "." is not accepted). - In practice, teams usually precompile into the binary - all types that they + In practice, teams usually precompile into the + binary all types that they - expect it to use in the context of Any. However, for - URLs which use the + expect it to use in the context of Any. However, for + URLs which use the - scheme `http`, `https`, or no scheme, one can optionally - set up a type + scheme `http`, `https`, or no scheme, one can + optionally set up a type - server that maps type URLs to message definitions as - follows: + server that maps type URLs to message definitions as + follows: - * If no scheme is provided, `https` is assumed. + * If no scheme is provided, `https` is assumed. - * An HTTP GET on the URL must yield a - [google.protobuf.Type][] - value in binary format, or produce an error. - * Applications are allowed to cache lookup results based - on the - URL, or have them precompiled into a binary to avoid any - lookup. Therefore, binary compatibility needs to be preserved - on changes to types. (Use versioned type names to manage - breaking changes.) + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results + based on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) - Note: this functionality is not currently available in - the official + Note: this functionality is not currently available + in the official - protobuf release, and it is not used for type URLs - beginning with + protobuf release, and it is not used for type URLs + beginning with - type.googleapis.com. + type.googleapis.com. - Schemes other than `http`, `https` (or the empty scheme) - might be + Schemes other than `http`, `https` (or the empty + scheme) might be - used with implementation specific semantics. - value: - type: string - format: byte + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the + above specified type. description: >- - Must be a valid serialized protocol buffer of the above - specified type. - description: >- - `Any` contains an arbitrary serialized protocol buffer - message along with a - - URL that describes the type of the serialized message. - + `Any` contains an arbitrary serialized protocol buffer + message along with a - Protobuf library provides support to pack/unpack Any values - in the form + URL that describes the type of the serialized message. - of utility functions or additional generated methods of the - Any type. + Protobuf library provides support to pack/unpack Any + values in the form - Example 1: Pack and unpack a message in C++. + of utility functions or additional generated methods of + the Any type. - Foo foo = ...; - Any any; - any.PackFrom(foo); - ... - if (any.UnpackTo(&foo)) { - ... - } - Example 2: Pack and unpack a message in Java. + Example 1: Pack and unpack a message in C++. - Foo foo = ...; - Any any = Any.pack(foo); - ... - if (any.is(Foo.class)) { - foo = any.unpack(Foo.class); - } + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } - Example 3: Pack and unpack a message in Python. - - foo = Foo(...) - any = Any() - any.Pack(foo) - ... - if any.Is(Foo.DESCRIPTOR): - any.Unpack(foo) - ... - - Example 4: Pack and unpack a message in Go - - foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) - ... - foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { - ... - } - - The pack methods provided by protobuf library will by - default use - - 'type.googleapis.com/full.type.name' as the type URL and the - unpack - - methods only use the fully qualified type name after the - last '/' - - in the type URL, for example "foo.bar.com/x/y.z" will yield - type - - name "y.z". - - - - JSON - - ==== - - The JSON representation of an `Any` value uses the regular - - representation of the deserialized, embedded message, with - an - - additional field `@type` which contains the type URL. - Example: - - package google.profile; - message Person { - string first_name = 1; - string last_name = 2; - } - - { - "@type": "type.googleapis.com/google.profile.Person", - "firstName": , - "lastName": - } - - If the embedded message type is well-known and has a custom - JSON - - representation, that representation will be embedded adding - a field - - `value` which holds the custom JSON in addition to the - `@type` - - field. Example (for message [google.protobuf.Duration][]): - - { - "@type": "type.googleapis.com/google.protobuf.Duration", - "value": "1.212s" - } - tags: - - Query - /ibc/core/client/v1/client_states: - get: - summary: ClientStates queries all the IBC light clients of a chain. - operationId: ClientStates - responses: - '200': - description: A successful response. - schema: - type: object - properties: - client_states: - type: array - items: - type: object - properties: - client_id: - type: string - title: client identifier - client_state: - type: object - properties: - type_url: - type: string - description: >- - A URL/resource name that uniquely identifies the - type of the serialized - - protocol buffer message. This string must contain at - least - - one "/" character. The last segment of the URL's - path must represent - - the fully qualified name of the type (as in - - `path/google.protobuf.Duration`). The name should be - in a canonical form - - (e.g., leading "." is not accepted). - - - In practice, teams usually precompile into the - binary all types that they - - expect it to use in the context of Any. However, for - URLs which use the - - scheme `http`, `https`, or no scheme, one can - optionally set up a type - - server that maps type URLs to message definitions as - follows: - - - * If no scheme is provided, `https` is assumed. - - * An HTTP GET on the URL must yield a - [google.protobuf.Type][] - value in binary format, or produce an error. - * Applications are allowed to cache lookup results - based on the - URL, or have them precompiled into a binary to avoid any - lookup. Therefore, binary compatibility needs to be preserved - on changes to types. (Use versioned type names to manage - breaking changes.) - - Note: this functionality is not currently available - in the official - - protobuf release, and it is not used for type URLs - beginning with - - type.googleapis.com. - - - Schemes other than `http`, `https` (or the empty - scheme) might be - - used with implementation specific semantics. - value: - type: string - format: byte - description: >- - Must be a valid serialized protocol buffer of the - above specified type. - description: >- - `Any` contains an arbitrary serialized protocol buffer - message along with a - - URL that describes the type of the serialized message. - - - Protobuf library provides support to pack/unpack Any - values in the form - - of utility functions or additional generated methods of - the Any type. - - - Example 1: Pack and unpack a message in C++. - - Foo foo = ...; - Any any; - any.PackFrom(foo); - ... - if (any.UnpackTo(&foo)) { - ... - } - - Example 2: Pack and unpack a message in Java. + Example 2: Pack and unpack a message in Java. Foo foo = ...; Any any = Any.pack(foo); @@ -3528,10 +3424,13 @@ paths: Example 4: Pack and unpack a message in Go foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) + any, err := anypb.New(foo) + if err != nil { + ... + } ... foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { + if err := any.UnmarshalTo(foo); err != nil { ... } @@ -3592,7 +3491,6 @@ paths: "@type": "type.googleapis.com/google.protobuf.Duration", "value": "1.212s" } - title: client state description: >- IdentifiedClientState defines a client state with an additional client @@ -3606,9 +3504,10 @@ paths: next_key: type: string format: byte - title: |- + description: |- next_key is the key to be passed to PageRequest.key to - query the next page most efficiently + query the next page most efficiently. It will be empty if + there are no more results. total: type: string format: uint64 @@ -3633,7 +3532,7 @@ paths: method. default: - description: An unexpected error response + description: An unexpected error response. schema: type: object properties: @@ -3759,10 +3658,13 @@ paths: Example 4: Pack and unpack a message in Go foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) + any, err := anypb.New(foo) + if err != nil { + ... + } ... foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { + if err := any.UnmarshalTo(foo); err != nil { ... } @@ -3868,10 +3770,19 @@ paths: in: query required: false type: boolean - format: boolean + - name: pagination.reverse + description: >- + reverse is set to true if results are to be returned in the + descending order. + + + Since: cosmos-sdk 0.43 + in: query + required: false + type: boolean tags: - Query - '/ibc/core/client/v1/client_states/{client_id}': + /ibc/core/client/v1/client_states/{client_id}: get: summary: ClientState queries an IBC light client. operationId: ClientState @@ -3993,10 +3904,13 @@ paths: Example 4: Pack and unpack a message in Go foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) + any, err := anypb.New(foo) + if err != nil { + ... + } ... foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { + if err := any.UnmarshalTo(foo); err != nil { ... } @@ -4095,7 +4009,7 @@ paths: which the proof was retrieved. default: - description: An unexpected error response + description: An unexpected error response. schema: type: object properties: @@ -4221,10 +4135,13 @@ paths: Example 4: Pack and unpack a message in Go foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) + any, err := anypb.New(foo) + if err != nil { + ... + } ... foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { + if err := any.UnmarshalTo(foo); err != nil { ... } @@ -4291,7 +4208,7 @@ paths: type: string tags: - Query - '/ibc/core/client/v1/client_status/{client_id}': + /ibc/core/client/v1/client_status/{client_id}: get: summary: Status queries the status of an IBC client. operationId: ClientStatus @@ -4309,7 +4226,7 @@ paths: method. It returns the current status of the IBC client. default: - description: An unexpected error response + description: An unexpected error response. schema: type: object properties: @@ -4435,10 +4352,13 @@ paths: Example 4: Pack and unpack a message in Go foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) + any, err := anypb.New(foo) + if err != nil { + ... + } ... foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { + if err := any.UnmarshalTo(foo); err != nil { ... } @@ -4505,7 +4425,7 @@ paths: type: string tags: - Query - '/ibc/core/client/v1/consensus_states/{client_id}': + /ibc/core/client/v1/consensus_states/{client_id}: get: summary: |- ConsensusStates queries all the consensus state associated with a given @@ -4664,10 +4584,13 @@ paths: Example 4: Pack and unpack a message in Go foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) + any, err := anypb.New(foo) + if err != nil { + ... + } ... foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { + if err := any.UnmarshalTo(foo); err != nil { ... } @@ -4742,9 +4665,10 @@ paths: next_key: type: string format: byte - title: |- + description: |- next_key is the key to be passed to PageRequest.key to - query the next page most efficiently + query the next page most efficiently. It will be empty if + there are no more results. total: type: string format: uint64 @@ -4767,7 +4691,7 @@ paths: QueryConsensusStatesResponse is the response type for the Query/ConsensusStates RPC method default: - description: An unexpected error response + description: An unexpected error response. schema: type: object properties: @@ -4893,10 +4817,13 @@ paths: Example 4: Pack and unpack a message in Go foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) + any, err := anypb.New(foo) + if err != nil { + ... + } ... foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { + if err := any.UnmarshalTo(foo); err != nil { ... } @@ -5007,10 +4934,19 @@ paths: in: query required: false type: boolean - format: boolean + - name: pagination.reverse + description: >- + reverse is set to true if results are to be returned in the + descending order. + + + Since: cosmos-sdk 0.43 + in: query + required: false + type: boolean tags: - Query - '/ibc/core/client/v1/consensus_states/{client_id}/heights': + /ibc/core/client/v1/consensus_states/{client_id}/heights: get: summary: >- ConsensusStateHeights queries the height of every consensus states @@ -5067,9 +5003,10 @@ paths: next_key: type: string format: byte - title: |- + description: |- next_key is the key to be passed to PageRequest.key to - query the next page most efficiently + query the next page most efficiently. It will be empty if + there are no more results. total: type: string format: uint64 @@ -5092,7 +5029,7 @@ paths: QueryConsensusStateHeightsResponse is the response type for the Query/ConsensusStateHeights RPC method default: - description: An unexpected error response + description: An unexpected error response. schema: type: object properties: @@ -5218,10 +5155,13 @@ paths: Example 4: Pack and unpack a message in Go foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) + any, err := anypb.New(foo) + if err != nil { + ... + } ... foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { + if err := any.UnmarshalTo(foo); err != nil { ... } @@ -5332,10 +5272,19 @@ paths: in: query required: false type: boolean - format: boolean + - name: pagination.reverse + description: >- + reverse is set to true if results are to be returned in the + descending order. + + + Since: cosmos-sdk 0.43 + in: query + required: false + type: boolean tags: - Query - '/ibc/core/client/v1/consensus_states/{client_id}/revision/{revision_number}/height/{revision_height}': + /ibc/core/client/v1/consensus_states/{client_id}/revision/{revision_number}/height/{revision_height}: get: summary: >- ConsensusState queries a consensus state associated with a client state @@ -5461,10 +5410,13 @@ paths: Example 4: Pack and unpack a message in Go foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) + any, err := anypb.New(foo) + if err != nil { + ... + } ... foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { + if err := any.UnmarshalTo(foo); err != nil { ... } @@ -5568,7 +5520,7 @@ paths: RPC method default: - description: An unexpected error response + description: An unexpected error response. schema: type: object properties: @@ -5694,10 +5646,13 @@ paths: Example 4: Pack and unpack a message in Go foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) + any, err := anypb.New(foo) + if err != nil { + ... + } ... foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { + if err := any.UnmarshalTo(foo); err != nil { ... } @@ -5779,11 +5734,230 @@ paths: latest_height overrrides the height field and queries the latest stored - ConsensusState. - in: query - required: false - type: boolean - format: boolean + ConsensusState. + in: query + required: false + type: boolean + tags: + - Query + /ibc/core/client/v1/params: + get: + summary: ClientParams queries all parameters of the ibc client submodule. + operationId: ClientParams + responses: + '200': + description: A successful response. + schema: + type: object + properties: + params: + description: params defines the parameters of the module. + type: object + properties: + allowed_clients: + type: array + items: + type: string + description: >- + allowed_clients defines the list of allowed client state + types. + description: >- + QueryClientParamsResponse is the response type for the + Query/ClientParams 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 + description: >- + A URL/resource name that uniquely identifies the type of + the serialized + + protocol buffer message. This string must contain at + least + + one "/" character. The last segment of the URL's path + must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in + a canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary + all types that they + + expect it to use in the context of Any. However, for + URLs which use the + + scheme `http`, `https`, or no scheme, one can optionally + set up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based + on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in + the official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) + might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values + in the form + + of utility functions or additional generated methods of the + Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := anypb.New(foo) + if err != nil { + ... + } + ... + foo := &pb.Foo{} + if err := any.UnmarshalTo(foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by + default use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the + last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield + type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with + an + + additional field `@type` which contains the type URL. + Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom + JSON + + representation, that representation will be embedded adding + a field + + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } tags: - Query /ibc/core/client/v1/upgraded_client_states: @@ -5908,10 +6082,13 @@ paths: Example 4: Pack and unpack a message in Go foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) + any, err := anypb.New(foo) + if err != nil { + ... + } ... foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { + if err := any.UnmarshalTo(foo); err != nil { ... } @@ -5972,7 +6149,7 @@ paths: QueryUpgradedClientStateResponse is the response type for the Query/UpgradedClientState RPC method. default: - description: An unexpected error response + description: An unexpected error response. schema: type: object properties: @@ -6098,10 +6275,13 @@ paths: Example 4: Pack and unpack a message in Go foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) + any, err := anypb.New(foo) + if err != nil { + ... + } ... foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { + if err := any.UnmarshalTo(foo); err != nil { ... } @@ -6284,10 +6464,13 @@ paths: Example 4: Pack and unpack a message in Go foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) + any, err := anypb.New(foo) + if err != nil { + ... + } ... foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { + if err := any.UnmarshalTo(foo); err != nil { ... } @@ -6348,7 +6531,7 @@ paths: QueryUpgradedConsensusStateResponse is the response type for the Query/UpgradedConsensusState RPC method. default: - description: An unexpected error response + description: An unexpected error response. schema: type: object properties: @@ -6474,10 +6657,13 @@ paths: Example 4: Pack and unpack a message in Go foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) + any, err := anypb.New(foo) + if err != nil { + ... + } ... foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { + if err := any.UnmarshalTo(foo); err != nil { ... } @@ -6538,7 +6724,7 @@ paths: } tags: - Query - '/ibc/core/connection/v1/client_connections/{client_id}': + /ibc/core/connection/v1/client_connections/{client_id}: get: summary: |- ClientConnections queries the connection paths associated with a client @@ -6592,7 +6778,7 @@ paths: QueryClientConnectionsResponse is the response type for the Query/ClientConnections RPC method default: - description: An unexpected error response + description: An unexpected error response. schema: type: object properties: @@ -6718,10 +6904,13 @@ paths: Example 4: Pack and unpack a message in Go foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) + any, err := anypb.New(foo) + if err != nil { + ... + } ... foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { + if err := any.UnmarshalTo(foo); err != nil { ... } @@ -6892,9 +7081,10 @@ paths: next_key: type: string format: byte - title: |- + description: |- next_key is the key to be passed to PageRequest.key to - query the next page most efficiently + query the next page most efficiently. It will be empty if + there are no more results. total: type: string format: uint64 @@ -6948,7 +7138,7 @@ paths: method. default: - description: An unexpected error response + description: An unexpected error response. schema: type: object properties: @@ -7074,10 +7264,13 @@ paths: Example 4: Pack and unpack a message in Go foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) + any, err := anypb.New(foo) + if err != nil { + ... + } ... foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { + if err := any.UnmarshalTo(foo); err != nil { ... } @@ -7183,10 +7376,19 @@ paths: in: query required: false type: boolean - format: boolean + - name: pagination.reverse + description: >- + reverse is set to true if results are to be returned in the + descending order. + + + Since: cosmos-sdk 0.43 + in: query + required: false + type: boolean tags: - Query - '/ibc/core/connection/v1/connections/{connection_id}': + /ibc/core/connection/v1/connections/{connection_id}: get: summary: Connection queries an IBC connection end. operationId: Connection @@ -7331,7 +7533,7 @@ paths: which the proof was retrieved. default: - description: An unexpected error response + description: An unexpected error response. schema: type: object properties: @@ -7457,10 +7659,13 @@ paths: Example 4: Pack and unpack a message in Go foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) + any, err := anypb.New(foo) + if err != nil { + ... + } ... foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { + if err := any.UnmarshalTo(foo); err != nil { ... } @@ -7527,7 +7732,7 @@ paths: type: string tags: - Query - '/ibc/core/connection/v1/connections/{connection_id}/client_state': + /ibc/core/connection/v1/connections/{connection_id}/client_state: get: summary: |- ConnectionClientState queries the client state associated with the @@ -7659,10 +7864,13 @@ paths: Example 4: Pack and unpack a message in Go foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) + any, err := anypb.New(foo) + if err != nil { + ... + } ... foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { + if err := any.UnmarshalTo(foo); err != nil { ... } @@ -7764,7 +7972,7 @@ paths: QueryConnectionClientStateResponse is the response type for the Query/ConnectionClientState RPC method default: - description: An unexpected error response + description: An unexpected error response. schema: type: object properties: @@ -7890,10 +8098,13 @@ paths: Example 4: Pack and unpack a message in Go foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) + any, err := anypb.New(foo) + if err != nil { + ... + } ... foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { + if err := any.UnmarshalTo(foo); err != nil { ... } @@ -7960,7 +8171,7 @@ paths: type: string tags: - Query - '/ibc/core/connection/v1/connections/{connection_id}/consensus_state/revision/{revision_number}/height/{revision_height}': + /ibc/core/connection/v1/connections/{connection_id}/consensus_state/revision/{revision_number}/height/{revision_height}: get: summary: |- ConnectionConsensusState queries the consensus state associated with the @@ -8084,10 +8295,13 @@ paths: Example 4: Pack and unpack a message in Go foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) + any, err := anypb.New(foo) + if err != nil { + ... + } ... foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { + if err := any.UnmarshalTo(foo); err != nil { ... } @@ -8123,68 +8337,307 @@ paths: string last_name = 2; } - { - "@type": "type.googleapis.com/google.profile.Person", - "firstName": , - "lastName": - } + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom + JSON + + representation, that representation will be embedded adding a + field + + `value` which holds the custom JSON in addition to the `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + title: consensus state associated with the channel + client_id: + type: string + title: client ID associated with the consensus state + proof: + type: string + format: byte + title: merkle proof of existence + proof_height: + title: height at which the proof was retrieved + type: object + properties: + revision_number: + type: string + format: uint64 + title: the revision that the client is currently on + revision_height: + type: string + format: uint64 + title: the height within the given revision + description: >- + Normally the RevisionHeight is incremented at each height + while keeping + + RevisionNumber the same. However some consensus algorithms may + choose to + + reset the height in certain conditions e.g. hard forks, + state-machine + + breaking changes In these cases, the RevisionNumber is + incremented so that + + height continues to be monitonically increasing even as the + RevisionHeight + + gets reset + title: |- + QueryConnectionConsensusStateResponse is the response type for the + Query/ConnectionConsensusState 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 + description: >- + A URL/resource name that uniquely identifies the type of + the serialized + + protocol buffer message. This string must contain at + least + + one "/" character. The last segment of the URL's path + must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in + a canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary + all types that they + + expect it to use in the context of Any. However, for + URLs which use the + + scheme `http`, `https`, or no scheme, one can optionally + set up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based + on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in + the official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) + might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values + in the form + + of utility functions or additional generated methods of the + Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := anypb.New(foo) + if err != nil { + ... + } + ... + foo := &pb.Foo{} + if err := any.UnmarshalTo(foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by + default use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the + last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield + type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with + an + + additional field `@type` which contains the type URL. + Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } - If the embedded message type is well-known and has a custom - JSON + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } - representation, that representation will be embedded adding a - field + If the embedded message type is well-known and has a custom + JSON - `value` which holds the custom JSON in addition to the `@type` + representation, that representation will be embedded adding + a field - field. Example (for message [google.protobuf.Duration][]): + `value` which holds the custom JSON in addition to the + `@type` - { - "@type": "type.googleapis.com/google.protobuf.Duration", - "value": "1.212s" - } - title: consensus state associated with the channel - client_id: - type: string - title: client ID associated with the consensus state - proof: - type: string - format: byte - title: merkle proof of existence - proof_height: - title: height at which the proof was retrieved + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + parameters: + - name: connection_id + description: connection identifier + in: path + required: true + type: string + - name: revision_number + in: path + required: true + type: string + format: uint64 + - name: revision_height + in: path + required: true + type: string + format: uint64 + tags: + - Query + /ibc/core/connection/v1/params: + get: + summary: ConnectionParams queries all parameters of the ibc connection submodule. + operationId: ConnectionParams + responses: + '200': + description: A successful response. + schema: + type: object + properties: + params: + description: params defines the parameters of the module. type: object properties: - revision_number: - type: string - format: uint64 - title: the revision that the client is currently on - revision_height: + max_expected_time_per_block: type: string format: uint64 - title: the height within the given revision - description: >- - Normally the RevisionHeight is incremented at each height - while keeping - - RevisionNumber the same. However some consensus algorithms may - choose to - - reset the height in certain conditions e.g. hard forks, - state-machine - - breaking changes In these cases, the RevisionNumber is - incremented so that + description: >- + maximum expected time per block (in nanoseconds), used to + enforce block delay. This parameter should reflect the - height continues to be monitonically increasing even as the - RevisionHeight + largest amount of time that the chain might reasonably + take to produce the next block under normal operating - gets reset - title: |- - QueryConnectionConsensusStateResponse is the response type for the - Query/ConnectionConsensusState RPC method + conditions. A safe choice is 3-5x the expected time per + block. + description: >- + QueryConnectionParamsResponse is the response type for the + Query/ConnectionParams RPC method. default: - description: An unexpected error response + description: An unexpected error response. schema: type: object properties: @@ -8310,10 +8763,13 @@ paths: Example 4: Pack and unpack a message in Go foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) + any, err := anypb.New(foo) + if err != nil { + ... + } ... foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { + if err := any.UnmarshalTo(foo); err != nil { ... } @@ -8372,22 +8828,6 @@ paths: "@type": "type.googleapis.com/google.protobuf.Duration", "value": "1.212s" } - parameters: - - name: connection_id - description: connection identifier - in: path - required: true - type: string - - name: revision_number - in: path - required: true - type: string - format: uint64 - - name: revision_height - in: path - required: true - type: string - format: uint64 tags: - Query /ibc/core/channel/v1/channels: @@ -8487,9 +8927,10 @@ paths: next_key: type: string format: byte - title: |- + description: |- next_key is the key to be passed to PageRequest.key to - query the next page most efficiently + query the next page most efficiently. It will be empty if + there are no more results. total: type: string format: uint64 @@ -8541,7 +8982,7 @@ paths: QueryChannelsResponse is the response type for the Query/Channels RPC method. default: - description: An unexpected error response + description: An unexpected error response. schema: type: object properties: @@ -8667,10 +9108,13 @@ paths: Example 4: Pack and unpack a message in Go foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) + any, err := anypb.New(foo) + if err != nil { + ... + } ... foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { + if err := any.UnmarshalTo(foo); err != nil { ... } @@ -8776,10 +9220,19 @@ paths: in: query required: false type: boolean - format: boolean + - name: pagination.reverse + description: >- + reverse is set to true if results are to be returned in the + descending order. + + + Since: cosmos-sdk 0.43 + in: query + required: false + type: boolean tags: - Query - '/ibc/core/channel/v1/channels/{channel_id}/ports/{port_id}': + /ibc/core/channel/v1/channels/{channel_id}/ports/{port_id}: get: summary: Channel queries an IBC Channel. operationId: Channel @@ -8905,7 +9358,7 @@ paths: proof was retrieved. default: - description: An unexpected error response + description: An unexpected error response. schema: type: object properties: @@ -9031,10 +9484,13 @@ paths: Example 4: Pack and unpack a message in Go foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) + any, err := anypb.New(foo) + if err != nil { + ... + } ... foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { + if err := any.UnmarshalTo(foo); err != nil { ... } @@ -9106,7 +9562,7 @@ paths: type: string tags: - Query - '/ibc/core/channel/v1/channels/{channel_id}/ports/{port_id}/client_state': + /ibc/core/channel/v1/channels/{channel_id}/ports/{port_id}/client_state: get: summary: >- ChannelClientState queries for the client state for the channel @@ -9240,10 +9696,13 @@ paths: Example 4: Pack and unpack a message in Go foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) + any, err := anypb.New(foo) + if err != nil { + ... + } ... foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { + if err := any.UnmarshalTo(foo); err != nil { ... } @@ -9345,7 +9804,7 @@ paths: QueryChannelClientStateResponse is the Response type for the Query/QueryChannelClientState RPC method default: - description: An unexpected error response + description: An unexpected error response. schema: type: object properties: @@ -9471,10 +9930,13 @@ paths: Example 4: Pack and unpack a message in Go foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) + any, err := anypb.New(foo) + if err != nil { + ... + } ... foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { + if err := any.UnmarshalTo(foo); err != nil { ... } @@ -9546,7 +10008,7 @@ paths: type: string tags: - Query - '/ibc/core/channel/v1/channels/{channel_id}/ports/{port_id}/consensus_state/revision/{revision_number}/height/{revision_height}': + /ibc/core/channel/v1/channels/{channel_id}/ports/{port_id}/consensus_state/revision/{revision_number}/height/{revision_height}: get: summary: |- ChannelConsensusState queries for the consensus state for the channel @@ -9670,10 +10132,13 @@ paths: Example 4: Pack and unpack a message in Go foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) + any, err := anypb.New(foo) + if err != nil { + ... + } ... foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { + if err := any.UnmarshalTo(foo); err != nil { ... } @@ -9770,7 +10235,7 @@ paths: QueryChannelClientStateResponse is the Response type for the Query/QueryChannelClientState RPC method default: - description: An unexpected error response + description: An unexpected error response. schema: type: object properties: @@ -9896,10 +10361,13 @@ paths: Example 4: Pack and unpack a message in Go foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) + any, err := anypb.New(foo) + if err != nil { + ... + } ... foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { + if err := any.UnmarshalTo(foo); err != nil { ... } @@ -9983,7 +10451,7 @@ paths: format: uint64 tags: - Query - '/ibc/core/channel/v1/channels/{channel_id}/ports/{port_id}/next_sequence': + /ibc/core/channel/v1/channels/{channel_id}/ports/{port_id}/next_sequence: get: summary: >- NextSequenceReceive returns the next receive sequence for a given @@ -10036,7 +10504,7 @@ paths: QuerySequenceResponse is the request type for the Query/QueryNextSequenceReceiveResponse RPC method default: - description: An unexpected error response + description: An unexpected error response. schema: type: object properties: @@ -10162,10 +10630,13 @@ paths: Example 4: Pack and unpack a message in Go foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) + any, err := anypb.New(foo) + if err != nil { + ... + } ... foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { + if err := any.UnmarshalTo(foo); err != nil { ... } @@ -10237,7 +10708,7 @@ paths: type: string tags: - Query - '/ibc/core/channel/v1/channels/{channel_id}/ports/{port_id}/packet_acknowledgements': + /ibc/core/channel/v1/channels/{channel_id}/ports/{port_id}/packet_acknowledgements: get: summary: >- PacketAcknowledgements returns all the packet acknowledgements @@ -10287,9 +10758,10 @@ paths: next_key: type: string format: byte - title: |- + description: |- next_key is the key to be passed to PageRequest.key to - query the next page most efficiently + query the next page most efficiently. It will be empty if + there are no more results. total: type: string format: uint64 @@ -10341,7 +10813,7 @@ paths: QueryPacketAcknowledgemetsResponse is the request type for the Query/QueryPacketAcknowledgements RPC method default: - description: An unexpected error response + description: An unexpected error response. schema: type: object properties: @@ -10467,10 +10939,13 @@ paths: Example 4: Pack and unpack a message in Go foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) + any, err := anypb.New(foo) + if err != nil { + ... + } ... foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { + if err := any.UnmarshalTo(foo); err != nil { ... } @@ -10586,7 +11061,16 @@ paths: in: query required: false type: boolean - format: boolean + - name: pagination.reverse + description: >- + reverse is set to true if results are to be returned in the + descending order. + + + Since: cosmos-sdk 0.43 + in: query + required: false + type: boolean - name: packet_commitment_sequences description: list of packet sequences. in: query @@ -10598,7 +11082,7 @@ paths: collectionFormat: multi tags: - Query - '/ibc/core/channel/v1/channels/{channel_id}/ports/{port_id}/packet_acks/{sequence}': + /ibc/core/channel/v1/channels/{channel_id}/ports/{port_id}/packet_acks/{sequence}: get: summary: PacketAcknowledgement queries a stored packet acknowledgement hash. operationId: PacketAcknowledgement @@ -10653,7 +11137,7 @@ paths: proof was retrieved default: - description: An unexpected error response + description: An unexpected error response. schema: type: object properties: @@ -10779,10 +11263,13 @@ paths: Example 4: Pack and unpack a message in Go foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) + any, err := anypb.New(foo) + if err != nil { + ... + } ... foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { + if err := any.UnmarshalTo(foo); err != nil { ... } @@ -10860,7 +11347,7 @@ paths: format: uint64 tags: - Query - '/ibc/core/channel/v1/channels/{channel_id}/ports/{port_id}/packet_commitments': + /ibc/core/channel/v1/channels/{channel_id}/ports/{port_id}/packet_commitments: get: summary: |- PacketCommitments returns all the packet commitments hashes associated @@ -10908,9 +11395,10 @@ paths: next_key: type: string format: byte - title: |- + description: |- next_key is the key to be passed to PageRequest.key to - query the next page most efficiently + query the next page most efficiently. It will be empty if + there are no more results. total: type: string format: uint64 @@ -10962,7 +11450,7 @@ paths: QueryPacketCommitmentsResponse is the request type for the Query/QueryPacketCommitments RPC method default: - description: An unexpected error response + description: An unexpected error response. schema: type: object properties: @@ -11088,10 +11576,13 @@ paths: Example 4: Pack and unpack a message in Go foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) + any, err := anypb.New(foo) + if err != nil { + ... + } ... foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { + if err := any.UnmarshalTo(foo); err != nil { ... } @@ -11207,10 +11698,19 @@ paths: in: query required: false type: boolean - format: boolean + - name: pagination.reverse + description: >- + reverse is set to true if results are to be returned in the + descending order. + + + Since: cosmos-sdk 0.43 + in: query + required: false + type: boolean tags: - Query - '/ibc/core/channel/v1/channels/{channel_id}/ports/{port_id}/packet_commitments/{packet_ack_sequences}/unreceived_acks': + /ibc/core/channel/v1/channels/{channel_id}/ports/{port_id}/packet_commitments/{packet_ack_sequences}/unreceived_acks: get: summary: >- UnreceivedAcks returns all the unreceived IBC acknowledgements @@ -11263,7 +11763,7 @@ paths: QueryUnreceivedAcksResponse is the response type for the Query/UnreceivedAcks RPC method default: - description: An unexpected error response + description: An unexpected error response. schema: type: object properties: @@ -11389,10 +11889,13 @@ paths: Example 4: Pack and unpack a message in Go foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) + any, err := anypb.New(foo) + if err != nil { + ... + } ... foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { + if err := any.UnmarshalTo(foo); err != nil { ... } @@ -11474,7 +11977,7 @@ paths: minItems: 1 tags: - Query - '/ibc/core/channel/v1/channels/{channel_id}/ports/{port_id}/packet_commitments/{packet_commitment_sequences}/unreceived_packets': + /ibc/core/channel/v1/channels/{channel_id}/ports/{port_id}/packet_commitments/{packet_commitment_sequences}/unreceived_packets: get: summary: >- UnreceivedPackets returns all the unreceived IBC packets associated with @@ -11527,7 +12030,7 @@ paths: QueryUnreceivedPacketsResponse is the response type for the Query/UnreceivedPacketCommitments RPC method default: - description: An unexpected error response + description: An unexpected error response. schema: type: object properties: @@ -11653,10 +12156,13 @@ paths: Example 4: Pack and unpack a message in Go foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) + any, err := anypb.New(foo) + if err != nil { + ... + } ... foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { + if err := any.UnmarshalTo(foo); err != nil { ... } @@ -11738,7 +12244,7 @@ paths: minItems: 1 tags: - Query - '/ibc/core/channel/v1/channels/{channel_id}/ports/{port_id}/packet_commitments/{sequence}': + /ibc/core/channel/v1/channels/{channel_id}/ports/{port_id}/packet_commitments/{sequence}: get: summary: PacketCommitment queries a stored packet commitment hash. operationId: PacketCommitment @@ -11794,7 +12300,7 @@ paths: retrieved default: - description: An unexpected error response + description: An unexpected error response. schema: type: object properties: @@ -11920,10 +12426,13 @@ paths: Example 4: Pack and unpack a message in Go foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) + any, err := anypb.New(foo) + if err != nil { + ... + } ... foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { + if err := any.UnmarshalTo(foo); err != nil { ... } @@ -12001,7 +12510,7 @@ paths: format: uint64 tags: - Query - '/ibc/core/channel/v1/channels/{channel_id}/ports/{port_id}/packet_receipts/{sequence}': + /ibc/core/channel/v1/channels/{channel_id}/ports/{port_id}/packet_receipts/{sequence}: get: summary: >- PacketReceipt queries if a given packet sequence has been received on @@ -12017,7 +12526,6 @@ paths: properties: received: type: boolean - format: boolean title: success flag for if receipt exists proof: type: string @@ -12061,7 +12569,7 @@ paths: retrieved default: - description: An unexpected error response + description: An unexpected error response. schema: type: object properties: @@ -12187,10 +12695,13 @@ paths: Example 4: Pack and unpack a message in Go foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) + any, err := anypb.New(foo) + if err != nil { + ... + } ... foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { + if err := any.UnmarshalTo(foo); err != nil { ... } @@ -12268,7 +12779,7 @@ paths: format: uint64 tags: - Query - '/ibc/core/channel/v1/connections/{connection}/channels': + /ibc/core/channel/v1/connections/{connection}/channels: get: summary: |- ConnectionChannels queries all the channels associated with a connection @@ -12367,9 +12878,10 @@ paths: next_key: type: string format: byte - title: |- + description: |- next_key is the key to be passed to PageRequest.key to - query the next page most efficiently + query the next page most efficiently. It will be empty if + there are no more results. total: type: string format: uint64 @@ -12421,7 +12933,7 @@ paths: QueryConnectionChannelsResponse is the Response type for the Query/QueryConnectionChannels RPC method default: - description: An unexpected error response + description: An unexpected error response. schema: type: object properties: @@ -12547,10 +13059,13 @@ paths: Example 4: Pack and unpack a message in Go foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) + any, err := anypb.New(foo) + if err != nil { + ... + } ... foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { + if err := any.UnmarshalTo(foo); err != nil { ... } @@ -12661,7 +13176,16 @@ paths: in: query required: false type: boolean - format: boolean + - name: pagination.reverse + description: >- + reverse is set to true if results are to be returned in the + descending order. + + + Since: cosmos-sdk 0.43 + in: query + required: false + type: boolean tags: - Query definitions: @@ -12692,7 +13216,6 @@ definitions: If left empty it will default to a value to be set by each app. count_total: type: boolean - format: boolean description: >- count_total is set to true to indicate that the result set should include @@ -12703,6 +13226,14 @@ definitions: key is set. + reverse: + type: boolean + description: >- + reverse is set to true if results are to be returned in the descending + order. + + + Since: cosmos-sdk 0.43 description: |- message SomeRequest { Foo some_parameter = 1; @@ -12717,9 +13248,10 @@ definitions: next_key: type: string format: byte - title: |- + description: |- next_key is the key to be passed to PageRequest.key to - query the next page most efficiently + query the next page most efficiently. It will be empty if + there are no more results. total: type: string format: uint64 @@ -12836,10 +13368,13 @@ definitions: Example 4: Pack and unpack a message in Go foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) + any, err := anypb.New(foo) + if err != nil { + ... + } ... foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { + if err := any.UnmarshalTo(foo); err != nil { ... } @@ -13010,10 +13545,13 @@ definitions: Example 4: Pack and unpack a message in Go foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) + any, err := anypb.New(foo) + if err != nil { + ... + } ... foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { + if err := any.UnmarshalTo(foo); err != nil { ... } @@ -13086,7 +13624,6 @@ definitions: properties: send_enabled: type: boolean - format: boolean description: >- send_enabled enables or disables all cross-chain token transfers from this @@ -13094,7 +13631,6 @@ definitions: chain. receive_enabled: type: boolean - format: boolean description: >- receive_enabled enables or disables all cross-chain token transfers to this @@ -13173,9 +13709,10 @@ definitions: next_key: type: string format: byte - title: |- + description: |- next_key is the key to be passed to PageRequest.key to - query the next page most efficiently + query the next page most efficiently. It will be empty if + there are no more results. total: type: string format: uint64 @@ -13207,7 +13744,6 @@ definitions: properties: send_enabled: type: boolean - format: boolean description: >- send_enabled enables or disables all cross-chain token transfers from this @@ -13215,7 +13751,6 @@ definitions: chain. receive_enabled: type: boolean - format: boolean description: >- receive_enabled enables or disables all cross-chain token transfers to this @@ -13227,11 +13762,18 @@ definitions: properties: controller_enabled: type: boolean - format: boolean description: controller_enabled enables or disables the controller submodule. 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: @@ -13241,7 +13783,6 @@ definitions: properties: controller_enabled: type: boolean - format: boolean description: controller_enabled enables or disables the controller submodule. description: QueryParamsResponse is the response type for the Query/Params RPC method. ibc.applications.interchain_accounts.host.v1.Params: @@ -13249,7 +13790,6 @@ definitions: properties: host_enabled: type: boolean - format: boolean description: host_enabled enables or disables the host submodule. allow_messages: type: array @@ -13270,7 +13810,6 @@ definitions: properties: host_enabled: type: boolean - format: boolean description: host_enabled enables or disables the host submodule. allow_messages: type: array @@ -13340,7 +13879,7 @@ definitions: NOTE: The amount field is an Int which implements the custom method signatures required by gogoproto. title: the packet timeout fee - title: 'Fee defines the ICS29 receive, acknowledgement and timeout fees' + title: Fee defines the ICS29 receive, acknowledgement and timeout fees ibc.applications.fee.v1.FeeEnabledChannel: type: object properties: @@ -13540,7 +14079,6 @@ definitions: properties: fee_enabled: type: boolean - format: boolean title: boolean flag representing the fee enabled channel status title: >- QueryFeeEnabledChannelResponse defines the response type for the @@ -14120,10 +14658,13 @@ definitions: Example 4: Pack and unpack a message in Go foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) + any, err := anypb.New(foo) + if err != nil { + ... + } ... foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { + if err := any.UnmarshalTo(foo); err != nil { ... } @@ -14211,6 +14752,7 @@ definitions: type: string title: client identifier client_state: + title: client state type: object properties: type_url: @@ -14316,10 +14858,13 @@ definitions: Example 4: Pack and unpack a message in Go foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) + any, err := anypb.New(foo) + if err != nil { + ... + } ... foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { + if err := any.UnmarshalTo(foo); err != nil { ... } @@ -14369,7 +14914,6 @@ definitions: "@type": "type.googleapis.com/google.protobuf.Duration", "value": "1.212s" } - title: client state description: |- IdentifiedClientState defines a client state with an additional client identifier field. @@ -14508,10 +15052,13 @@ definitions: Example 4: Pack and unpack a message in Go foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) + any, err := anypb.New(foo) + if err != nil { + ... + } ... foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { + if err := any.UnmarshalTo(foo); err != nil { ... } @@ -14613,6 +15160,7 @@ definitions: type: string title: client identifier client_state: + title: client state type: object properties: type_url: @@ -14724,10 +15272,13 @@ definitions: Example 4: Pack and unpack a message in Go foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) + any, err := anypb.New(foo) + if err != nil { + ... + } ... foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { + if err := any.UnmarshalTo(foo); err != nil { ... } @@ -14781,7 +15332,6 @@ definitions: "@type": "type.googleapis.com/google.protobuf.Duration", "value": "1.212s" } - title: client state description: >- IdentifiedClientState defines a client state with an additional client @@ -14795,9 +15345,10 @@ definitions: next_key: type: string format: byte - title: |- + description: |- next_key is the key to be passed to PageRequest.key to - query the next page most efficiently + query the next page most efficiently. It will be empty if + there are no more results. total: type: string format: uint64 @@ -14877,9 +15428,10 @@ definitions: next_key: type: string format: byte - title: |- + description: |- next_key is the key to be passed to PageRequest.key to - query the next page most efficiently + query the next page most efficiently. It will be empty if + there are no more results. total: type: string format: uint64 @@ -15008,10 +15560,13 @@ definitions: Example 4: Pack and unpack a message in Go foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) + any, err := anypb.New(foo) + if err != nil { + ... + } ... foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { + if err := any.UnmarshalTo(foo); err != nil { ... } @@ -15256,10 +15811,13 @@ definitions: Example 4: Pack and unpack a message in Go foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) + any, err := anypb.New(foo) + if err != nil { + ... + } ... foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { + if err := any.UnmarshalTo(foo); err != nil { ... } @@ -15327,9 +15885,10 @@ definitions: next_key: type: string format: byte - title: |- + description: |- next_key is the key to be passed to PageRequest.key to - query the next page most efficiently + query the next page most efficiently. It will be empty if + there are no more results. total: type: string format: uint64 @@ -15458,10 +16017,13 @@ definitions: Example 4: Pack and unpack a message in Go foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) + any, err := anypb.New(foo) + if err != nil { + ... + } ... foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { + if err := any.UnmarshalTo(foo); err != nil { ... } @@ -15624,10 +16186,13 @@ definitions: Example 4: Pack and unpack a message in Go foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) + any, err := anypb.New(foo) + if err != nil { + ... + } ... foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { + if err := any.UnmarshalTo(foo); err != nil { ... } @@ -15890,6 +16455,21 @@ definitions: description: |- IdentifiedConnection defines a connection with additional connection identifier field. + ibc.core.connection.v1.Params: + type: object + properties: + max_expected_time_per_block: + type: string + format: uint64 + description: >- + maximum expected time per block (in nanoseconds), used to enforce + block delay. This parameter should reflect the + + largest amount of time that the chain might reasonably take to produce + the next block under normal operating + + conditions. A safe choice is 3-5x the expected time per block. + description: Params defines the set of Connection parameters. ibc.core.connection.v1.QueryClientConnectionsResponse: type: object properties: @@ -16053,10 +16633,13 @@ definitions: Example 4: Pack and unpack a message in Go foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) + any, err := anypb.New(foo) + if err != nil { + ... + } ... foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { + if err := any.UnmarshalTo(foo); err != nil { ... } @@ -16256,10 +16839,13 @@ definitions: Example 4: Pack and unpack a message in Go foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) + any, err := anypb.New(foo) + if err != nil { + ... + } ... foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { + if err := any.UnmarshalTo(foo); err != nil { ... } @@ -16348,6 +16934,27 @@ definitions: title: |- QueryConnectionConsensusStateResponse is the response type for the Query/ConnectionConsensusState RPC method + ibc.core.connection.v1.QueryConnectionParamsResponse: + type: object + properties: + params: + description: params defines the parameters of the module. + type: object + properties: + max_expected_time_per_block: + type: string + format: uint64 + description: >- + maximum expected time per block (in nanoseconds), used to enforce + block delay. This parameter should reflect the + + largest amount of time that the chain might reasonably take to + produce the next block under normal operating + + conditions. A safe choice is 3-5x the expected time per block. + description: >- + QueryConnectionParamsResponse is the response type for the + Query/ConnectionParams RPC method. ibc.core.connection.v1.QueryConnectionResponse: type: object properties: @@ -16574,9 +17181,10 @@ definitions: next_key: type: string format: byte - title: |- + description: |- next_key is the key to be passed to PageRequest.key to - query the next page most efficiently + query the next page most efficiently. It will be empty if + there are no more results. total: type: string format: uint64 @@ -16715,7 +17323,7 @@ definitions: this channel will travel version: type: string - title: 'opaque channel version, which is agreed upon during the handshake' + title: opaque channel version, which is agreed upon during the handshake description: |- Channel defines pipeline for exactly-once packet delivery between specific modules on separate blockchains, which has at least one end capable of @@ -16790,7 +17398,7 @@ definitions: this channel will travel version: type: string - title: 'opaque channel version, which is agreed upon during the handshake' + title: opaque channel version, which is agreed upon during the handshake port_id: type: string title: port identifier @@ -16955,10 +17563,13 @@ definitions: Example 4: Pack and unpack a message in Go foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) + any, err := anypb.New(foo) + if err != nil { + ... + } ... foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { + if err := any.UnmarshalTo(foo); err != nil { ... } @@ -17158,10 +17769,13 @@ definitions: Example 4: Pack and unpack a message in Go foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) + any, err := anypb.New(foo) + if err != nil { + ... + } ... foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { + if err := any.UnmarshalTo(foo); err != nil { ... } @@ -17314,7 +17928,7 @@ definitions: this channel will travel version: type: string - title: 'opaque channel version, which is agreed upon during the handshake' + title: opaque channel version, which is agreed upon during the handshake description: >- Channel defines pipeline for exactly-once packet delivery between specific @@ -17446,9 +18060,10 @@ definitions: next_key: type: string format: byte - title: |- + description: |- next_key is the key to be passed to PageRequest.key to - query the next page most efficiently + query the next page most efficiently. It will be empty if + there are no more results. total: type: string format: uint64 @@ -17581,9 +18196,10 @@ definitions: next_key: type: string format: byte - title: |- + description: |- next_key is the key to be passed to PageRequest.key to - query the next page most efficiently + query the next page most efficiently. It will be empty if + there are no more results. total: type: string format: uint64 @@ -17754,9 +18370,10 @@ definitions: next_key: type: string format: byte - title: |- + description: |- next_key is the key to be passed to PageRequest.key to - query the next page most efficiently + query the next page most efficiently. It will be empty if + there are no more results. total: type: string format: uint64 @@ -17888,9 +18505,10 @@ definitions: next_key: type: string format: byte - title: |- + description: |- next_key is the key to be passed to PageRequest.key to - query the next page most efficiently + query the next page most efficiently. It will be empty if + there are no more results. total: type: string format: uint64 @@ -17943,7 +18561,6 @@ definitions: properties: received: type: boolean - format: boolean title: success flag for if receipt exists proof: type: string diff --git a/docs/dev/development-setup.md b/docs/dev/development-setup.md new file mode 100644 index 00000000000..61e90590806 --- /dev/null +++ b/docs/dev/development-setup.md @@ -0,0 +1,61 @@ +# Development setup + +## Dependencies + +We use [Go 1.14 Modules](https://github.com/golang/go/wiki/Modules) to manage dependency versions. + +The main branch of every Cosmos repository should just build with `go get`, which means they should be kept up-to-date with their dependencies, so we can get away with telling people they can just `go get` our software. + +Since some dependencies are not under our control, a third party may break our build, in which case we can fall back on `go mod tidy -v`. + +Other helpful commands: + +- `go get` to add a new go module (including if the existing go module is being semantic version bumped, i.e. my/module/v1 -> my/module/v2). +- `go get -u` to update an existing dependency. +- `go mod tidy` to update dependencies in `go.sum`. + +## Protobuf + +We use [Protocol Buffers](https://developers.google.com/protocol-buffers) along with [buf](https://docs.buf.build/introduction) and [gogoproto](https://github.com/gogo/protobuf) to generate code for use in ibc-go. + +For determinstic behavior around protobuf tooling, everything is containerized using Docker. Make sure to have Docker installed on your machine, or head to [Docker's website](https://docs.docker.com/get-docker/) to install it. + +For formatting code in `.proto` files, you can run the `make proto-format` command. + +For linting and checking breaking changes, we also use [buf](https://buf.build/). You can use the commands `make proto-lint` and `make proto-check-breaking` to respectively lint your proto files and check for breaking changes. + +To generate the protobuf stubs, you can run `make proto-gen`. + +We also added the `make proto-all` command to run the above commands (`proto-format`, `proto-lint` and `proto-gen`) sequentially. + +To update third-party protobuf dependencies, you can run `make proto-update-deps`. This requires `buf` to be installed in the local development environment (see [`buf`s installation documentation](https://docs.buf.build/installation) for more details). + +For generating or updating the swagger file that documents the URLs of the RESTful API that exposes the gRPC endpoints over HTTP, you can run the `proto-swagger-gen` command. + +It reads protobuf service definitions and generates a reverse-proxy server which translates a RESTful HTTP API into gRPC. + +## Developing and testing + +- The latest state of development is on `main`. +- Build the `simd` test chain binary with `make build`. +- `main` must never fail `make test`. +- No `--force` onto `main` (except when reverting a broken commit, which should seldom happen). +- Create a development branch either on `github.com/cosmos/ibc-go`, or your fork (using `git remote add fork`). +- Before submitting a pull request, begin `git rebase` on top of `main`. + +All Go tests in ibc-go can be ran by running `make test`. + +Please make sure to run `make format` before every commit - the easiest way to do this is have your editor run it for you upon saving a file. Additionally please ensure that your code is lint compliant by running `make lint-fix` (requires `golangci-lint`). + +When testing a function under a variety of different inputs, we prefer to use [table driven tests](https://github.com/golang/go/wiki/TableDrivenTests). + +All unit tests should use the testing package. Please see the testing package [README](../../testing/README.md) for more information. + +Test coverage is continuously deployed at . PRs that improve test coverage are welcome, but in general the test coverage should be used as a guidance for finding API use cases that are not covered by tests. We don't recommend adding tests that only improve coverage but not actually test a meaning use case. + +## Documentation + +- If you open a PR on ibc-go, it is mandatory to update the relevant documentation in `/docs`. +- We lint the markdown files for documentation with [markdownlint-cli](https://github.com/igorshubovych/markdownlint-cli). Please run `make docs-lint` before pushing changes in the markdown files (you will need to have `markdownlint-cli` installed, so please follow the [installation instructions](https://github.com/igorshubovych/markdownlint-cli#installation)). +- Generate the folder `docs/.vuepress/dist` with all the static files for the documentation site with `make build-docs`. +- Run the documentation site locally with `make view-docs`. diff --git a/docs/dev/go-style-guide.md b/docs/dev/go-style-guide.md new file mode 100644 index 00000000000..790c5fae3fb --- /dev/null +++ b/docs/dev/go-style-guide.md @@ -0,0 +1,115 @@ + +# Go style guide + +In order to keep our code looking good with lots of programmers working on it, it helps to have a "style guide", so all the code generally looks quite similar. This doesn't mean there is only one "right way" to write code, or even that this standard is better than your style. But if we agree to a number of stylistic practices, it makes it much easier to read and modify new code. Please feel free to make suggestions if there's something you would like to add or modify. + +We expect all contributors to be familiar with [Effective Go](https://golang.org/doc/effective_go.html) (and it's recommended reading for all Go programmers anyways). Additionally, we generally agree with the suggestions in [Uber's style guide](https://github.com/uber-go/guide/blob/master/style.md) and use that as a starting point. + +## Code Structure + +Perhaps more key for code readability than good commenting is having the right structure. As a rule of thumb, try to write in a logical order of importance, taking a little time to think how to order and divide the code such that someone could scroll down and understand the functionality of it just as well as you do. A loose example of such order would be: + +- Constants, global and package-level variables. +- Main struct definition. +- Options (only if they are seen as critical to the struct else they should be placed in another file). +- Initialization/start and stop of the service functions. +- Public functions (in order of most important). +- Private/helper functions. +- Auxiliary structs and function (can also be above private functions or in a separate file). + +## General + +- Use `gofumpt` to format all code upon saving it (or run `make format`). +- Think about documentation, and try to leave godoc comments, when it will help new developers. +- Every package should have a high level doc.go file to describe the purpose of that package, its main functions, and any other relevant information. +- Applications (e.g. clis/servers) should panic on unexpected unrecoverable errors and print a stack trace. + +## Comments + +- Use a space after the comment deliminter (ex. `// your comment`). +- Many comments are not sentences. These should begin with a lower case letter and end without a period. +- Conversely, sentences in comments should be sentenced-cased and end with a period. +- Comments should explain _why_ something is being done rather than _what_ the code is doing. For example: + + The comments in + +```go +// assign a variable foo +f := foo +// assign f to b +b := f +``` + + have little value, but the following is more useful: + +```go +f := foo +// we copy the variable f because we want to preserve the state at time of initialization +b := f +``` + +## Linting + +- Run `make lint-fix` to fix any linting errors. + +## Various + +- Functions that return functions should have the suffix `Fn`. +- Names should not [stutter](https://blog.golang.org/package-names). For example, a struct generally shouldn’t have a field named after itself; e.g., this shouldn't occur: + +```go +type middleware struct { + middleware Middleware +} +``` + +- Acronyms are all capitalized, like "RPC", "gRPC", "API". "MyID", rather than "MyId". +- Whenever it is safe to use Go's built-in `error` instantiation functions (as opposed to Cosmos SDK's error instantiation functions), prefer `errors.New()` instead of `fmt.Errorf()` unless you're actually using the format feature with arguments. + +## Importing libraries + +- Use [goimports](https://godoc.org/golang.org/x/tools/cmd/goimports). +- Separate imports into blocks: one for the standard lib, one for external libs and one for application libs. For example: + +```go +import ( + // standard library imports + "fmt" + "testing" + + // external library imports + "github.com/stretchr/testify/require" + abci "github.com/tendermint/tendermint/abci/types" + + // ibc-go library imports + "github.com/cosmos/ibc-go/modules/core/23-commitment/types" +) +``` + +## Dependencies + +- Dependencies should be pinned by a release tag, or specific commit, to avoid breaking `go get` when external dependencies are updated. +- Refer to the [contributing](./development-setup.md#dependencies) document for more details. + +## Testing + +- Make use of table driven testing where possible and not-cumbersome. Read [this blog post](https://dave.cheney.net/2013/06/09/writing-table-driven-tests-in-go) for more information. See the [tests](https://github.com/cosmos/ibc-go/blob/f24f41ea8a61fe87f6becab94e84de08c8aa9381/modules/apps/transfer/keeper/msg_server_test.go#L11) for [`Transfer`](https://github.com/cosmos/ibc-go/blob/f24f41ea8a61fe87f6becab94e84de08c8aa9381/modules/apps/transfer/keeper/msg_server.go#L15) for an example. +- Make use of Testify [assert](https://godoc.org/github.com/stretchr/testify/assert) and [require](https://godoc.org/github.com/stretchr/testify/require). +- When using mocks, it is recommended to use Testify [mock](https://pkg.go.dev/github.com/stretchr/testify/mock) along with [Mockery](https://github.com/vektra/mockery) for autogeneration. + +## Errors + +- Ensure that errors are concise, clear and traceable. +- Depending on the context, use either `cosmossdk.io/errors` or `stdlib` error packages. +- For wrapping errors, use `fmt.Errorf()` with `%w`. +- Panic is appropriate when an internal invariant of a system is broken, while all other cases (in particular, incorrect or invalid usage) should return errors. +- Error messages should be formatted as following: + +```go +sdkerrors.Wrapf( + , + "expected %s, got %s", + , + +) +``` diff --git a/docs/dev/project-structure.md b/docs/dev/project-structure.md new file mode 100644 index 00000000000..01fcf6b04da --- /dev/null +++ b/docs/dev/project-structure.md @@ -0,0 +1,46 @@ +# Project structure + +If you're not familiar with the overall module structure from the SDK modules, please check this [document](https://github.com/cosmos/cosmos-sdk/blob/main/docs/docs/building-modules/10-structure.md) as prerequisite reading. + +Every Interchain Standard (ICS) has been developed in its own package. The development team separated the IBC TAO (Transport, Authentication, Ordering) ICS specifications from the IBC application level specification. The following sections describe the architecture of the most relevant directories that comprise this repository. + +## `modules` + +This folder contains implementations for the IBC TAO (`core`), IBC applications (`apps`) and light clients (`light-clients`). + +### `core` + +- `02-client`: This package is an implementation for Cosmos SDK-based chains of [ICS 02](https://github.com/cosmos/ibc/tree/main/spec/core/ics-002-client-semantics). This implementation defines the types and methods needed to operate light clients tracking other chain's consensus state. +- `03-connection`: This package is an implementation for Cosmos SDK-based chains of [ICS 03](https://github.com/cosmos/ibc/tree/main/spec/core/ics-003-connection-semantics). This implementation defines the types and methods necessary to perform connection handshake between two chains. +- `04-channel`: This package is an implementation for Cosmos SDK-based chains of [ICS 04](https://github.com/cosmos/ibc/tree/main/spec/core/ics-004-channel-and-packet-semantics). This implementation defines the types and methods necessary to perform channel handshake between two chains and ensure correct packet sending flow. +- `05-port`: This package is an implementation for Cosmos SDK-based chains of [ICS 05](https://github.com/cosmos/ibc/tree/main/spec/core/ics-005-port-allocation). This implements the port allocation system by which modules can bind to uniquely named ports. +- `23-commitment`: This package is an implementation for Cosmos SDK-based chains of [ICS 23](https://github.com/cosmos/ibc/tree/main/spec/core/ics-023-vector-commitments). This implementation defines the functions required to prove inclusion or non-inclusion of particular values at particular paths in state. +- `24-host`: This package is an implementation for Cosmos SDK-based chains of [ICS 24](https://github.com/cosmos/ibc/tree/main/spec/core/ics-024-host-requirements). + +### `apps` + +- `transfer`: This is the Cosmos SDK implementation of the [ICS 20](https://github.com/cosmos/ibc/tree/main/spec/app/ics-020-fungible-token-transfer) protocol, which enables cross-chain fungible token transfers. For more information, read the [module's docs](../apps/transfer/overview.md) +- `27-interchain-accounts`: This is the Cosmos SDK implementation of the [ICS 27](https://github.com/cosmos/ibc/tree/main/spec/app/ics-027-interchain-accounts) protocol, which enables cross-chain account management built upon IBC. For more information, read the [module's documentation](../apps/interchain-accounts/overview.md). +- `29-fee`: This is the Cosmos SDK implementation of the [ICS 29](https://github.com/cosmos/ibc/tree/main/spec/app/ics-029-fee-payment) middleware, which handles packet incentivisation and fee distribution on top of any ICS application protocol, enabling fee payment to relayer operators. For more information, read the [module's documentation](../middleware/ics29-fee/overview.md). + +### `light-clients` + +- `06-solomachine`: This package implement the types for the Solo Machine light client specified in [ICS 06](https://github.com/cosmos/ibc/tree/main/spec/client/ics-006-solo-machine-client). +- `07-tendermint`: This package implement the types for the Tendermint consensus light client as specified in [ICS 07](https://github.com/cosmos/ibc/tree/main/spec/client/ics-007-tendermint-client). + +## `proto` + +This folder contains all the Protobuf files used for + +- common message type definitions, +- message type definitions related to genesis state, +- `Query` service and related message type definitions, +- `Msg` service and related message type definitions. + +## `testing` + +This package contains the implementation of the testing package used in unit and integration tests. Please read the [package's documentation](../../testing/README.md) for more information. + +## `e2e` + +This folder contains all the e2e tests of ibc-go. Please read the [module's documentation](../../e2e/README.md) for more information. diff --git a/docs/dev/pull-requests.md b/docs/dev/pull-requests.md new file mode 100644 index 00000000000..477a741bba9 --- /dev/null +++ b/docs/dev/pull-requests.md @@ -0,0 +1,68 @@ +# Pull request guidelines + +> To accommodate the review process we suggest that PRs are categorically broken up. Ideally each PR addresses only a single issue and does not introduce unrelated changes. Additionally, as much as possible code refactoring and cleanup should be submitted as separate PRs from bug fixes and feature additions. + +If the PR is the result of a related GitHub issue, please include `closes: #` in the PR’s description in order to auto-close the related issue once the PR is merged. This will also link the issue and the PR together so that if anyone looks at either in the future, they won’t have any problem trying to find the corresponding issue/PR as it will be recorded in the sidebar. + +If the PR is not the result of an existing issue and it fixes a bug, please provide a detailed description of the bug. For feature addtions, we recommend opening an issue first and have it discussed and agreed upon, before working on it and opening a PR. + +If possible, [tick the "Allow edits from maintainers" box](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/allowing-changes-to-a-pull-request-branch-created-from-a-fork) when opening your PR from your fork of ibc-go. This allows us to directly make minor edits / refactors and speeds up the merging process. + +If you open a PR on ibc-go, it is mandatory to update the relevant documentation in `/docs`. + +## Pull request targeting + +Ensure that you base and target your PR on the either the `main` branch or the corresponding feature branch where a large body of work is being implemented. Please make sure that the PR is made from a branch different than either `main` or the corresponding feature branch. + +All development should be then targeted against `main` or the feature branch. Bug fixes which are required for outstanding releases should be backported if the CODEOWNERS decide it is applicable. + +## Commit Messages + +Commit messages should follow the [Conventional Commits specification](https://www.conventionalcommits.org/en/v1.0.0/). + +When 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.** + +## Pull request review process + +All PRs require an approval from at least one CODEOWNER before merge. PRs which cause significant changes require two approvals from CODEOWNERS. When reviewing PRs please use the following review guidelines: + +- `Approval` through the GitHub UI with the following comments: + - `Concept ACK` means that you agree with the overall proposed concept, but have neither reviewed the code nor tested it. + - `LGTM` means the above and besides you have superficially reviewed the code without considering how logic affects other parts the codebase. + - `utACK` (aka. `Untested ACK`) means the above and besides have thoroughly reviewed the code and considered the safety of logic changes, but have not tested it. + - `Tested ACK` means the above and besides you have tested the code. +- If you are only making "surface level" reviews, submit any notes as `Comments` without submitting an approval. + +A thorough review means that: + +- You understand the code and make sure that documentation is updated in the right places. +- You must also think through anything which ought to be included but is not. +- You must think through whether any added code could be partially combined (DRYed) with existing code. +- You must think through any potential security issues or incentive-compatibility flaws introduced by the changes. +- Naming must be consistent with conventions and the rest of the codebase. +- Code must live in a reasonable location, considering dependency structures (e.g. not importing testing modules in production code, or including example code modules in production code). + +## Pull request merge procedure + +- Ensure pull request branch is rebased on target branch. +- Ensure all GitHub requirements pass. +- Set the changelog entry in the commit message for the pull request. +- Squash and merge pull request. diff --git a/docs/dev/release-management.md b/docs/dev/release-management.md new file mode 100644 index 00000000000..09869569608 --- /dev/null +++ b/docs/dev/release-management.md @@ -0,0 +1,82 @@ +# Tagging a release + +## New major release branch + +Pre-requisites for creating a release branch for a new major version: + +1. Bump [Go package version](https://github.com/cosmos/ibc-go/blob/main/go.mod#L3). +2. Change all imports. For example: if the next major version is `v3`, then change all imports starting with `github.com/cosmos/ibc-go/v2` to `github.com/cosmos/ibc-go/v3`). + +Once the above pre-requisites are satified: + +1. Start on `main`. +2. Create the release branch (`release/vX.XX.X`). For example: `release/v3.0.x`. + +## New minor release branch + +1. Start on the latest release branch in the same major release line. For example: the latest release branch in the `v3` release line is `v3.2.x`. +2. Create branch from the release branch. For example: create branch `release/v3.3.x` from `v3.2.x`. + +Post-requisites for both new major and minor release branches: + +1. Add branch protection rules to new release branch. +2. Add backport task to [`mergify.yml`](https://github.com/cosmos/ibc-go/blob/main/.github/mergify.yml). +3. Create label for backport (e.g.`backport-to-v3.0.x`). + +## Point release procedure + +In order to alleviate the burden for a single person to have to cherry-pick and handle merge conflicts of all desired backporting PRs to a point release, we instead maintain a living backport branch, where all desired features and bug fixes are merged into as separate PRs. + +### Example + +Current release is `v1.0.2`. We then maintain a (living) branch `release/v1.0.x`, given `x` as the next patch release number (currently `v1.0.3`) for the `v1.0` release series. As bugs are fixed and PRs are merged into `main`, if a contributor wishes the PR to be released into the `v1.0.x` point release, the contributor must: + +1. Add the `backport-to-v1.0x` label to the PR. +2. Once the PR is merged, the Mergify GitHub application will automatically copy the changes into another branch and open a new PR agains the desired `release/v1.0.x` branch. +3. If the following has not been discussed in the original PR, then update the backport PR's description and ensure it contains the following information: + +- **[Impact]** explanation of how the bug affects users or developers. +- **[Test Case]** section with detailed instructions on how to reproduce the bug. +- **[Regression Potential]** section with a discussion how regressions are most likely to manifest, or might manifest even if it's unlikely, as a result of the change. **It is assumed that any backport PR is well-tested before it is merged in and has an overall low risk of regression**. This section should discuss the potential for state breaking changes to occur such as through out-of-gas errors. + +It is the PR's author's responsibility to fix merge conflicts, update changelog entries, and ensure CI passes. If a PR originates from an external contributor, it may be a core team member's responsibility to perform this process instead of the original author. Lastly, it is core team's responsibility to ensure that the PR meets all the backport criteria. + +Finally, when a point release is ready to be made: + +1. Checkout the release branch (e.g. `release/v1.0.x`). +2. In `CHANGELOG.md`: + +- Ensure changelog entries are verified. +- Remove any sections of the changelog that do not have any entries (e.g. if the release does not have any bug fixes, then remove the section). +- Remove the `[Unreleased]` title. +- Add release version and date of release. + +3. Create release in GitHub: + +- Select the correct target branch (e.g. `release/v1.0.x`). +- Choose a tag (e.g. `v1.0.3`). +- Write release notes. +- Check the `This is a pre-release` checkbox if needed (this applies for alpha, beta and release candidates). + +### Post-release procedure + +- Update [`CHANGELOG.md`](../../CHANGELOG.md) in `main` (remove from the `[Unreleased]` section any items that are part of the release).` +- Put back the `[Unreleased]` section in the release branch (e.g. `release/v1.0.x`) with clean sections for each of the types of changelog entries, so that entries will be added for the PRs that are backported for the next release. +- Update [version matrix](../../RELEASES.md#version-matrix) in `RELEASES.md`: add the new release and remove any tags that might not be recommended anymore. + +Additionally, for the first point release of a new major or minor release branch: + +- Update the table of supported release lines (and End of Life dates) in [`RELEASES.md`](../../RELEASES.md): add the new release line and remove any release lines that might have become discontinued. +- Update the [list of supported release lines in README.md](../../RELEASES.md#releases), if necessary. +- Update the [e2e compatibility test matrices](https://github.com/cosmos/ibc-go/tree/main/.github/compatibility-test-matrices): add the tag for the new release and remove any tags that might not be recommended anymore. +- Update the manual [e2e `simd`](https://github.com/cosmos/ibc-go/blob/main/.github/workflows/e2e-manual-simd.yaml) and [e2e `icad`](https://github.com/cosmos/ibc-go/blob/main/.github/workflows/e2e-manual-icad.yaml) test workflows: + - Add the new release and the new `icad` tag. + - Remove any tags that might not be recommended anymore. +- Bump ibc-go version in [cosmos/interchain-accounts-demo repository](https://github.com/cosmos/interchain-accounts-demo) and create a tag. +- Open a PR to `main` updating the docs site: + - Add new release branch to [`docs/versions`](../versions) file. + - Add `label` and `key` to `versions` array in [`config.js`](https://github.com/cosmos/ibc-go/blob/main/docs/.vuepress/config.js#L33). +- After changes to docs site are deployed, check [ibc.cosmos.network](https://ibc.cosmos.network) is updated. +- Open issue in [SDK tutorials repo](https://github.com/cosmos/sdk-tutorials) to update tutorials to the released version of ibc-go. + +See [this PR](https://github.com/cosmos/ibc-go/pull/2919) for an example of the involved changes. diff --git a/docs/ibc-go-image.png b/docs/ibc-go-image.png index 649c879bad963a747cc6042dad4b0fe14bc18dd4..70d8aa202051d34d78b91622e0942ce71f58710d 100644 GIT binary patch delta 1343865 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⪑+J| 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%0AMAi>%))7Po$%2B$+C8=v0gyW%=vupXnvQ!t& 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

9_m@03y}PWUnPMTvIRnyI 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

ZdWct>4AX-KhZiyLDdE0nE;HEE?{h-_+ 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

  • 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 G2{(pAm8ZipXtkwNwuaNv{!z?Q)S-5=s19}+ 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 jlw5kQ+V~rEd71 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

    W1I2*O9vC^ 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 delta 1349720 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 ~wTnxs$O9slD( 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

    sE1R$-10UY{5uOEJ+($oUr=p)@9%kO=iDgZ9 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%0DnlcT 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 l1#oCYc}6_SDf7-(qiQ?6 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�kNzuXsX(cG6UzFsZPi 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ÈmZD^G5P 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%0A^9ZYnbRm8TKSE5A0B709*xkLM$3THbY+{ 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

    XksB?|@xJe(+ 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�(8=3hl5Kfc)!Z?A;g@xPsc_(2)LvJ{f*k} zM95dv*PbmN2B_3dzb`nEQ627=8YgpmfdeRpWuK0?65II3%GLlvkJftllC12j=(0gR zvo=H18W<(+XjZK4?E96_XIDos9Y|S*XiZdhzN)hy#7!=?^Vq@V8Sy>VHn~`I6JJd2 zs=1gkq=b{5I|D0N?ybll$3IFOydffl9#ma+WQDkUU(>7uNl7FGIgB7ylGx8UpAZvR z#7FM^iF;qHn$*u{cJ^U}@<&9$TyZ#eINWYroine64;vN;fsLo+p600;1Xrh$f<%fq zSu!h>`vFAp(QJ|Jw!wsg05vpBue5M(;xxPWWw}ZC#^M5ljOeeYcMLYB-@f0Jpsr>b z)U+w1wZS@CPnj@^oyP9x{~N^?4m51t`I>fB6^eE`@TAOpt(T0ln4IFjJtF`4p_XVyfA&oB&W3`3!WGY}GOsvvPy5XQ20D?(uaMaceE`!Noc=`S zm~;K{{x{jk-MYvu(@>>pL#T%Ghhs%`&)=ZuSCOZMujOlTA8MXq%HNYWrd8u7pCFZv znS|{+6ZUc#8DVVKc*G#~B}C;THT8&G%_@h#q~2ycA*Wtbkk6Y}VG!MZOgy%G{LQ!= zr`iKsX`xOV4TT(w>qqGQB_KH7l<57?(IQDmctr!I(j41jI@1FV1l>k=pmhJqd6Q4R^w=4qC^;$cZFRdR}@VJ2x7y*GMlpcb#_nzn(ydY@7F)^gTAzS|6^4 z9hSDRs{r_?fYQS8!%vdW<>Dz}|Fr00xV4l}VV}P@+JD>I`T6JqRJ2lf)?*>Zuo<4| zjkJUb>N#y0d0my-^>Ep?*K2rQx5aFDL&UY<2*E|>33>}W!e6|gq>Rc(3Y?js053*+ zF+`p(WppT})jaGOyX=c4-Y(R3y7%0@`wV&?3$JtaFpXO5{zMrgK^}lIr7-RH7Bi&* zVMS4%dH=;kvYCE>#JS9d_TWI(f>rNpJo0Q0D{N;<7%xTGgW;V$l_p~oRt)GGM>Rtn zSCWF(R5Lix*b1Nj0d+#QUxigCck(ZK^WD4MF2(*ec^{LPgV-Y~2UI^4I;k;UQ5_0v zw%+;Y^$`u~LqO^scwy4mfA;?mwoaLU7r);Vu&k{72`N7O{NQqQh&A;Yk);6eT#RJu z+}+pKL67-{B>b=u^yu@(EcMzM@A2!I?R!RhvPbQooy1KUb=sW79tieJ1P`%As}AdR zT6cU?gzQv^`hYlhPy_4e#k)RNJ5g| zD?`=0ubPK=m!8EH`USN3GD+pu9JF+iK;25;$KlXdx4;bGf$`>~DjhRfmQ6t<)zkv`zG zL;s{(>%p7wfVB!F$}wM{v8sI9Bza+s&JY`EgJkJ9W8T(`IzA(>zvg+@DWvKP>)d;><`q2( zPW{P)ST{Ts+xm`iI6adgMvJmxCAG%A*){RF@t`N-Q(Ko6$xEbxs35W*yY*kjA0 z$#+tY%7Q^hF~!6lsg*Q3bFt-ZNp5)%D|N|L`_bxXqY=UtN0 zS4%t*4RO+R;W}2```oOMw|(g+qSkS`Y@6&}3+tcSH8rFQ`P& z+GguWlkB!PQ=RDaaw4JKDYM`!ajFTZ3mTj*Q>~u!rkf$J-~$msodH4gtA`^=aG(#7XcIlWPAf zircI6Do=*~r;H@YPQy59R?GwhmS|Pjl|5||97Uy~5zudlx-t{C-M~3QXo<$~XC(*% zMR@jqo++YZGC>o6{G%qB7Hi@v&*RbVM|L+(Rb)H zdQ^%Tzi1D_0Bkfk;H^Bu{I~ofV;_s~>`kS2oB8!xzYb9Hi|Yq;4BR!ljNPMJsjQd6 z@Sd|~(U)h9R@ganPZOnyE&gxKpyf-$VLvRNv0Gj;{G;|TRT#?uh*Ho2Lo0~(8~sLJ{rtSGpzzkZAs&FrxxhTSd%F4_D?BnZpFk&c@?rS zH&taN!)0=zq$wyb1)O=XxZVrawK6Ezw@T+~`j=IjNLQvN?78TeaQ>9T;X%Q>$C%)F zL2>#|^QZu|=&#r>Q?|0LvxO`YRjVxjB+Td7F6cp{(vFMsp0!RxujJTb`!L#BDm7bG1>w%g~%hNk0M z=40-||DG2C<@kymyeA4tK|HRBSmZ!p>Y~wMrW$P%bB?VVO7Sl7*YCP`eaV)2bC`cE zWSzW1J9ACb<;P{I%QQ0CkDAlz_!NVbcJn3=N-)W!5SHn5q?@S_de`+zeY3tzVkS^r zha(L=&Mqd|G*Q!iF@RJm1=>R)UZa|NlkZO+GxwTy!0Vomh3zqnx1NuAYk=$fNrM3X z{CS)@bn?X9p`am?c3gILQntOa$>H*eNp{X!?{?TssuqW2gc~;rtBTh#ca&Pg0m0su zwFPQCtg%x>LOUd|Cjs3RO?uX(PVc|jYOiIKzB4>&+BnO97Tj$6xR@0hs14@F zo;XbP4dWXfer+N?X9D3^gCGormdcHKn}6IN@H}p8*Tpv<< z8UJ;4ZZgkq`S663x0ZKkmvf<_SHl^dLQ|^!FmTbfxio$SGhutK+=GziPyaim3VR`oT;1QvfB;hH?Ts0cs&< zIe|n`K?u@4?(Q;6fsZO5nX&IhmczHD*x}2s&4)ky$yMLm@UuwX(G6A z0~8?n&3&yGwbvC~LI}`vo)n5O%5x}iQ0CX_c?M|A89MdhwIxh{c(E+N^GQ#z=Qc^V z$BhBAnHG39h7Wbic0L)6Ui`;0%Th!)SN9zS{@)At*~L;oo9DuMQjPi8 z#$i(xmM^++)OMB8*DZe}s(c^)ubeJaM_EVjXW zMK-cEer(h5>?sQ`u6W_tQdVgR^WuOIMr!dg?|!K$FL0v_L@;pw9lhg%c8?SqH7UTErUkt z!7GfO+X4*U`=;KI&DEJ##9k<~1};SmME7Sfp8jDwaSgz1Yk~v3-r27@-ElHCo?#2= zTVO6FL2hJ~`aBKZFBaXFj1FNnL007=f$F8c$E4uQ6Ny8;!JVT9y`bc_-4YLhiw$r_ zM?c&h4O}iEj$?%=_iKZBM^_@hUr8KzXI8y|E@?bLejDAElo2 z@Hf@n&7AF+E^-x&jVMOEcf1%OOTxOVc%eut2ViFK0(Oybn{7L+8{9%l+IaM-(`Us& z_WIT1|He0!K{spW2WS<@CNi9(6}_S{`)NEM$x5EK6@udk@A@wWE6I3;bhVfsH-8>A z;Ae)8xLD`Gi?jP@4fQ5n*<_v+X17RFr`Y(4UbnCb7e_5Rn!$uY35Dc+GDqNj+jIZ9 z!iPn*D^adUwj=wR9cnhA^_OZJJhjg(^12-uZfGUiWU6qN%Aq8rpVqvTgndaXvJSqL z%(6$huLs7;W!*&$Brv?Pb;~zpPU*IbF1_*X&|%tE_OsjnwN+<+zgbLx|A#1=V`Nwq zc@L&d5z(~}BilzAxMS0=Aff}P^yAY~wXjYE?0-J6U5dFWegORh?pb=R(AlJCTl3t0~sd>;$wjqYge4#8;aXI{HuZP*Ux!r(svQL zI0=}d!Q%Yx`-UGeV9L21?^Z#!PV(5FSIRP6D&-_$r7ww*@a21`Byqp=qg|@cCI^7J za{b{EFEhhcy@=t{SA0@RF$N)fJ!$F^{V0X-*hCr4pE8}&K;iUpIrque2VzOvKbVxL zX|?tld1V48{JrJe#2+|}^XO`|plLIy$TKJUWV&P<9{999T z#&>l3b1ce)>&th(G&HWF{*Zo~t0Eb75+Qu@Y$7*Gm<`{Vr%0>orqwQf9jO^`EpK>dBQ6~859e@2+6MD&>C(S)JVL3iTRW4B3H%b>wyyEj?dk=&~oqAw`4 zU}VTy(;@B`_)c1~{;D_p9&^Ky(<}QKrgt2_Moz;Kq@!VnW5sW+O9S1#Z&7G6w}~~& ze+l^?W9$$z5(^$gbxaB}*2eN(_rU% zPsfRmLz1&X4YLm9;C~Ua|8)O*!ScAR?_SDab2HUHE@9`7c^@wM^lGE?> zE^G%WvHaJ1=?L!#e>Q9XO?~N|zU=e)X&dcL`!C6VL!{wiq*EJw0gD(574X=AaT?D6 z4p>1bcuN89LOf~28=dw@CvKShae#Av4MHJX4(A;Ko%WG%Y(Y0SoRMYZF>!G|tMB!Q zUm>_GwMTK%1?v;Tf!QhsXs`uGiLFW!M&ntw^CnkONEbn^f8OmsuoqWddkkk!Z`W5R zo;}st9*dq9(&WFOWc_IUQXp@*^%`7%^HsR_qxa$Y=T6LQ@^ts_wEK7ZIVd=Vj$$_d z3*;*SX77sQVER5C`~1lh&*PI1JXiBweq@h2+GD&>b0)dJ@EnB;5?VAh(bfU|Me_>1Qhaui~sINYE2xm^1(rlCB zx41pho%+7$ju&JB?l(JbY|KtaE>E2vMqIZ4X#IWs%=3uxyXtVqW6-1pLiI@PF~Bdx zm&Q}pm$968n=(Bclf8(1^|$$Gipx=zfB1zhZ}tCo;^eN@o&>!@87bW2veW$G(0z@b z#eE6Wf6;~cc{)XBE98HbPdf9nNt?7uo3u$UgtRnSZ_lT+6e#^n>Rk>7meUL|%tMv($_^kkTb!0kwKaj_#GRxP!T_&D3-N`e( zPLnc9tY9ae6e9L148nT2TO$lxW(4=$rLQ0Y1-o-A7aEAy0t%V2FY^j~S7{x%*xj1m ze|IiF19AbRWoWYw!UCNn1`Ub6YubU#b(PAX4fxuiMN;vQ2k8M=ZZEM|42FvDNAO&J6>d1 z3?Ib7G^tQJy%z7Bt@MlYoOH76Ao-@Xgq{@wpG zKKRj3E~zTV(@1uL@va&47wHXHpbb9wZ+sw)52UY2+-4`am8iD`IEn|*OpcSTWLQX% z_`DDClONs6`fiL#ZfcoqcFBCL-}Hn|harN+6hwly#XJW|GRo>C@@08$a-_mEe+ZZw zVRytQ5c@O$HEMx~DaYSk793(&2j!$N$K)@SzRAo=d#}(2C`m6?Fyz{MII||w+ zwQOgw$vM+>^rPkCp6KhYz8u@_{*McE_I``=ru8ZOCipSVjtRqq2(xa31@JqfW0I9@QtGc?G~hPI zc0raBbO}FW;*%|xYts~^+codO0&A&sc2DVB`P1%xRvKBq(hltu01f_)f4&$R@i^m2|XUN&9k-{HlXuK$w}xe^o-|xTrP&w#-Gf$v%tLzUD%qW3#i3X zvVHWs!w1kYsz|&VzJw7qWSwn=wVNc(`jC)L{KInG5v`E@!d#uDf4jgF_FZ1$$n2;S z3Qud@jtlm2(CX=Q{PP!m_2=ULyC13F|Mfq4H$MG|hrE+QeRDi15q($tD#Kge{s!Fs z%3JXZKmIRq^7yG*u3kl;yI;3o{YI3%`x-1w`L{Yvv6>wzv4F&PEYq&xx^!q9gH~2X z1KN96f!eDsJAj*}f4hTMLE+0U+wXe+@uyDW@l_!B@uyGW@l{~>^{=`PcieVG_&ki; z3FdQSy?=W;;`w8%!0-JJJzJk!AXSmBddPOdj&%`~K14fqx zEg0;4rTkR!JmmTY-hHe0`C53+79V&$(%@~^T)=7P`jbx;f6`-~MdhFr3s$$bEr6Tg z)4-+CFg8n+ID+36F6sAD##pRU@i@Ak^Y*n3KBhe>DFn>m+A2jHD9`6@^+Joh=yuh4g~aju);pS+T`r1cO%wy3<_+ zLq$eHO_2^YfBmQMLmW?{KD>>m{MdVhUiXD<$Z4|!c#}41lQ!uEoR*x-3;kYKhkf#D zs@?Kio+TJ|tABB#XU+QywSS@SJ*<7cTyG4R;&n`BFT=!xmlm8YWnol6Z3GN9su3Fe zaNAM0<(btXe zVa~XBvD6Dz+u$;Q;bAdP2zlG?g^D|A2{mhE$0j94pE#V4^fIrZO9g8KKU9@Z>eAqs z9cKT@6F7ZiB?h(DL8BcMOWfZ+@qk!n`5FK`d1l~+BP+Ti3$u1oedHYbN@Bkp3vHsS zw7ca(e-3SMA#)fR?&2Q50wx`~Y$6R&${cshQ}DhG7`U=Raws!dnpmYB9~4U(Ad>JP z+Fu*gwRy7@zex|z8d(Q@(%>cr6NGMOdYM(XMn%nVkrjjL1POh`_sHevtm6aljMpra zu)!4VjF_I;?%F$&7~HcInjtd{E}Pzi9q$!mZ=t zKBxnIJ6C5V>Hoq^ijead1UEY*39sDa0@e;Cv^er)c9yx)fA87K1fwb zMkVZH@6!sxcQCpXM9>5Tr0GNOuCLPUe>}-A2YC!SA(uG9ICAI!;=~W*N8=H@s8Vg?7;e~QH! zC{f>5?aPQhDCj$Lr@N)MsJ}vH+GtlYuNe;%wil*j>#>Jrx(<)*WXrQ9hWo+mZ^ul- zZMDf!$X21dMHfo9yf{Wt9UJqKPGKnpj>CrWZM#26;ce+L*{RM*(J z^kCiTJ7Dj=JvHC zH2&`APK1ePuvS?K`%zHnZs=UM*<7p3?`3s(Mn^{qOejxGBprs}OOT!mk0*`_FK(Yk zw@pUBn$yOP$h;YgajXwbIVVF5k0tu%$yzUdP>}gi?Fxg&r^_mO=?L=apE zxowLBV!_Jv{Ec7mYFv5EG5ni<{@XZr_AK?c^>6$3S57N#s@@p5pT&Z~fpg<+4>($P zOX#>35ie9n)43QUux#?Yy*-^aat04P@;p9z?~{1)=~F9z`wR}P9HCKl0lG!z1aMwh%=Ok496qK|;qy zhtE<}2Wnfz%EIl|FoZC(Uel6Z$CfI?qC0bqecMsiqw`>03}*dydpjbexZz`K7^0uP zGq;dGQl@RNY6C(>U2hvp8E(R0YF1r;l!oGq-CDk(`Da_9BX7OxHk>+p5>Fg|oN!@_ z5A@h=GD3XS1M5letI56DDv?Fqn)MeiykZ?+=u?S6xV3jS=JF>lHw7U$A^$_N|Aq9c zxWB{go7}r^Cz}B9CT-FtZPE)qnE~V9{rla1_s^H!FV&HZ=9+qzoY;%0dnwO+g#8z@wg1uQivRt3zDS_o3%i zC6e+{<}pbFYDpzBy#7oIea}sW%ye`kUXleUOX?2?jg@|YZu-2q`WUDr|Bi>~C_C#u znOU-k*j+zauPwL8h0~y-J%}tiS?=`L?`^OfFdJS2-(^YAX8lIjub7B`%i+eJZWh#u zfWf@(6r}WAgkum*XQi`#69nz4*E9LAI1;KsdfxUzq>Cl>%Lc@$9j^}|PhLN~cj$ht zwNsUb!WAJ^M{)+!OEdf<=`jKGdHa@*5Y@7FEH7rJhl0xzya(mA+2Vi%FdRAWeE09- z7yj2@!?DYb;x(&4@E`Pl^wYoaEBNDo=C9({e&fA3d-j~NN~SBzzlpBtx4bh4t^?XL zz-yyp)mhiKBU0N_qh#i3DEVokRqaPD)#=b!*-r9lI9FUH)uh6I@|MqXy0filI6ZmUNr%6 z**DldC`URWO&T#gkR(~O~v8Mbb;UB8ErW99`tQ?C}?HvPm{d$>q6IEv zwoBH6CN`O8G<4WN6N4J`a8>K}#wKlbV&9Z8(Z1>F9HeO*b&Au*M8vPH9t#_Q;B6_1 zXH7=fp9;T5(w*L|d!zGlQ8{tnOOF9=qDDIn5$(6#$t>`HEFr8zSQdHFN!VpCZw)al z`pAQj+J?VF&@ANJ=u$#2C`Z|;FFr{xAzv}hpumxYK5eaKKixj@P{iSpLNLdUXN zdqVvIknCZ9ws~-UQ)P4|hX)Sq!O>OF_rSp|>{$gK&Ys;_1(CLK`qcS){5=CB68!Ht z1ItHI-j8>K#*D9GOd>?du*)gPT_XwnZQU?iAhToKfN58Flnk)mPQfa2B!+j)xmn&DL% zIUm@+2(-FQiWXo2O0T<6z|rADvT3E$&lB#Z-RPhDMX$wu_dJASR~*6q1N-p)-~0&R z`h@T}7JqKL>!;5n#}45wU;Ku8_wT(Q{dDk+^54!?*J8Zln&uCf9o0S?++u#t^#4wA zKCbV7i`r^C2H0s}xC_+!G06TM-j4C#$&+XC)HA2*X#v+=-R=smI>&gY0F3Pi9(oq1 zRsrB*$7h!uWVSocn+T2VZr#%E%T>*z_2%!oowdT){Ij^9cgk1&ph4-q@=w^_+X)E9 zdj=cxk;_d@5s-Q!z-A(~()L>twG;G?f$ze&D~YyG%t#j){; zpVpx(*mic3Jwe(d9yz!bF~7oPNg zYFjXAyR1Wh6KrHZns2SAylm1YZPF%f(hD!K;p4RQn+L^9L0KNuEWPi8WlMSI1Jd;{ z+jp*Ow|vD1*D`z0<(IxM)xQq5Cx;g_x) zwPU_HjTYK{zj<;o4alaCA&%3DL3reUu`=dTly2}|2TT(lE{5h>mpiUpw*pzHULV3~ zKR7_9m~HM3b*|Va*Y6G@-%|wiL7bZ1WyJ8#gG2+Aok;j^=9MLM@O@C+5Bu)5+u)sG z<$guGxrV}oR%R_FkrA9Z%R|&-lY-F)Lm=lbot$U-#A6Q!rd}?}VlQh{ga%@=kEv;#%aX~;sJjzfpY;p~-C?M0T7LpYww8oA<##?} zFn^&|jxQ|M8P~B*e^0xPSyo2dK`8C`=;=6MX_2vZ zCz{<+NAzeU|Npc1Z!x!J*?AZ?#`@3wTK7wLRdsiDlk9FvWSf$I*pfw?@`DWcF;9x# z0z`=u2ytLI2m-wrK@dAgfapm{9>O>4!A^i65FJ2@X^WyPHZ4lLnB7hGy{qefIaTNW z|L2^&M)sO>d}DlLuJxa)79HCt?bTi9-+S$~<{Wd}=NNO2>AHIPIA`)S5+NRhojfra z2{r-$NhvF=E=NdzdK???7P3f9GKI!Frn2X#OSsj}}Phs)!dm3{k@c{rV1MU)=ol}7JuQ) zB<1W_wZi+M$F1v%EG6yz^hrfKDnkro6~Z5X?3?Sjh4y>68jcDbnAf}Cl5A0^(nn#{@$c5VoYdtuw(j$!>f^@D?>z4JEto1S8NfmCXB8x&*>X?P@;ltVJEL0fjfn}s zP}?8J!2wN7aI`#{{+Qwzoc1 z#PIm=UkS)q4lzOyv*kQ$qm}H2VIMnx)D>@y2~LSVl-|kjr-(^DACpHZV~R;yj923s z;Bw^EptU!jZ7M%hc187oj0V$THKWMK!ES|@;F$-$!Eess%{eDxaIlEsFWlr4<6O;+ zradSUIC2nV#0UD4_5Fa$-y?@yFe%^{JyH;N@H}u=u*17d(#fy1PVj!grmdy6T$fSmi(Y#DkrpNe9`)%%H z#M&_y4wk2;&V>PSW}KYbydVI79}`(>aWxje%hyaVIto%B%b>EfC4ztcU;Q7*zy0t2 zZ~uFf2>$WwZ|#%5|AG8lf9>CCN#MAcHzdb3i4^k;uFydVITJQoQl&1A(KNwkDL7Dsqsj)U4L>uaN~^38aYKbv-crUs-q(Ze`I z0yB1N8Ui9dZn9h0g#qnQwIpc_(kWfEE$-*8q+DS?k?i1n^9<&S+tA+2%)X8P!uPE*Q|ngZ1k7?ubty^CSFhBkyLk?qJv;mp`6Y2K-}1~+A1KauL$W$bWs3QkbrUJOTrkBY zbx~q+xi{~2fwMT@*%Q8*JhFtF^s*FRCIkHV+Y-@EhUw_YcTsza#ZvyqU|z$zE&Dsmi>_@f87kF<$^M7dkZDAc(%@2a%I%?wi6FG$T4V%VIZk zyEjSEYk~XFsT%J`(3WT`nzZJiEGwi1H=s5CH!kpKcuj**#>B9Jj*hLSvrv#jj1m^U zz+}00bC%_X(6dc{>98X?nY3Wu2HueXN!fQ^+RcfNuJjzPwLRf7+>Y3gPv|w+uEeRN z{39T5MmVu<=h|~7M`u*7Ts|R(B6HbQTLBm*K4^j0*PAg7i~s@DrA2yHqDguPW+XBuxr3)qgk9c>b!--oGZ}o zZ)otUSo)BFMdN+I$hI$eKe?LZh;S#wchLDWC;cfI#(wj>t7E81tT{EA%O30}&#^)W zsyYt*o`PnnH{nld^p)~2@w)^bIr(M5Ptb3>++~U9r?iJQz_(nl{LE+C-?v_SukGZ0 zaQv{#yW~TESlku<7xC1UYnS9pU;SL$`Frc5+pXw&HE^ZwF6~~*Rg*5WHrr2D|n?rOE)Xh(G$%Z}~8 zMVY9W-%m<-^%$Egh@MoQC}u($!oXvludiLW*2a1J{>|{=prh&{I`KAG2N0jaeyIjO z^yfQ&43k$g3SE%OENoAC!(mQ;WRBPmcr7H57xcK=!$Lx|F9i=8c_o1-a9=R_V{-{?zhL?XE*`FY4^D;EkW2%2Ghs2 z<4=Vn0K<;Y8MMG8W-!JINA_{>(7XkXgVw%(@p)?;oN&%IRzpbeXePn^q$kwdi&o3K z*&M$buu6C8j2*cSq|~73a9M484Lto@`sCs^vK<)^txzq8fB}`0e%5*14i&+IJ2388+k^97Fkk;sbInhU9Aqh<1He8 zYR0@u$!+<#E1*j}EzM~LtGUezyRHV4>_4r$ak$YInqU~Mw8tYDzvn08?^+q$WFRN= zCdkeFFv>rVNwk(tAO33jQrocsz7_sTpSpFE8_58M$cEmQRo~odVsTgiv4TJg@(TS4 zUsduXcrRcfJVE{jqB&fgB-b(kovQqQB!3gVP1a*1*<^WpoUNcvgvaY4nF@dZvB+Qk zJ7Bf+sYT|J4_R4e`xr~nkF2AX&#~i<`YQNmLMIpe!MDC6fA|0S|B>U92lB7~r9UVC zU0fgCx-I|b|I6Q%|M~ywKa~IK_r96BOt$19$7ZBE>6aCMqcNtDQu`~~IbX(qLK*2g z4M9pF=mM&!d~rH|wP!+)Q;BAA%4y{jB>U^xrc>;EY;r$o2AEZ8Y}Fp3udN|y+Y3zr zSs__65$4x~xdxVl4k^TgoD1348sMoxAM9q-zyZs@u?#{csOjiNM;}kfr*!y>fT$F~ z#tY(sAhT#U{1w9Kv|)2I`K3L7S+5#NM(|cIag5Tsy@lo6!3)dIn!U^}X90sR@;@Q@ z@0315z>s|r0jx=vKBNrRC9ZNY4w9=P29PG=OU*n|YoQx%08(F-OU|t>QJ5 z!Q9)8g&GS@==clB{mgdOdDCF@g?TM;!q0=R0{(c-Vxa<>isKVV-9Q`yXZY@8T;yxE z+(n;uF=>;2z0Y|heHIRXO3y@#usLCK>8}URL(gdXZXglLO`^26tBWL`0|@+2q%--7 za}14nh~Z8?09@vDNxpIUAkEldo>Qk_9ChJN@8|5~u+`sow?%jVV z_wV;}edl>5ea5oT;VQNVpP}b0$C0{zc zN}<7W%`?Machy1fl=HCpG9+{L)|ks{H6XZ^-LEcq{ZR76XK?4WFXJ_!oZl7v-sEuFHS^_kLR* zoIH$vpo4awc}3=nm!e!UlC=|PM~PqPvW|L96vEy&J0`h!5aV*%_xCLDEYx=W|h^VeEZ_15jGIdxfL#!$MRR3lUTk2bhyC6D zQ0}T0q7Qg#rt-#+6Iw_z4fcOkR$6$`p3`Y*#^H-``3}{Pe}`$M1P!>G6KBNt$tzuE zKmYU>Jc4A2Jkz}4@-Rx6@?g*Wz1x^$4Um_8$A^(R2@ZmaKhwE_tOL~2hpmI&3 zR44SbXU2Q@{NNhGdg<^vy!SqFVK`#$z}^eZ<~Z3B@YM*3)S1v4vnpHgYN4#@zJb<1+8lwt^lPmCM6~M$HchGUM9v=bj2SG%2t;vj@RUl8oU=aClct1#K6cuTT;_o zJ9+R>e(SgYnf%@V$A2o@x8y(l2mg4VggznLvG!XMKdG~S zSk~Gvnvx4VP0%Yww3;$N4E{k-FL13ATC$dLmoM9I}k#Dy&5K1ygaCQZuuqU?*TImSp4 zAYgBiaJhW>LR**te#}Y&>0fclR^ZlV$Pf2Mfil&>myxhr?eCOHpWcl|yG90#s7k?F zDM{|czoor&8>+R59hXEVMK2(YDky>VwrnslB4+Nh3CjFh`64Rw2kA@2{BiHLv@Az{!F}_ ziRF|ni?csjJQHA+(cTE*+G!g}1`t0I4ykW5Q38a4zEbQr>s=jeY{B&kuY?D}M@lF$ zE3_5yg^f<9bl%DN3yo8`rG5`G$_=y3XRj63u>XNNi@q4*yvIC2`rZhq+?(_I)$5n# z&;9D3kst4qzPE4Ok)Qj2#aHExAH6MazxG~?dt#qQv%iI>miK?vrzEJo?c9Ei5gcf+AQ!IHs^jC*DcBl%zlbM zSio%g{!0H_p3979`dY!1{}SsV95@H&XrS(J9|H_Uxzu?>e!}Te? z#(?CX<~a_+{Iu=-G;n;yzBaxH!PH{f^Q1Izbr{o)#(Y2g5#2R=7P~wY>&K zPwAI)3=b|D8ofPn7%y3wcxGV92KrZCSOxaOF=twx#+SMb57hw_myit)DSz|kZTa=T z|C{n#-}vY9ul>bek|(ZTmH&TTONrtFLI)8Hep8=f2UGS*+F0%;GNXQeqo5nsxO2D=F1hw88gK;<{u);e{R%EDYRB z(N)j{VQYPS5$ty_Vq(5^eq44RUA}N$&YU?T_xHipr9HN3vy3(-m4D1wF`*<0JctZ% zXW#jvlT=@Tc67ziyp1V#fQrQ8J?m{8d7^ZpAf=g+snR&hI^?7<10^(3A<+{rj#i1} zo&jHu57FSK$ya?Ku!sc$>^CZXZ|A;FL;RP9SqNUpAp#*Pl{vv3n#qg? z1MSFkU88|qkyvTVHUz+0;zyMH<3N;23R^ZA$%Pn#Rr>RQXn%a{iwof;_}Cvtk|f_V z=VNWv2F_nNBbWC{->X;8$+@#fZFleS@guo+_mLc*JWTx|sl#zWH-Og?zlnbgJ!rbI z=uwFFLP>z4S3nPDSfWw%HRldJA$=@7{({S-Hzx>Gth(jO?~u;cVIb6AI%Sf!rX#G< z17sxnXA+fSKYuP;Sai|lpMI0ns{y++R*ogdzaoWsubQ6$OZqs;TM4HHA9TD~42O3@ zwxMbrdx*!JxM!DWQY4`|4rlM9=zoGg49@=Ji?2K*uYUOz`PT1#w;et3>X%=ZZ~n9I z$VWGB4P#Cnro^zn{HtG+r=DGQ|NawsaPlDVhv#-Lzke!n@p8&P@G5DW*>bxjvvTB_=n$>Z-3{Vwz%Vos~6=P z|NO^$f8;@d{n5jny!Fn_mJGgo?}0r1cHo$2fe)(MWMJU6BrQ0Wew~eoRF7qyT9^>Kbm#)i` zm!6b2K6+h_A0Ed-Vk4aexn>yBk0qbhvT8?OG=E2y)KeBb&#`KYg=gU7v4EyDJ zxViNz#s7f*YagfXZxWFUc=~RrE z{B*on@9}dg9OP?Y**c(x-w`Y;!QX)tIS@|Q5-xNj8N*~L?ZJ`v0A34x`Y)3PKNM2w zdh8rDD>PQ_bHvdWPih^%YLbuv2mjOi!G96t0CR6+@ZqPE`3ymDn@D_epnS7kO^9^( zE0*%F8$4MPIh7eoQgEwZ%(aB@BisDT3lKP_1X&p0XWom>L>@!)f(#keVcGkG!*a{F8WXa4 zRU%|aN&va&6u%IxOzpL;CnA{WxPR;KaXH-12&rb0<`PeFz|_g>O+=z%a=A705@oSM(C81!VwCXt$=OUKx{I#KN+zKbf<&^aWkU5`(Z$nm3}D zH?NYVibGxl&%6|->V=?{)Wko~0*som-t0lkX*rlzIFOfT02aS*(CG+@Wq+b_+Xq83 ze7Tm8N$%QL)FJD}d*+s!*s&kTmX4c+7Xft{n``fHYoC&T@@>K!G$ z=i@>kw||$wgQI8Al~YL$0pDn6B0nK*Qqm*10D3~5S9cb>lvOFPC|-8+h}%ef8HSz8 z-suN^@6-wmpyWE?RHqQ${(o7Ms7qUUwlIQ}MyRf|fPRQJB@@#*hgrUu=%6c^pHY&P zcUo87nRY+UdlpLEnn_t`0Ug!}Ou=7>oKe#$Pl-7rvom-ML}Lm4L;i{JS>brb#}~Rb zOEdRhN&gTp_U~y>XBp0cB^a{QbGCsb8smOdlA0A8b(y~7azhi>?SEM#W~7&kk`U5Q zd}d+lc9l%t!lo(|hIK8*fp77#fbS&VbaH2+^uqZgxqRiET-pD3_S{jEgM0gA&~mo# z@$tj9XeE-B!$`t6bzL)@s?;71J>rT13%t{e8`+t9KgJPbPaPBHS5wTDSp#wzZLuGK z-z9YAOi9PtY*h|h3V$|5c)^MT9Iy$)N556yg%tZqx?&GM~C0K?xT{x z{_8LN#jnWI`vmSce&hG#*%zOd&%XM+{PFL9UvA<jU=!<$r}F%h|ub@^AhH`R=m& z_ggm#f^FQ0i21L4CHaJoC*J+^1pygS{V^0_oPx5)q(EY%t(dXs9C4KPx zU;ULY%GonV@}K?I_j>v2{R5XSoRt@zxgzhqe@E`zIZ45xv%?pC@X@V%^8FvZEAQX9 z(`50L&ps`m`+v++^0}9uZs&gA+b6^qe+E*9q-`(noC(^l0<7>?BwQtrqyWrIX=#)u z1|2(N_e8O~ZJsp7Vt_vGSj3g1lHDdX zX5nSLOO-E8JuO(&Ah4q(alni3trJx1=N{MIe^YS%X`UP5-+GoO z09()gG=FfM_G~&8`Mpj8mwSBPUc4E9;n=auPd${r#`_~&IZ=rjRJNfaB`686+@yLU zs>Qv@3HQ1m6YfTbF$_V&n3?Ko;;0FDxgSmOz2UI}y2vTt2#=!myPlRftbt^nSjs8= zz|kK2-saD(-;`j)mAq3>gkKavFYrVEB@z)LD}P5bgA7U&-VGQ6(_t)5193Vr|AVJm&zUa{B*f!v-yxX7Pz~C@8 zSKQ2VU7u@tOym@LZE#)A-*3Br!E($q<9}oxI%hOvN_Ag5-4j0}9#_1@CR&xKsRf33 z$X_*rcMPE8AtNoRG(f<-e2ueWAqx<0OB_s7?$|5Iuh!6@AT8xBKc_5o{7nf9Xs>B6 z(hi!#si7(nG>Z0uMoKnkz(ir>)EA)xJy?cutuGcTr>AZ`QtiOJ@Gmv?j3h?_!FA)sw|hjV`O40 zB^j740mR<%(LS0xQzApjp5%BzOMeY`MS$bft4J6__5gp;xsn%>wIakL{x8!!WX0&* zS+SC^gg8f1m}D-`U4=g>15Nt9DAhfi)I}K*?JIB#mE<^`G#9Q&=uoY^5B~T2Jl6}D zr6$w+LWh~~(3|J8Oe0ZkiNPQ-?fzq4QIdC10K0I-wa_#rY$;O~cobpTuz%Ri9hwPx zLx_L;GiVom;q@%@Lb{OkRe^!0N&$$lBx3zMW{E-H#n9W)Cb z#WR_h8Ih3ZCJW|dpFz&J^oh=+u1aS~XQi4qP|+`05A~#gHI%VhOYBv?cYT|K%TGz;5sR7 zMH!b$R7eTkp+o~zo{*}Wqeqvsc0>j1;kJmt7t;ZM(MPAjApv~24%gv2T%WRQOm>Wu zu+!c-?YVIR&41swKL2T9Jq_-$ueyrXK5_r2wL3oZ(_cD#w&C8^bK8CPG4frc!v6{-WH7=e~o5+Dx2^DoQiJl zEdzVSu?xKVbISLJJ_>(vI|&qniu)1l*l3geF(sHQ$A1j`5CfLg1Q$Q(#E95xNs{I| z!c%clt^xntKYU)nc!tI^sixJDAHNezmfnN)Dgd0c8!1xSxkhCSIqf1zk(iy>s)3ER zQf~l8DSz^oyqBq8b9lK=Ev-iXV|Vxs_eduw;don0WP_82E|de>`RpdI`28q@%I}5F zGM695*U*fK=wtD$_3Hpb`GMs~MMfP{lX6yY##dKuCZ(wY2Vpp#p*FQXvVK=LhM5Ry zt&Dv*>Tce;E&thXe&bJdKJah-_CJ?@>#zOIc7NXQ$x;$8fRL5R%H)F#`gqNz;}A1} zq&$Qo(^ZDl$3L{^_?RWr_w7l7XWVU)?H4YHe#*|37B|S$LE($gC9GSZC^^8Z@jS}i zf(f!Lb>g!3heRBZgXc4um{38#rH&KEBa4^p7Q2}U34Vr@x&AlY{-zL4nt4C3Cc5PySnZ;{$Q_a|J@Sv z4G$+vDd>FyW`X78PE7E*u4RyHvL)S8K0>~hMp zwiV6Or~Hc~XXhCU9fEd?O;cJRsE~iASEYRD_lf7c664|W&q zlz*T-`AUd)rIfAg??|0fu&jsfB0n+uNZfDneP|PE4|<;UQEeAmLi3(8hSE;pfUWc+ zF=%r|gVTV(U`qC7Dd~LS;@MW-`ND;>`y{Zo9lrPOJ&?P1AGX9z*r|!fV0Dz5umpQ4 z{cd@tGoA@mscCz_dWo*;*J;s-Ie!*b+hKn#7aocZF&a%A!>*K+ZsKs!Ee;Dt) zvPjKQqCcx7r^tpNDOd+!4WR!n3^@!f;tV*uh`WEY9Sn^uP>Fc=6k33tt zO5K^cN&hEt@bn4(e&7Z**R*!S@H*%LGQ%is%thD4j?8{1Xh|2~+}S+^#Mb6+#hj|Q zJgkI#xEs1x-W$&gAw(mJG!w&f3*CG&1&ooF+q^1yw0g)e_N}Z!s?N!1vp3oyEKZ?Y zpH4_+Y(84#sa&*e?svqzKgNf!uEv~{Hn(g!VZDn@n6tJTs?n9qJJeDiArFoP`cQY- z<)cBieR0CKB-an%NRCJfS*mneqOnVx|LU4?Wd)7kLB@|w@Am0lWfTglCVYMMY=V{C zA|2fgvp+fBr&oULpGpl`E9X{hVA+23C(bHYSImjySosFeKo&bDDI_QIkhnOkR0t^& ziyo(XCSc*OW+19-ZLbrO^DQF-U~)Js924)!>?+x>dycz+9jYRmwfyNYBJ$x~enaGC zyAJT8R@A4gJE7N~Qzi6y5i3;@LS5(m<>Ph4W~Q9V3R!?#AF77gR-W4RlVGdNtwq09 zv%nukdEK0nM#5L)BV9*Q7;TpuFU>tzu@L_qeBAg_)fvr->~IN?tktDX0FVs|G0H|p z6GtopHl?Ew9YU!^2*vDf4<$p>cMdwi><}i*&j;6yGivw^^w*9bmGRTjACQ-A+iR$6 z6xiwdct_bcMkBRhUZBgc{hDx^T7m z#0%oUA&-8IPU^GN8(O@h%oo%PQwa7F6$ex}f}}=dG*{}Qs?c!hAxUmr$dYAOy3DJz zZ*Pft@RPY8Lb9`m!G)diPa={T<}*xm@cOM)hZT;%<$&^~!H`8p7#lGo(CJ}NYthk7 z{7u?EDwAlQPDC71W5wh+?mkI_Kf5E#>NqKpnYukS+R`tip&Q$fM(Vk=NK(Se@ejrW z4>|TW$`?>wrl9`|m>NECPL)iYyUih5l{QwKiTp1+@G&Ab-?**7Wq+mD=ZO=QzoVrb zP+D8mG?5mg7N+;QapCC(0Gt3dY4dxj-AeU0Hh%P@DkQm0lGvf(TTv=-Uhr=))m!Nt z-Jn5lBvI;z8O@Z43<}fqdOy|M?;y+`g|Ep=Jtcf;JlFNO>?NK8?A_H~2vT1X)R`l7 zCbmXbf-Z!%>`mE#qBHh5hzpx_^_)=7VOGS;`{3QxKK>-{D@iJV!-*AE_K_joCs-{b zB@a8xz$RKGS3=`Mw3HfE9h_^${H_@1ykm9w>${Nkc)%a$_`}n@?Ku#%)$!VDSAcZa z3(#zowCViwaJSg>8V)N+@qot<7Kn)jvT|@F{N=Ma`3%w-#@}%ayE7A&FwZOBuckoz zR$H)i+{a^(a5@0sz`5U8u$v6}QVP99lFkz+2Y%i+7#deZO~RuS7tx;Kzn&gj`qxqa z@*xzFPORMndXOMERx{+)JIOsG7NdOq)+tXnNAkZPhJ~2lrpt8xet68a=7ArIk6GK~ z{c_t0$7D#Eu9&4Oy`X_O)dm$RsGmLaKCKSueRzm` z+`hw%R|k&QKuNg7j8EBNIL0$CX~)L)g>E)38o3mx>H2Yt=vqp_;iI2@!53gh7&gYu zFWI7nVUb>NcOP@c0rEFn?Q+vSW@N;Dge~kyphw!J7KU~{G!8e-bwA_3C}#^9itz5{ zDGRs@smtFsBk48{8@QzVvM~0iWAf;h_$;xNAMp2^ha3%Ua?^Xs9+3;bW(;XObWtHEECi`QbsilxH&9rjR)CHhod{0~LI@!wKBnu9?w1 zzZsbQ+OB(H<+)TUTT_}RW0W@l{^k)$U#@!l&O6efKJcTa!RR^X_Yv5;S929w1L`5b@0!}Y!tIpn8qiHon~cb*E`vdJ$_l38Mg2acW@pL znL4BTj@9;+)D3D%;M5K_cl{C=e~mffMa!0}Ffwt_tWpKrA%Ml`F<4TKVxt{?`pk@^ z`p>|iRq7;zWqCQtLAEGU{c6|+zu9;KR>@U`{Q6;;a$AK>75`fXVvC%H<6iGknUl}I zSWk`Wnw#z?F~L;NnI>t(t)cot*8Hs#Zwf8xtljP}5+9Qd3z2Z%eb4jxRrg zWr}wGZpNqwGvAwjkFINHNLA)OVmLCN@I{Ar80 z@6cyi6?x${h9>W)q93>jys>M&4~ydL+&vS)sSpor+0EZz*}F)qX;Np6zpdpc!)L^S zB-WEFD&<*r+}H`wWsPg*5C+mN+06^N>}2bg)E2hts5nWsJ>D@saP;*)A`sBEds@H@ z_3sf0v}fU&Pu)B`8Ri9J0J7~7F?1k;R9;za>>#esiLDQKr)ZYWdYWY@NE4+vB+QTP z^e>8~e^-_WVG(H?C9Y0gZMO9svB5KxS8x-Z;lfR91Kbhs4BWhT-#Nn;d&+ot!lS5& z4@Cp#2t~9UuM0;5+SoWonSB=X-JZZ;?*vt=*?ThFrpU-*6gcA6vWnI`d~v0%*9I0t z((=l!n6`6UU*PxZyq2KPEEM!;1rodJ{eaH>#uwiIJt0ua;XCZAG+@X z)efsR(3e2z`{8%-(Mm1u*OP2T4!TO4dT zQz5pY@y~j{=5_biFDU|fpnv}5Olr18M&(~w{abKG!=1X%t$Qnzsv|+++kyYlFw8eI zPUi%))t(zUEQ^t$e;!9Wohj{Qv2#aFuXFLl1A7|n{tkb&&6kCzL_d!oCz;qgTDO#vJZ-REPv5LV@rDr)H)0ZZiu}? zvGJVWuT!!bb>xZ;Y}R0{e&+Ew5lPNK0@NPqi)x$vzMcHW*=4s!Vp^|65-H6CTOYi) z@`#su$_xB-oV)LeV%@3=%ekY~4EUgweP?2jBV3oCc_v^}Ne|gyLi|QQL81XF1R;Y{ zgQ(Cp9yc+u{4PLQIvN-`C5%3e2?N-j>wYCUbalj{zkA%LEJ#tKg#B#my^CK4dcY(C zW!=Xq{nrd*Ee`O(RSh`;$@D;D4vz&EokaWxfmGoZB{Zj-*!&p$Tb zdmsJ>=HSx9y>>Hm;O$KhXYP;fS|`ALZ`=yYz`gaV5t8jBjfU{phG+hGrld6OY3)sG!YPe| zHDKn$&kujq^)Q5NUSRiqb8Y8iGGFaOKKa?kOV^C6l){IO`dm9BF?BKouP0_1B8%O- znq$3G+(&N*jAoy!Af%?q|6Euub-*PmuN9+L4}GVjrE_fZGdxXv6+8fHWXVU=B7+f9 zX$1(58>gQN))%OtSCwI`{U2RWe*E{Dq%I!Wt*Kk-Wjwq zmr_IJUDbCn$Z&s`0ewpH@Cy)yuE)TK5%ZKkhlk@QuQo)O09ur4zjbDX(1$ATx9g+R z&z^q`@H(v-RuE-bQoy$dUvYP{6W-YwB=tS-)jQcWI@=0)0opX4l`*g1~WACQ0U zdxC7l4eH;a_?oze_Y_Tz>4W151yA+px1Y28^WZkKYh*aBz*Q2_iT$r{T$M8IQ>wr} z(yO^T^eG{?RG_rQLJ^gwQgXrRlcVOXI)-l?^Xl#GA8oQq{w8LyPsiCZ*A~sKg*mdk z@B01O0q`igb9-4;#&fZD)ADGDx_#URSRU5^K|1rrCdx;S{+h9*wtTIyX2&0D;75h(az}}~Ch4>>cZCr6=f24paV_4K@ zM{;FX105);asHEO`6BnDl<)8pT{%jt2E=D5o?ZOU{KN(;JM~D4VLXf(j6q+34-XVQ zsJ;pO?JtajF(npUa5?=?iJj$PB}5u-N0JMCi{hNi-sSlC`h1~zMwUOHjC z&JW76#!|e*2-FOXVeE#yCiL;6zG`WKht0>nd0;Bxyj1WYcaijbSRC5-Qxb0Fc2EBj zfvv@Tn%Bg(6rjmmx|Z$psUsHKA|8|dZYK3UwR0XE$!}4z+DI<;BAK6BP-ISQ>^^== z>QlzW^#aa%M}B?2BvMkl&@`N~K{kURu4!iUF5e$_t+XV44={EgSQ>r|^GqibgbK&E z0L^gMo2hGihtdVNZc1_IGx`Sw%vdf8=``>`nfcS(LYhwBo55F*^3>)V{}C`~L;X0E zlDFJ(C-Zc`d-^wJfJ&CfTl=f)8Jq595T%d!Z1yVuea`uMok&u0i+Ws4^gP&F2-M8H zh{MYG7Y?cS65nKg2x@7xF@EW!z|1ub*lFdpIq(UsI6-ZnE>9`da%WwNk8TM*J^So$ zY%RNPdf3zT{!#cd;=5+zGCeRwsYQr^>ELuV>p9?E%tyIQ!I;-q)uZW8bDnk0n>OHdq9@dcA%N-Cc)Oz2ow*vz9-~a&n&fV8Z z_v;BjuBRci4$A+y?|y|KJzMHK?YhG{Ss`E9UdD!8%vKo*Eh;BaCAUHw_dCxxGk?pK zR~?uW-2S^OIu97-Ilji?SR@)eZ>$1yJ@|tQ)*p`69^4p&lVWFS0&k(?XA`JC#N)}s za^BJ|ri4)Bsz6_Y&T2pr0dQeBIMB=4w8|q;GUm zev5+qHA3oW8RF%%N(&0Co)_Rq?no3`t@DhN-UwD#$2!J7{cZ&5@Kqy-w>mz^S)6I&ar9IMK~vS>hzk1Kj5Jc{Xi%du)Yt`KFAzX zlh#C*9Qe4ztgTPLvj)jU7_RY3YCp}tz6;F+XVISwVKM~>yj=TxBDJ^A!aFpGkwxY8 z1_Zp)5JK`@h#HXL-n0Bork)3RvQf6zheZEf%BJ~+GsAt8Zzs|F+*D8HnOWa&n9t}P z??Bz`$aJQ2%Ulws;8(hfqTi+OE`|P6qIXG-SE|cR{TKGB0ge1kdT20Q&wv>0kH;1p z?x>H!EqXnW^ORS9tjHtoKEy(SQGXt_1CIdEEdV`*s@Q2o1-xIW7&fS4ecM-Jd}0zQ zy89~DsJ+y@Rm&`*HxK6qQS|H&*x{by?i;=pjz+hImM@vH$w)HVv}fsHa1zCTI#9D< zWMuBsC>+m@xgx0yO{!TWK;gl}9ob2(X|bf$_dr(x)^F@1$<-=#8SiC-s#XPgnNvEWXU@}M?|8MZ*`OcQ@uVVCy&<6aIrN$);*gCk`u_ow*7OOkZu zg(4VT`BwftbE7egKeU$Ja5|euv0HZFpoK9{qw#s@ozP1S1va0BVG&Y5JaHh* zNT=~*lrz4S*#x?;F62G*JMDmn?j?Tp#pD%9pv*0Ef7Gq!7yQ4%2?PV_R=Wgr-l-thDaez3D@jrll{RcD|uS zlAFWq;z$&`6>%j@O4KOXi(>A;JG;@p(4 zMlyDJtK`$=bkT;M$z{L*wifntasHz5m%?0 z#*6BFeDi}uz$HIW34Fvv#sT+c5ahb{dk)^=fp>i7@3>(g4@Gm}oaM=>T7VdM;5th? zgqZb3VAUAXU7`i#9}0|5N?dP`y7aQt^*tOjq^p?wTya58vp9Bk-~|r#y!e|tk{e|! z%?>L>Z}VeHT^PZCRrUfucCEoSj^1U;Yh5db%_dmNk<9Us*uHIWk%zy~@-!vzK}RL> zbcJN7i4zMGqOuHjhPoKX-d}@MGorxXZRbzVpp%xbxGvPw&O%?1u5ITHlO{(qmuF~85{u$r-7cNYqedQ$Gm<2U*PnL> z7}T0|G!sWmxj(M91xgXKqXSZYtdxAYPaa*uAcwj4^=jW^{T|UepZ~k-j!yHSNv*Yc zJhzQ+NU7(z+NH)uK^YQoIk-e!)_6x^9vKY~DtnW6AtVQ$kb3z$&}8at*=W$Q(nAMy z^O++u3r@)Sk@$w#e@vMXYSxo1DK5d!%oI-@i%qx4KXr=zFgOCi1x%;(PP@d3KP(1O zckSDx`e5_gTvH~$uD0qI)@E2FHSc$DbiQhm59O&W#$27D+{!!Otf!r!c;hmv+1w=G z5&L9eZ0xYw_5}5SLT9j z(*`V`uf8|=)L+cwF6YZ^sH@Z;$+XsNC8Da}@Kk8bmS0AUj->~wWB4#ItN-R`#2v?sIOl&+~baVNaz zkjlA^We&V2h(Do4IbVv#v%z|%pBeZIgfNjhulDp4x8@e$i*StI*aziUo=DGz&n$kgmKtMIZHhNZjs$ut z#CTIdshN_;f>#wVL22FX68=y&Q=L!aF0||JD7wmMw-E)d-3061I6N98=zDp6&`ZR4 zrNR1tON?0g${$C5_1+G%jnGm7j*%5O6@=!5uaf&EBgEOUbaW`>2E5uel9_NP+?f?c zch*>$MS1QNdQ%PKv{`FeznCb)uPy6wK48!b*l~&xwaS8pv8n_s^{J>h4k$^yIgPu` zuwu4nU0PjRYq|mROd%Z| zBLf91^{JPw-RdU&pPYlWH$Ta$@D7xJf217z1gqT-&zoGzP)wO5I{u0~>LKAuFfZUf)W(U;Df$d;)C=cZ&|4$CAm z>tAu?E2YGr@;**xTM2Ih4gFbd~J?_vsjz3SM8$zA{OIC3_Z+B*Y zK=aiSIb^*sVw_l~d^)DKfvv^oe9n=`4OEZcQ^aQH53Dhi-)9c6)&jl%JK)7L^Yb*N zdOgz=d8Ta3!^aZrSY~tmaN zw!Yr^)XvZkI0{eIct`Mcv)D26`qT`y>A&=l828kF+H+{F87B* zBa%gI=jtW8)->ZS(DV35t?5E2Mt6)v)x631NC9ljx%VMu(67=&m+~6yzJV6j^PP&I zhnERAtC?i;B~fkhA)BXhrWiSKHJ{36Nq<7 zms;4I^`IPv>?SMui=_hV9Gg_KuNEM$RLv}`{F?lXeBa+G0KoLvbn=PmarZ5+UHai$ zi?C2t5(zl|vByoIXQ@*gB0R=sSyWqWmBT;7V+foel$4b%4*#p1UIH?&=+U zgpH_}Hp$UaRhPZ)H|V{nUS7I;0bFpn+MK*t_|F3ajTyx2RsWV!@w1!y;zBky)4_(t~2)Ri3J}npWu;9JT{RbjQbX$Wk7y;8x+zp3gCDb{%lacG zW9{|0^Y->uD?#3cSFCJ{p06R9AwR5#KpNL2!hkep=i7oMjDV)_-0hpd`v1A_0RMlz zRo6QXf2KwF@^oH5Ic;e{H8CC*7rdnPY;=pH!Ab~n2vj|2Wj8ZeE@)!F?*#VgO4nHh zIU;Vr-dVHSi9QLy7Bo=Y@N(tCH&(idHl(mv>qKBt0E!=*EL@CSr57N|Wuc}E)W6oo z6JwUNJMQDY`zqFvl$(fwPACs}8{a5b@S+GuTU&~Jf6mzoLBjWL+zbNJ3+m2g+;c;zw+jsXuY44Lu=-16i(Oa`ulS-GjVAh4)J`L`=#8cKGEc5Uk_sD}@anveLcP z+Br3R?L|hUn&B5R;0?Q_m@HL@kXXX{W`$quVHj2+@0eeXh0g%iZAh+iB>HCG5k`;dMzrsNis zI$IM`b$fOh<#f@84)+1Hge$1M|GVJp!__xE=qyD)Y4^_wr7BGrltJc{?;1~sywmpC zoct-g5oY=WKO!oJS+^P##qs5X?MQpeZdZyTF53W%gczf*zDO85t_BBr7TNoO1#^B6 zL^WP?ZKtF7-|v?m8pngm)o1Y%&d=XDTg}jYB)+mfq3k;N9#1I-gz#M*WmVd|A$>W( zK$lyHXiokwh4N*pcfLx4;)@OoC$||s($~uWqF4ug&y*le# z>PCRUClQ>n()o1Gz+_w}CW>ZxwQ46>qh~+1@maVhz->DQN9!^1-p z?;pGu2{L`Qtf4=Y)lzOfPJAV#5o2c2a;Ss@CQAm*=YQIrGQz(0#P)l@ziQOL`z!c! z@$@1Y<*dr-jjYXHc@*#@!|xW%f}ua$I7n~8^zgzg>{WpI-(a{)rgs$4tlP+2Sg+x>Nu-8rF8I-IT?tIvr2P*DyQd+2lfXsgD|)i!j~pyVW;`*hIH zUG}*KSRm~Sa}QkxwdVF3lKv;t5R%xaqRjivd7$_58g~uJzW9xZp5kZ%7QpWKAyYgl zI9@J><{#O|@i6C%$G{yig#6}09hc9*5%(9gp@p?aQPd{o-)rC^j?okHA66s2-rQl- z?#}tc-9zljc*c~tMHYWNh6@e!kw@9lTCNU2G+v@(^M98c#h;n{Jb_qkXF~X&^qO|M zng{;dG1%TanBR5)-8G#x-N6`_@0EM+QW&A9p;n7WDOIFC_Im+!2T23Rt@Cc`0R#iM z?X&zx;-$Q{@-ix)P!*OR^^qLlbN^E)0HJKGyk%TgUIu5L7dVk7xGjJ#KZCy3>Q z>0@Daw~1izi_R5@g`!ZBnhl%$Mwj5TJLXf82>J^L>Ciz;H;{k!XE*8>*y3;c?2XoO zvFjVd_kC5yRT=3$jc~aN9p?YXEd>52hdbz?{wqIyn!eA4O-jq*(PF!&;A->g=57JL zb<1EGuoShuDd>_Nu0y>a?e|Q9w6RiGGt|=7?*o5{MrQRcAx=RV0SnTh!Dj%on%2E90 z;HlDw=o+D8{k#lg4odni;8{rSnh@oG^5>NTGj~>be5gT*f3daBi`&{=UX$}%kw8C| z(R2>Wyx26^q-4W(!hDhb0mw?_mq>*AK;Oz<> z5|Pbvq(`wP8t93B1srdp3k9YI9dC_Hag~DJGZ+3+(~tQQ#0lxdR-#4t=s%Z@n~Aui z{#t3kmKu~SS+s4trc$61%V6PVCdok&(yvsX^_=8 z)qqs^)KQWT-yRlcm+uq3#zUFxrYiSCmUg42$A<-Ux&c32Apr9fM`rKTEv{{RSI9D~7ANasQXByH^BOk{QU=s#{TepqzlSsp zU(al}0`j-VU$nlD45s2dO~&Gi#q=&QD{jgktWmZzNtk84fLWcsTy#uT)xuBTgoPpK z#4DzF6@-%hfVsunzLU|!JY$QZ_p1jCT~!FaQ9Ot+StAIh zlj@LR^};^rCUT)1>EJ@gUxD|xul`YvkpCzD4cOcFDX`K^k+%37Zmr8}%o$@QF#bu3 z_8$Hp9z;_cY{Gx`OL%_gXUq86B!J>b8te7Hba&Xenq11N9xX0-fRFnv-TmMPYV#iM zo~%kNo>mWW{(yWb{`2th87;M3wZH0zn@%l!V+FX+g;nhM7&sphW#>%7HEM%BMi*H} z0A4+-#lx(}UD&?S%|GyJQWsbYgU{Uut5sx=%N>CXmUKHRo|{w7D13;2HUcv_Y(8iEJksi~q%A=(a~YzwqMFLx?n}?gjq^U94zAO8`l} z*n-vmz?E3o2rxXm*{xI5IvZqDsHk86weOdOy0`EG9pi0xrd8UpkGsq#&w4iYV7U}P z8u4ql&XS#>&LsRR))+2q<@7F|&s!O9dB^p-7i93UAYD5`!`xSc+~3e;Snb(Hzv`o_>1A%o%SRY0 z2xpdisjmj7U*SAxe6zgC29-8OfYdbFd#UPj2z!j!~fOuf)}I;W?~duxUUqZ|o3Tf5q`L?u{fd)seO zBKE$Ch(caOp!aUi8pCkC^3Phnw?fp|Qd;*K2Ic4UukC1eXP}r` zG$Q(&i|YZTn;J19A$2;tQYW%-myXw)AN0PX#kgnb&Mc-eK)lV<#-qN3NAS^Yzm&u@ z46+28Br#`(6BJ}J9GkjAfc$A3`)tKx6}a6k={oyGYH*FdIDWPdo z<>!NsBhn!}tC9Yn`W?UJ`}7*NaiXc6Mceu;ZHV~S4?Pee@GI$mht1v zUe{EK?Eq{BD|;j#iHcQszY(UIV9ES={|CH!n?&R`;t)tNon$)>hlcyB#AA}H-!3*h zbYy+{=`nJRK1aWZ!IXlL+p!$`UE%}|QYSs+n_9l0=#fpOwdlP{K4#up*6IiUi$Mbt zKeTRBu;gSwg(lC+%FI1~GX=oE{h$tR-BfvvxkR$I|68kvam^RsqPWF6-_uKa31W2r z#(^TRELcmSv9yN8>q=+}54q{Mv#p`^PyKg!)XjHQ)^+y0Vi+_aEq6X!A^PVSA7t}+ zy=SgQL(KEWKySuv5ExaR3j%-3JL&?K0pv-Nq`yF07@~2BQko<=sm6eGqr>~L?e4OE zD3RVAFDfuJ0gsVc=LmCy^|IoCx4yV$iyBRZqmsa)A=FeDN*hH6EvmMCzM^6UgH?J- zIKbFCex16+7hgCnvm?ZQN-7rnV2>Cw5G)(e#Dy#r>%(%WN}uHm|yJlk^6R&G2NqnAvV zVxF1L?#pK=xoy}SqJ8GAJPzAo+R9_>tA6{798X|M;LI5SKTiC9LQ#G(GIYD2zL{w} z8}41H3wVH=boD*PJ&J$f2R^yBd-Hdbph7xo!ZkmoAKzzKwQzzEG2I$i`6|0^DmVM> zdsnR|iFgfm^Oxfo$!zHU*Q=N}f53hn2k)#EgpScNVZ-KWP(XA`7a zV>Sf=y==h+z?oeK;I7qs{x@NJq$c4Cy6@-I-St8Nd{Q#>NlQ z|4#3=0%_}N6c+7uF7Q{k0vWE4krP|%(lxLK;5iY4<3sOn@2s(3=K3k}??QSsz|vze z8n_KOzefG6K=>?wXJ=RcoLie9K7Sw~#a5@V;@ENLw)WaX77n4}|J_Z>$JC=~=$=iA z|E`|X>dz%T_pI2=;x%;TXU|$xVjRl07Towi@AE0{4lgzxx=5w`VPJn|Mi5KGK%gRC z9(YZqs=ODV9s%9*E-)TZ_}tI7+M7mvD!P&3i6T`&th8Qb*>w40G`I|@5SHAh)IRf2Atl;I3>)M*(=qnXGAyBTB{#@ zHnE^a<6##orDP}lU+mP$^rnR?a=Qi5fIYA%q%)Vh9BQkxm->>n6Q1I%O=Mg9weYME zOFPO$4#0^|_W!I9Q-kGuHmtP9^r+)0JcMn!y@?jZWmw#wJSupYd9hddaNUp5!st|X zIP&)Sec#|`c9F31gd!T1PvLgl)%RO@?USxrjU5e?XD^UsyjOTv0)@|VFmnp^fDrHt zdlbgQ_F7S0*g74|eURl-AD$#$+YyMUlYN!(B2v4JS^x0;KG)Lr6{K0M)Jpb_B&fu# z*;^M|j41rV=@a|fiAbUV-@BIW9ER!x^r(g4egEZu!VgQk3JPFMW-%lSUczW{OP~0ck%LbelWj-`*a`QUt+6rc(MC4(xN=_#5b7qZ3n@ zg1<9EChI>-!LJV!?=V-y<0}WmfS^^r)+W5{C?|%?ehV&HK@P`hHN|;+uw}B64||Aa zl*?~Usq7(+t{wlgfylOPC`av>u$Ea$PTMi`^fc-}yRxKBN42Ue0Z(~$U?yz+%Th5t zl=aQ{qhBJ~4ta(SH0bSXT_4ILRBQ?NFoBsZzl0O@8%i~aAJ7<*ieoao8=`dIIy$dG#V+Z~r75DW14J8M9~ELF`O15TYWoPxHR#Ucxhpbu zR`L+aI6X}yA^$xWr`mlc1$>PXzIp;nrix-+a&T>e;Ld}AL=CFq%y|M-{Ok7aud}F} zWY=Gn42i)Ye4eE4n)eV}Z^*jscL~?{H#dv9`hsEVrZhL|J!>dE_U2r8#Q7Yy%}u2u zHVkL)@SH>=+~glj(?UB%k_heUzgsO9`LI9j;VU!KL;FX3WqJJOz`G@qUbUxQ%}v?; zex3yJlIEJX{?}*EpH|()c@SYonJFH6Mi~_ctAr?FmrMBDi#o#d${qVCM(s+E9&ymw zFN^d<5!&Q{-B5_6n@zfTugX5td4pv&q*j8X5kvUn)3ivk>g;uc6`x~qc%MUa4jn!o zd2EyDP?S@DwNCIez`M{bA@E2+gy+GdS9@ z?7I`g-vZBQ@-M=K!t|41q0de#es>n!`?|Mb`wNA-Yv;o? zwy}LM>>31gG9gxdEKu8#@Ese_H3V|@)9WUjAC)WVgZ!}ia3Xbcd;nVJ2v68+r|=MC zTJ*eQ{GshXpg)Aak<1;G`6UfkZ}Vj=B;x)KgE2v?^`Bt5F!Q_2kvR4d>T{Y|;Scsq zKP*Q*%4?92xkFvq-c%%N|0W-;*TcPP>(^Zb9SrzZ>$m(qPkc@32bxx(5}WwEFe$xa zFw=V>eV@}OK(hDZ;fdvp834?FS97n06M$C!A*^fflbU~jw!(8!@ZZJKcyC|F-}qOZ zJy?VJ`H_IU%m0!HU4KZkkJhyArYI#E5*%zCx7;3FE~RiLwL&9-rTufa`UF5AfX;CX z@$_+bpUzV*vr$Cen-8-#Ix)ChS2G7OQNYf^L^fdH*Z#JVbeWmnJFM+uShiYW|DZIQ zrXK1QH@SvJdDGdy1kWX`*_b3gcePdC7)7v?&+X)reaCh65rUF7&U){ue?7o33z|!Q z@qyQq6aBTiK?{n!(!XJ)APwl3mrjUsdm)wQJZ|AGlm&k08UeC@Lz*l*CBS7dVEfg7 zhaN%4CL3q2y+5aaMBAr18LC}|hl}gB@$TFqI2n!mU6?P)#@k=^V(cdZ3}yM_VI%{* zt{bJzvy!`7A63il=l0%jS*lPq=Y2U@+1pNu_%6)zzYZ3(kNuI}Q`Ovx+W|?!TbW0}jOu{Wb5&?gk9?~%Zdlr7IG=o`n zTeS->Lkul_%vGk*ne$ZSSJSZie=;$eFJ|BK!WR{S zh!;W+KPto>0`_GTa=CfMe4*hb{?*DC6C06UQW3LI-dIU&oTmY`yBwQuslyfG#F<;Z z$?D9?+C5@ajrr+kC1Ie0VMG**tQ-$`Kn5uM#T$*Hw_{)aBu^>h?C)vv)Z6`T+UHyO zqb^-I1TrIIhC#epgTcCuEf1z0O^trzUCVbw zr0W6-$t_l-FlN4}$Z+OU(N2<-_n}54$BJbazu`xsZ#_9!D=%<}rW4jEU=+`$Dx5Mh zIBj~T%o51o6QDAd*=#GSOSa6QXnroMd#Pu?3x?!EdENrCXd>RFQGK{y)9kf$wN%Xa zVm=3^vUR|fQ|nL4OGA$_mbv)71**^gTjvp1iVE1x56{=!t0iSOE(~b}ry_?E3Jbmr za;R>UlTt=8B5`RtXV@ArjkX!PG0Iw5oBVFfkGct{^CQnqTI`XM2JX> z9ffZYg?dCSk>Qu-Qp*gF-s+B5gboRgP7ia(*2I9)$Z*B_g}RgIE_ti$f8{FPpP16_ zg7|-pW=6?9u0Se|+{sjBjDj;id9C)F#f-8}QGaA}uJLVM%pHKmH@`&_j>$XSp#3iq zlz)}K^PPBpa4PC?%6I75^lL3CKQn0`xID!5k=19T(k4G~zV|UAm1GOG%8xpOwP`&` z01%*xOgn8t+}+N7hELgio*``)G5MLtu`vltA>utyed9QrYjflMK-9%vblvPQ&x1_} z4APptnK1yYpEAd_NB2G8M}`;Y#-BPbtUvy7 zipGAaBrDk1tysG4I0^H4`$8>%{z`6H6jG&Xp@h$7 zoA}3U@*(EV%l98veI9usM|q;zD=Nz}li=Id;cw$ZeBNvBvu!Wyrm4ynmwbxeC%!VH zI6m|qu-kL^G~pR$(3CU9_}a}`V4bWjt6EnAhOO%jF*&iG&i;}hqpQ;Qu>a%05Ac^t z?IJ!7W7sXb0%P!tU#cfSzt^v1OiLYFDKst~fqa-#CQ!~iks7NnHag7U#NZ#%cd5uj z-(9@Ue6RE!W>OzMwxmOGh8T^`znN8Qr5ztaKhU2`nf~tplHg3or9^GbmlNc~ACE0z zlY+K&WP2$u=)4^Z5Pn)M2{`G|!kDw8{Gm4NlRP8cl~!&uN4@N*F}pZ3+VAu$BHjCa zX7u@&tYNl-(LA}hV=M8OQ=eTXY+pz=67D)=nKzWY6tIQp3bFl`P|Z9u;!e@r5zBtD zK=mbW;ETuSEt_JH>~7$l-Gu>krD}*j8Z8#r$Zk;WMFzpTGXSN+p!xZvnJ{gwW_kY% zaBPZAzkoHujq*V1=>jT0M_ULp*0^Mob}jb7y^1OGVe(kr^@9%N)&6no-1jfqJZ6L0 zM-5vBL2I-K7heo7_5~Nc=8tLwCr5U;#q~qgVXMN}V3G>73 z_3V4!_jOq+wEQ#=7`+!D-Wb%9bU_hh9r_pYE5wX-@e!j6!hvbeGj0bTh~3?~WL zk;^MR{afkpfjO9CwC&h6y+IM$~TT6`YoOGdT$r6Pd36)p&-Ik-2U$jR&RqJ%n*Z8=#TG^p*0sd z)Rn@aZ@R{aPjBsbfHyt(M&*$RS=d=z?EEUu3{-9f^oLcIFTZBS>o-I98UE%1Jx~8} z9W7FItrNA|@u4ENcd$nepwE51Wc=wk-nj0eSgN3_)^EE#E=Onp97soF8<{QBMIC{Fkjs;k70W~-*j1xqEhjub5 zgkB7FAL4u`VLhu@AUz!OS;jwcvCh}uWuXTNYgf3;KQ;bXelh(h<(_~>2FvT5a+h^N z)7q0OBi4O+nk!;`%-gRdbh9A4DZR`$Y#K;a$X_CiS44EHWWVO9qDLv zSCa+*Y- zcc{Y05(0D|iRBXeV^*h*qjtQ%1V`TYoM^Y*q%XDJzi<1O;UNMwRmB^|=wlSO|DFGM zFW~TU(;)B2pu8BF6%3<2+$M86XNT{q)7yntK00z9`i{8HP36&Y)Ki|T6y!rv_;rBu zu^&f3dr`Sj$_rq2b^K+HsAAa6Zsd9P?)HgPED`5rk+Ok)-TeAqw}aX~4eOOKRV_$I zO0ba1%~xulSpoP)s5YvfJ=ff2YHP9Bb%sIfUoL`$|I!fJqa$Y-$OwI{6&!kg6O%U)PkY6aEIUB8g#*8HJ7CmAl*ATDdTNCkymn`g!5> zc0)~f3af}%V&YiNi)1pXTX>+;;l7%XSAK)eCg}TSAC!#+RS5838@qHMDSIm-^%veq(n~!F=9B~SypoR-lFi=S3NCRsZa`8km zL%j=uIAKYLE+D9B4mT`7ZpFV%T*%sO`jDRPSNgy)avkezdpJHwl|gBu2nP^jKY8o& zTWj3e>m#V1_KbWOQD^uHyNkKsMzMJQAzYGp80~Kt2N;}1PG$dI8UTC8ajQRDi|_JW zsGZ{?5#Wn-L{RJw1Lg7&Ot09rLcU#BBJ@b1J!1PwShZqHm5C%ap#`FK7=cblz7$8Q z#o|T1N-r@c9Ccv<={)*ty!l;HnwXAvn6dY0je=U5_Un8JiStSQ@bi>v53id7yo_Ko zuKFfB8?#xj#FyA4!1>!M%ZZ)(FvxShD7vG%>>mQsF6bbs6F|3V;EtH;(GLmt%EI98dd@I!=$UNPuvO zp}&gPpVDIqq|UFi3Y^-&Uv3kx8m8NmvfQDZ{TBO(MpV3dDDYPmHs|=ZiTQrVzt&wk zhNFevq)AE0ty@ul4p(N6P#^5UHX000d>uiT_AKKoHr}X!hqy5j7$-903) zPg!y_VL`UgV2~J#wEO6+WFD|G@j^ z@_aioUkvt3F=$iYIV#A%c=VgPetHalBzvK6(!tPR>h<69p%KgyW`iatT6{XuuY8VwP_-~gO6{eA&@TY5^2A4Klf>@J!Q>T>i|BU3Y!bY8y&4!G zYBC}O8AQEPKuz7pJ zp5UiyFhzvLA}l7i+PB`^yHqg`6ZNlt6<{uB2h@wI2wTcs`kzjspMxv>R5F|HLS9^1 z?Nq=OmXU*f>)DFl`9?iJQ-8AJ4lIlW)`;ZIdC(>}T`ZwLG874oEfW~C2AVm~lU|m7 z?0S17De%QGjPmHDTAxt_-FX4x>7N)BS9TyW?X|N|a8Oo*bFvh7`G@VW9Y1|YUQ15q zW1rYVj*y2U1YFdHl{x}nNZ0fGRaGmnmGS7fdzR_}XNa>Qv}bQ074M#*2hyY?WvvkP zZocjN=cU-H8uzgJRXjdQBHjFF82r)zEtuV^Wof*V%OuSbJ0e-UZ^&mCtKo|& ztGjt35b*^LmR|-0ox-*3ex?ZtwdvVgeh>Uz!I*P5ijRh1+YQ%8=-nqUc+zSti0KAD zffa~JkHV)L(ltLOAjkzAujshWc!}{nX1<2B)1wkC1xe!QB#{V*?##&;BvmAn5gIK% zWO7t6lsg>@AyC!H^?#|fLABZA!eR2Y3X!Fn@kR2@Y?Li9$Ut$URH$cFs+j?fo~5DQFb0gaOi9c8eb zB?V7D+4%$o{R;i5cH^2)1vkq~vrZu{q>(o3?6GZSQALtePyIEs+3_{DqqhUBiY*7T z>VKJ?;h8LH_hB^FZ3<0PtF*8Uf$u1*7&&<+84Y^iY7VTmmIaJ6s_o*HWF0CQ@1$% z+?&PLeuS#*cL=rwStuJ<`MjE(bewjZvg>lCh%nQi6HxclOV&4VjNFsfb{d zs7*2a08>~e<8Du2QtiT|>}Msa_s@&L()eb@Pf|m)V@_;sSf04;>OzuWWO^8{^>=IT z*n*}WJVR0d%Fw+XkHLTL5~~#kvcF4XS@}|A4noSpUU+F$=uMmrB}KX%AL`zoZj*am z{yZtvwfjpJ`9_V9ldS`uow>8JYj-d${4FSf|XTs~2u#MPHSJNw_w>L!Q!kj4qkPeE0A9z#=Wfo6D_8Gjpn>)vTH zv)=weHH@1>?*7-SG2L2jJ*B4cw^LE-#Pl%30rXH)#d6X=*Hr9oQc@NhH@^5*1#ix zn%`SW3x1-D#tA*H%eL&@Ep4&swq?(gZX&B=gtmj7$g6|%KMLo)9RGp;o;N7vV{xb^ zQ?E?g+}QA)_+4pc!-S^D>OPu1WQ$KFD2#PyhL$I6*2Lij`;IBS7@Vy#9SgGBZ!_vQ zkC!*{RZe-PGLo-5Be5@7AARRuIDo)Fjf$c3)`B#t;OpAC;O>^o zYS*j*ox62h7$@2>7>JK^dtjs7nb#WcAZ-AAjaEb&IV#%k!&)v6Bg=R%6~asXya7b`Q* z;KL5jLOYhrPqv4(sOm^w!x}y^|`@H zCD^eAAR#j|Dyk=5`Q?_kh%BNYpUhw>Q`EUooI zeS6bcqpx4vGfQ$f9j)QYqm1IN9yy4nD^vI-24Sn<%NZ!h0O2dUqeZr)PPbHNKQ!s4 zZfhc<%1{wQT>`;nRA|SVQ&luzc|oQuUgNYV(J9>gprnLRYm;m~{|Mt0r;oB1)E=^$ zHAc$_O#Wfv^5`;GUV^9>yk6m^$tdZNq?ys=p6~h7CCCn*_#<*5tEgt5{7-1o9s)CUL7XGPL%1*C?*UwIgXz;c= z=g&Vf3~Gs&HHgU<;Ko`j{?xyT*zwgs)!wk+{`wx9G9fcSjxN4Bc@INFE;M7Spv^eH zVj_^6NNAAc5TVaA&9JliA9QUF?9#&H=ar_Ab=Ao13E0LlmiZ>MdVf^^-68Z^U!G2_ z*udAZn)Bb3Zcl}@c$)0@w{1LHVwk!={Q;x1*!(B3F2P_3(n{!^I0ADBZJLS?Z8;0? zT<>D_jPMYW)~SSdVq&da!6;)1xz)cCZ^N2*>u^sn#i5fFA(tT@UC;-0Bm2FEfRy(R zTMoV7$bhwt{NnbzW5PuGF!J(dAh^AOu8QCzXx1OZ?@D1_zu-yVUGyipiv!gZoG9tq zjGPz3!xirGW0ey7n9Hb+A$ah+Bb(dKdAGx{6Ct{Hqi|V^{2ym zp-zY6cywA;H46C#u^wRF)wEMhXckIwM5v|NiQH+J$DpFy<4G)LFm%RZVG^`KK8Mf( zR_bM%q)2`WuY?Imd+@kqq2ASo^jYNgpHiMP7BRc{HhUl8ea|#GvBJwB8DA}TIkg6| zg@J2;8(U)voyRae!G8P~vYuF)%MKy_{V!Vli-7P)w0iY09^0}hil!b`h0Dlq)?=uW zGQk|$^DZph!ws?5Un@QinxxBodo3c)N#LXGjahqW^l10b#6-;ZT`d>p?XbVtW_x0O z*&pKUI7K!85V4}-WI{u4!xi77jMk{kp4MS)fTNCIIYvdz@d&tCc zqsrNUbj<1xBDrX zi}#4&^M1G=*pq6m<47YsgMj(g!GHpOew+8>db8K6qTo=6Ida)y=LrTl-tnBlp2UE@ z+}`AoHX;Q2-W0Ji;UST5CA%wYx%4|hFY!4$*W&Yl5^7o5mm`q$Ji)^#WtfnCh|uVv zrKLdyNz7|2;&Equ(1)@WEatuQ+pp`!aG#Hl1M#TNSctvj5rs}mwXhk?pk9AOAxh`P zIh8N@Yr&WfQOK|M zM1ut_6E^=SBDOJ1sip^?52Q&wWpkbJ61P6H%PI; zNkN0wdBUx>H_EnF2g|XxjxS@WZz);jYTH_c3 zhSSMz4z;+}>gUJ}OtT0~WATdQ6HjpXde%q?a_9k;@5gXWC)`Tl%m};A@;U0jrsR$@ z*k@j*7xSVFSH-J+g30uqz)~0{3kv|=${(WEsCm+>|qx$_f9~ zLQ-lTnsBPgKYXE9Y{k{U9`Ow-3s6}THY6X7PbdVU6ua&3DyH}8eK5f#?@1uVW<($L zS|4mZMT!5X73B`tLS8MrExR*{v>cK#c`JpUn)BXsg6at0ob3q3__B?D%V^n{%8UPv zKf)?&_2&>s@G@~w+J`W{XBJa=VVi$BZT8*QGvg83Ut!6s0f{bkOm0!y163;9N-yiv zaMg=ArbJB0Lu>;idz)v3FgrQ>Zj7~Z>iOT>N(J+PJdY6XS*Ay@p*qfc5R=C^{$IJ~gft%g7qU~ z^^S1(g&9sJIa?}k731EBH)4|mKQsN(Lt0y6PG_5n9UG)RTP9be#u7$gt~0b(H!hQk zl14686)B1KAe?uV?Rh*gvb-(tn`&jopW03ATMpnzj&oS*#?4sAmHEy!P}pR^E= z>%eSe=KjqO6s=`Srg3lVDD5L5F3q%D{R3Fi)f za@6P5W3p%znRB;d%k^}d>9;WS|EpFm0cehT84BYEuO^`% z>B@DR=hGT!1caZ8)0-2zh5m9UUL1V!IGqRauyU>ZjRo^{rBE64w9q?XA!)D5?M60~ zECrO?v`b|Z_WYwH_!g=JHbNixp1PWStKT2-?NbehDQ=L!n4N?BaF-$JJ$Bcf2|)2+ z6HkU<_p6r(70V2zz39nos{b{ z=ywe;!f;&)x%jb|3Gz?@Naz4TVXeJ+(Pn%R|F zQ`Lp&ri*(~f6(7`ZvprEPeA#_=UKgNCu-uw!{_68<1;B0)1CFGt(DYPK)J=|IrCvw z?55E7B${$ydNwnTqvce{>$WlF`nh!m*5larTsH9K0R1dIc>lr1-5s61rvj|G<_`GWc4;$MoxQwCd-8sE&0Pc|oG-bSbJMs68< zaJVfh??q=oxJ&$iH^whpTpjGWiA;+LFU3!oqO`o)A0pKI>U(MpzegUNHmF(*?C;tm z`#;2;ztO6+4mK)p(Ynn7qno*;W>q|XKwqzv-Fn*yk6ghFl z-4K0|oT0(*&@z*z;=xz_h(63%m^3UGkr$lq%HrT$h`~jaS?Oxt>d;A&E2S3~#xVWM z42=ogjq(12n&85|U=aDwhWe>OcY6HsGYou+QN(+dzS_7Tjj4C@J7hn!n#&HY`G^s~ zT!FxGS*PF*ey#_DeAK03!SB#fi&QJYj zLRcz)ROi2sFbY?%GkBj@Nc}lio?P?ifT_=`UKPnMNrW@5ncfPbXLVqEh?a=srR4kCZuFwE|AYAU+H# zpX&Y8dh!h-#jcZvH03$a7QYFjANy6!)3#7uRr@ZN~r zsHU^jyV)}c8*6JO6#GbMN@PM}3xUJbVNX`1&YPl4BH@j5oOv?)!zsE$TZklKtt*Az z6YK4QO$iAe#8T2x2j`JuO;O^%6%FmT$#Aw~Lh+!xS^isRB$b;hI2(+p?#QcE?G zh?QlL@Fm22FMwWDuw#4GtP(hfB_1VM zRAQ<{V0Q5kOL4@49n?j++_dOm)b6f&+~!(wamhVK8*QEsDIaC{_YE{g7BGAN!|S@L zJ)w;mOfZe6WS9*w$jg!n!fyu%P1nAMdCuPNw`n*Y9JfMTPBt@JuEq||mq6qH_32fi z7nhOe>*^c>gTZFED?i|Ha4_8IQ;{K(@9=L{My$5b^jb09{ia<$Ir3hY*G74EWSH$k zY92jT3r^DC7)z!<{;)fOY5P79jvFNM$XIQ?uozVLUTWD;2UqNaKYErKl27!f=`D<0 zb=&XWW>7&HMm|UgM~cq(etfU8nG3SwG(9Uj;h!zX@wu0DA^$Z3_h=NDqq$x55JCse z(&xM=`Jn_TO&mPUPeT7o+*bE3W5}a9Z$PodWZicU`UWneoFzPt9jrMMc=G=QG1Dso z{Ll}JI^De2mBb1G!DE~JdeA@seKcoxZ){CfYycl>)!pMIEvL|U+qt1|<~H1CRTMsK zQri=w;?47Me>!Wup3(O!w-rviK`5;EU+OyWC01aJ62C8UTSIwW0OhM6>@9LZvF4A< zZ?6YZg7hu)Ef9*7rxfRQBpx0*1-6Kp)#=Qy*DECODN{n^@=-H8Ja1 z@X?o6>628UNetvehsmCK0YMBd_#)djYxJ*^hy#Naf60yk`A&vQT!jgRukC^8BsAu< zjucE{4(LS3H}u>hUwFt({Y1__WfY=-XQ%YQ!z(QH7?rrM)9DznuK5{9Wjb1_4h1et zR7ac3gi3l2Sin38ho?Ho};<|o~{$d&yz ztcRbT+(?Ki1Z_r9e2A>Uk}6!pG^2Bz(B&ba>vN#e+e#B0A3C{4Nyq|9Qt9Np>NZcw z@R5j`FRjSJ*rfymllH=R^7D`DH+cDys zkqCBq7*ZwiO6aw6q`MNH3L1D8xhbUSGQi`~zejKoEGZ@Oh9!?1q=!YX6#w{^{WYph z^Vkwp>@639vH*NQ^4J4>VE))Vo}VdK_3@jN@qzneQK1#|>MY;&G?DwuFO5FePfsg; zYc7TM9)-`S4sjyJfKQ>Zr3!=1YfMP8x7P;K?zFQ+1a-aozH|M2YQ|I?ezPr_Uoc$) zqj`kAB+jR+(rF%ioVA0?Y_nO-1n^YDC1w4dpuL>>&)Ur&anFFLSh}ve2FZbCVSX872Q z(+i5QxuvsxsRTYxV-An4T(s9T$Y?n+e^q>PTv1OHk)*;Ep})b~|t0kq;$)RyC_hu^FxD zjJAd*3R~W*v?)egxj9G%>3(?iQZvMlP&&)_HteIWWD0a&&9BlbQSlCbw9+Zc!v(ss z<+o--uVco@;-9IjSQ>6TOoO3Gxffo6q;neCJAA{d;U~t1Z{FiW`RMO)^b$5+0!fP4 zv4eCGLi9~Ri2Sq>0;Kf7orei3ifdt-kKA&S0bU-Zi3nD^7borD8F*EHgs~6p)mL~A zxyKo20eTI+@vnMQGHrpBjJ8M$>p#?2z<34(EU?mw=i;_E^ugwDh!VjzY|FtJnHlgO9I(aSK|F*;Z_i%l8p#}f z$HZPc!Hee^lr4k{T$^f3C5NnQs<{gy&6`_*4#7%;*MXs}**xOXt+%^go9iO?bn04- z(`&l1&@a%#7Cs+7RIE9wQXMJGXScNxG1@&5J&x#@wmS)#lSOWuB~A5f>QCo? zOYvKtCw9K;1(wa%fy-k z!ajY3(t@vf4rG!_e1!e)7noAsP~!Ym<(cd*B2JVnMl+?^X!+C?`^ER(w9J;U{I@(V z<;o+%xBOww!w)ro9Q2mPF0ZRNMfztheSav#Mca-@C^pxgK!X_jHvXtvo0xqq`7~k` z(5-VQ*H(?MT$P}mXrXA6(R^>HBy$Dt2FAf+9?bp?D31~pY0L0Q*VgF&d$SGIR`T{e zhdU)v9Wk}fX1l&=vU_x2fSW!jdxe>htWmbdKE__#@p%o6$C46S64}n(rv3|Ya{K85 z`(eL6U&iu~*g0T^|EWKB#*iV9i*E!Its?v1^_Pqu%pE4-5cQq}!3cn^ay*i5Nl?*q zUEnxp{ax1D9IvP6DTeZDI^pS0?60PD&ML{VT&p4EO)%w*!rg&&ADl~bA3fiG`u(Z- zWh8+N^N7K`K~H{OaGK{A-t6H{c{J((nUlD2|5wb+E902;iNs}M6(rWXtJu69q|%aV zLE&ddV;<``4g4;f%i;h9sTo=1M1G=ksN9f7gu{jpN#IxLOui$MjN@rOxd;<00uukx zva_7@jxAntYoe|YYL?|c3h;EP;e@QLb;tJ;84i@{$>v=MjKB{e|D-w)7xG0UIQUhx z&dOtb(v9Q9pEdi=R~%n7-o~6tp*I!KFd^$;M&kYPL2ccXTvY&|eOIB_xVme9Pa($U z%~YDw#8#|NG`4z)_6gXzn3jQKYGN`7v8(L++>RBV%5#|@qyBLzP}v9*iJ57}?2&4T z>zkiaYR^)u8fwzNR@ubNTBezJBcJx>J4O&B<%Gk^$rdiq17dn!Bw)T0QONxtU*mHs zmhgJZ1u_61uyO!tbHTUyr8EI@5QJ0qRmr%S&nF*Dm!+-(!mevYIEA|NvgTdF`!~_E zEaW!d;ufkKkE&^FtE=VO90jb1E9R;g;|p2crh+|mL`-nRrjC!)%Xa9=dc4ot9OT z^$!h=-QZfkKPy$K)su{vRqm&07IJXqBtba#mZTNTqhDhuP!t>fxJujTvKOSkj9xQ- z5?ntVlf6;b%~ZBi-F*GpeT+cSQ%;VoRdfygR$rgFUOdWA%}EaCc@GqtNOcs|9a{0h(xP+WCW6kxm%w>haKs z^d7(Tw^^$ZK4zy^GDD)-06_cu^tNzlcA_W|F*r`7`9xs zl&SR0wg-nSMAVr=A<3~HI8%1l+?T`F%OL$FjNg# zGK6<_==FZeACVlt%uo4YryjG^ZVzbMBI}Y!Q}iAix+lS=0q(G=>^y!5+}7%Z6j+xI@(fYe}mPx)(|4` zHB^lP7Gyu|EPWEqF%OZ^=E(ibLgoPx02Mrh<%e(ZV|BZYf5sIxL zMC&>I7%0-&0Yh`<@c$y(B*JxwMyJ<*K@TL=qE}%LJd&#>1J`2I-KJHzG~2$%@ADEp zRJ^-CLB!%?d8TiWrGVqxaVXfa{i%)&4mo}}iC7%gW*-hefjeb#-A-w@QKeKE!hrLv z=a^V{HGKi9>HTJoe#;U;*L(1_$<_Gjmk@Iq)`ZM;U#x===bZ84*BxSGHXvbgKyfTX zh!$8IZ=-ve(ZYbMa4}5L$xIr*!N*sJtBHQq57(jOG}ke)i`TZ=?4iG?tC$%l6~74& zwT-(*A#Z@T*#_S1mCgLy{#iS*RfZpNQ0-saRnCEXF+`BQS`5 z<{Rw(u~DecGGyoy4`#$?jHy@fzuo&s1y_6JI||o~WQ@lKcYfrl*v9sxbn<6^eKoIx zr;WR9+r#JD6FsM^>rjS(<@s^ggwxYv1@G12kM>!v;m@W?{ve@?uA`ci)`y+7=WVeL zy!FlBU*Ceozaiz3req}H#3GKRuqESg0iM}P-&f?vr#BN8j!thlM_=9J%2xNWiz0UB zv6wrAE{2GCd@$$axSe^W!l{yyi6Icc86>}NrWp45xo610V4AttT2$-b(;}rp8V`Fd z?$}y9qEC*bTL0QTQKeN&m5*ENOO3Ay(%58GuXVF|lW3}~m#w^^eFPXsaK-qTQ1%ggSIO;)oA zj!~ME{A74li{>(EmHVDae7Qe_r%+wu?ss~0UHU~N6(Q34^;O_;LIV2vCJ4~px`Sr> zIb`iNc|7N55HBSlv;`AEQ>}#v1%2Ja2`|$bFFZDz0W0NN9e8xYgNo-he-!V#-C3{E zuc*RN8Aa;Qpxc|~Ys3d-dD+iahjOPrj*dH#Tuqgu7BVE4Wj*j@-e{LZf~MtJYP!~_ zhJL9V{f~ja0>;+ch{A$11c5;C3zaglk^M^<#^Bk^DYRMK+U#F1S=mF0g_`jmj@8@P z3zG7Dsy^|Zrxm;-?3k(5oI`S>wnY+QY;PuxOyqn!73$KMcl7skaR6qc`ln@8x6Ld&5uYH z=D|SsZM~U<|3>PV385oe|F+oi_tt%dD#n68)Fg0Vcf?CyAh=$#;Ge(!`;Lyp3a(keB1LO=(D?$^qrIpzNH6Uw@vO56?XIt|ofTTBbGi3s+qAq^v}%=y zz@;D~_Lx}sN2oy|(@?n1+($W9jBRp>rnUjG7vY?BUQc!5&a2mal*m}|=H-86WTAi0 z!j^8YXKGXi8eOfnMzdN#Zu;KBK2IaiSRXA1c++&I(HQd66p+$R<#7dXzwQ@{Lx}f8 zN}DSe1I5)bzp2t0N~pK2qN48`1-Q_D>v>tgjdp*(pEk3_WEy_&eFIgN3ki#4mt zkLB9Hl}@rsmJ#zRKR&cN4oXQb<&ctmht;1dd-KsuY^0l7GLsPvG;T}H6<-o6 zC-%XG`LBrhM6%VS=YEZe`5_Iq`3Pb&Z0R1Z=ckypMJ%3y+OaAFK!Oo4WfK|FXz`8y z5D-Ebh+SkFq&Sq{v4*V|^FB^Q&VOw$O&Xv~$_%Nn&hVlFNPVkZ@V-KyNGgY~eqVMj zv$%WT2qyMv!K=>o>1gI7*cb0H+}#fq5_l1wi3Uk6#pTYE&SGvZ!V3k9IE|tPnXT8Z z%i?^UzFQ#g1R;3WvCuoMj~5I~WP{Ezs3H$kl$M4$5jjaOLBD&&H|!jlecvX~lLN?l zA^wzVC?;$PugO>^5ea>rO>sWHBWD>&S%F3rz|t=b?$_H%CxHN}W8I{)KwSwLg{P$x zg;m1r`BQ(oG}eE%{H!0Iaww6m#Ff8AQ2Fk?k`&%0U*!#D?1mFaAbrvv8u<3e*(P!U zPJrsUPeujTEy zU_w*2`cO)4&{p`o5lP=0F{`Wdwqj)O-14MA_fI3ttT86GpQs#?Mf9d_JYX#B09gKm z1EMZNd+;a;<>&Y}Q*N*~rmB+rqo&%y*lI^jg#ebQNf`Mb*UNX9c&CWb9Am?%g&MT; zNSyBmg(oNkT|&~jX#!EF=z*ytmBn62h7Irzk|!6GZ2s4W()T}fd&+Ri_;A8~z^dAi zyBxNdZ>?=}R27dYC_E(LSd8wh`mO@Ct<%9(QaPDNdr^}8DpfDAm1iGYLn(^*X zf*|st-FLp z4e}6GA~VD1s!|U559r6gR=JYR`f@=9?StWdG8*9m*K}9ch>#0w$OSPLUdzaVi~q?v zywR`PiV6P4%Lu;>xIPiH+qs2$*0MmFvOzq8ne?=ypC#Drej;rzMPRg;q)g{GP84f0 z&*`EZnAQ8fC37O4&}R)s{lMVW=Xze*mzo+mdhlZ?{fcCNY|qB|br_M}lD9aR*PnR- zZ2v(f=v^72w_HiRen7Sj_~OjqIeJJg@+3KniTWK@xCYDvC{#7!#zVw-ss1ws^nLPN76-F@_+xEq9N=4o$-YNcm~}@c9c1x4tGV zV8hDPpzguvD0L|w%V=0%c!x}7_D8bdDc5EG8S9|5_*Mt;DSl7Ra^JAexQN#W|6{E2 ztEq7@UxECDBi(C@bO)23Us=SEs^c_FY^)`Jw}VU(;C@Ea1Wh%Ogo8V3s)gWDitOin ziOi&SNAYse+-()2RZNre@N&-ISZr&ZtjIT69*o+os?5!N#a=5;8x?Zrt~I1KfoVF& zGtSdz-mY1@kGp@-&mRTaI4bztH7r1XW1j<%95~jgt4s3bo!M zC_j+R{C5qe)Ai32k*hJMd}-F&_!-v;?735f22+#vX(0k11j^F{U2r8k^J}&ovF4_k zT(fK(uS~=3!-XT-{%uYHp8Axp&ioFvqIj-20OSw>`ob!HsimtNX$Ey9$KXE4yts>3 z%dSpi-YDPPjM&W1!L=5B0pF{Q1&TG{vZJ!=oBHW{Y!(W5Uaf8k@tCl zY3nhq_qilnUqbISEv%Pu^w2RIvb5YkGg8XnDqzVmjuU@ziYgwBu0rEYCK`H=yW~v~ zvC)H{woRlZ^(t1xIb2TTISY4WNs>+nuuv#-g>{b@F z#Mmm^tCchQ?*e+RP`76fzC?>0UeqWQa5cyGqwe_a9U~RG#&S8utWNebG8-?@yi4%J z{#5=;lqUK}N{3t2u&Oa}L)Z*T?AL3`-JB4D_nfpVYgOAs83*)3jWpVl@)lqKFRTpFMQ=l?3_3l14#)n;cK7ueY@;o_96eto&4uFcZ%MJ01HCRCcXa~ zA_THP+C6b~6lv0SCH0O>m+3`OA!z)IFm`AQi*ReI;KJqQkyHrkH392XKvHbf8$b{G z$NLhzq>5w{n!dg3XZs4&jU680(J5Ul&9~)Q3qUxIL{1MxTHPiExVMyJK1wctj2Jyg zF{@TpY)trxc(XEj;^s6K<(TuvMyh$nYJdnM<>>h><{zzt|pzdsBM--5ruxZD18t` z$YFYv>U#mV7BX@rT}$;6dx^oF89X7ymmwu#S$<)c>D$*o={Xhv-4Wdq7x7EC!WR<# za;wadE63i_(AVRHvTNllm(x{M(tZ)W$%Cp&-4{lWs<(Qb<|tr{Ydj^^A(pv3@#RdM~IDz2MjOE=y~)TksAX*C{G= zH--Jvkm~A+^wAW&8btASG-&BsAfw8b$eMW4~_yh={7k^ZPw%q{`Fs$*K|lu zt_HSek(;k+zrY%#Yf}+xbBppr1YdXMN*x>h9I7{$UGZ{M1 z>`srXpn*SNi)us5@Anl}P0g?KM5?rNafiuURWp#x&NiJpsR>kM88F5V_ilHdM-+UT zpJe^7aSQ?1TazY*Q?GrRIZ9gH^xz_Iq!IkgN80P_w(iuuCVde=`8bOVGQzMPp%vAd zV5QuAbXCToHj+_!R4nweAe~z~aR3{nVBXR(2;SG572Jzr1&<7S9ma_#{e&T98rgN2 zvXCg{-UZg;5556GIFcO5amq+~$~mNk{GC+V9)S(VI)9^vr6mB{Rn!g+zU(Qp={p;p6ub(_(jQu;%Na7|vTRI)j zF$1WrODpIJ>?cmtAXfBURI>F#LF|jFBo|{pK8Ikmg~g&98sBrgs8WjV;s2L}7tCtW zT-9zKzI3h2HkqEETP+5s5s*mxci5Glv%N-rT3YYZ1xF@KP-T*UM3pZ+Ap*sWc zB;mVwSKm!GYQaBMLbQQT4-ROyAoSGNqhM_&x;(01!1Hs}wdHc%5`P*M?oK+yww7%$ zS!P~$%uNfMrSv}M+4tZ6pHi(q!KKYSEG+2b$#$bnuw#*QrAusl%F{r+9r-cCN44LQ z!X78&omAquAVmC-y!u~1aSD);rzwY0G=AX-y*e*kYI0V3ddhlEN%@8I4LJG76J*qS zk@m1UN;%{xcJtf!>@?D>X4YIwR(k+N2q1a*J?0_&#H@?mz~0-3{bN%R^hgWl-73Fy!lU;YsK3BsQioG< za8^cosgc<7W16ptw~wRiRjp^AA6MV`Vj&V#;#EyNxTbb-0sTvrfd;$aWA19_@w!yk zY95OtTsqbCsz2jwfY&J<L24AKg ztw>t7)e9}{eA=t_HMvb1`49UOL;(+N{?=NrHt8-p;_GILwCpg(0Gr&5j&guEA|vn4 z_(NL&h!+%1;*@R8hQAUV&-N>3boOV;H6g*UN3CHRQm7iCvQ@BX9 zFO0_buKY@Vs-?R^ShqeveME6z$)$}AJCv2atBX%_N-aW^B$MJwq76+3!}MD|8EwFM z%BzRd+BCiD8eL@Nd3TX*eW=B%hU#4YWy>lmW;YNN4@|}G5ByMjd}2$~PF89oYnwTu zI@WyJisWnGT_wO7h8h-Vkg_1C`W*Ttq333K)rcl0GtlG#HN*)41hXjfc?g>XNU8eLH6zv{Vxz(bUK!qjkCi`uJ!&D=TK6uURtWl7u zD*Suo5*V(*o(yli6Y-HMo(&tKt66O1t>!J^-oc0L;L`$YXw4@cC9!X~2S7XKF2_vrG>LT;yuW~?y zGKfE#V){}#7dtldacy{vYR3h0%S-d}5A_9t3gGy7ZEJXaxlVP2-3@ZKd5y3o{=rOC z16>k#rt&{x<&E45c&A7!%|97-!=ZnU?#EK=@Kt)u!dU(sW#+M{BUFbP>Xv8;A0Ye` zofktkkPwCuC*P>whxpb&a3{g*>p>V~gZWQ5>@sN9sP_*BnLx-UMx7MRJGjMf!AD*G z<*F7`XrW%)qfUr7H;je#61lm&NtIDw}TN$M8AsG4Sw4 zszA&aKelIq^7MyZwM@s7I30`K8yRNx$~T{18o!d{*yXv>0|mMwS-Sc}^wkHZyZW076F_m|8SJ+Cr}!# za0wG6K$&THQlcSn9=HL`bX{*7$w_r6?tq80 zg74!_^})hg^(e}gr^LtZE3k6*esvqYkCOeSe<|3Re7h?j#Bk`6Dxf8athxRB`zFJbCYc=xGra%nSp2d1)g z^fRzM%NF)r+l$sjp(paj3x3=*q53%BoDprZm0F03rF*f?L7;QVt)!_F(E0Z6$5OKF z9IYry0hIDr{G9#j@z(EqfZ*u`1PxY%#y?0Ms(zTGm6XVO_|)J7CDw7!GMA(|s~yB| z^G%V9u*7EYzd7W|809?j?at5W`o=<6ap9C*d-pvrRx0Wt))AZ$%n7jtYF?j1~4B6gB3)zXOTzcVX zvO~0MJDB=qV^L?yXvuvdm=Y39rDiIANx))Ho{x9fNbbPFe)M+BP2(-ld>AM z6cLY-Qj_0D2Vbl$ytyZj0`-L=Y}{~E{eNL0+y^$!%Lhj1|8(lw-f!6RJgQbT1^k@G zDKf9qHj^hmE`-@;;P6>|A}|xJM0WR{^!|D~IJ+BoIWuzF=_M0zR^4wTC=d&to#bW< zcoB06WlNJz1Fz-{GAt?sy)*7_It`59Sjg74$T!%2)_H(A76j|w&~s?b;bwc_8<$eI z7+E_O(`{aW%Ml&PoW-@3Mf{Q#>dPux%ySdtfs-~>Bqh<3RHK4X^<Iz zyaDD(h-^qBb22yU=A>^vExG3^e@g{V@^~rbu^crP0>h{jAIbKi=B+04;uarT3(qFS zNn)oOmA|+MVaY{2tt&qV@53GbfLIireBUBuVTQm!b2Mj@n`5!F&(q!xK@UP=TcwyM z9-4hAreL}%7SmPw1>TVk^@#cptYkl(^eNb(6FPXjBhDl5k1=lIOt>dZ*I;sx_vrj0990V5mQ&Nj!d$cACg-{|KcLCK z6gz@Xf1`Tp_;}C}QfjW?!@(BY$}`SNr%oa<3rlI#GlbP0ws6Er3t_NK*bQZ7K$au- zQp=)A|9xQ|Q?v4l#G*?~k|DO%%qji#a5d$w!I%ek%&-+Y!UA+JE%e;P{~y1-}oyn4N&y=1lJZg&3hzV(Ta5JS2QKQfKF%_sQB&$i?5p8@i*;Aoj` zV37BsSqc1dkjs(}XNpnS1$U*f(!=~gSi#L!|8AoTg@iv5_&9(S{Vd)Nbu)VHRd=3$ zP90>}@7v`_c4!Oe4CnKHYi_z8z0eWH*apBEghJ+eWBqZYH>|NS!Fh1o8M6MH(oKGj z(nr}Ra7C?t=lhn{XEr8g!SD;NpWq!ADDR0O^W9FKI!^5W^mNhJc0=?6&U783??+;Uazr3GfK8eMiXWp5MKQ*|Zpp z$m~VCGTXFBb8amNW(KJ7Y*ILp zQSO?cY>4jSGv~~zxqHExMw|#Un!zPnL0FkCc}0(^*Rmsx#(S65jE{w*{}3 zRs26Bf;ym~d6Anpy><%ct}YhfUayEQbHF_->J_o|yBxK>olY_|AD;E>EcV9*f1jyj zJ>vwoZm-ZEoo?B!aSQM;tP%KZbqPFW9BR#Vn2_N&t$X#>Sn+rJ{BOu%-7~6m)E?KK z0E&tLXHkMzOq4@jR)*}f`@s`+RsFiiLFGJ$AAw1c%9{6tKi*wzU;hjMGuIDgRjE?s zR*3(=@$DCxuaxK#pF4vs>X4~rk(n`{|k`aUg@2@jW zGS`}NuAef!C^EV)sE1weJzz3jqR{(WZ$@p`DD%Zw{Gas@otdg^+<>Kg(Ktt(GqDiT zP^4xqMK|l5F^>=FV>>ZWjdu~jBDGS;Lm>w*C}lBjok(nY8UpOzqc(3leB*N*)BV-J zxuaArYt|{Tb|PDj!7|NYS$##k^Oxyr#+@^0@t-1DgqUn0e1VV{N#Pw`Tb@p9lXGWc zvfJu@vdWTNbN;`k8ajU#mt|`So!X+k{*7{@Y9(0-qh~~c< zPMGhpAn=UB75MPqO8qM*mYJjq8^-M0qn*ewx&pDuk(whn#C+TuX7FOuXtM){M8&kY zUWLLJE)&Wi-?jQCslT-@h+7H_!u~_6JkOa6V!q1s4k#juMzE$VM$7l(o+LpEDduGn zHjrQ2$;OTc0&g*$y^FTS*_XqkC*myc^xSQwOS0v3EkzxV=#RT!{hh{_Kxb8(3q61@ zVvTN_?`D^1MP=LElInp%w*JHXBt!d)@Zf)Xt%XFDwECeA8)3HoquV=Z>-ur3nb+N) zxuCpE+)?npIN_hg$CfM$20WL+&aY(qAzkoqUZlam0Z-LeBzOZwDNT`<;dB)z+HDo9 zhQ*5c^TSTe9m$O*s%4`P&Y{(}6->27trN@YRUK)*Oh;h|lvxjR$;b6)8u9GG7oDSA zv)s%w>K+og?tEp3!?uUhfI*LsEPlL~9nu!d(btOn1iT&#cY>qh=#3)o5T{ml8LyZp z8i;uS`K+!cf{fo{`mDvUQ`|nQWB@+b$Upw<;pa(&2{^{ZxvVUVchb zVMXHf*w0@MEnUJA)s z^Qo;^*hiA#>d$-5K>113@gY7wmE5sRG{r?g_^Ib0P{0U-{QLQfq}bE=Ww4<+f1IZv zWa;VC(C&ElySv=GH<{pPh}*%85~s;q3-PuBwKS0{{P(FB5OspO<`6%jPvfSSFPAgH z&Ox&6>vOX}lJD?tJ7WAjb-D z>WmiX6`=AC6bNart)e98a8`XnlaEr}QQ zcWqOA+Flwgj$8V!C)`@%MD$nzVFd_lnek*0`It8bo-dDQ1rb&hU5P|L2lG_M#4C)p zO2(kt?Sc?)^uiPN2*#Odz&D3(2e4Z-Z8QG=)4$e;^Y*3^(BxEeSH^M3^Sq*awB(<- zw(+_zbhRE*eR@U+b!c&0$QR3+aOB9XH5CTmhuW3bIa4L;Q@sV4Byz~Qm^?}NFr#uU zw`&N#7@Cgng-^;yZ{*Ixl{`H`?d?6s}8H5+9VZFkI@;$v`X(OaR$YRi?zXM-6t5akJre58;f@xwDsRc zY1B^Y75F~Zm8QG$w$Lv=MccsRzyB%PtA~>RI{;$mF_9zk_#?3qz7-yOotm>=BRm1O zfmd;$AVO8(8>$i@6lm_ny5Z=!)y6-9AI(Ai37MBbmDokEEC*u%x&G5ah$mn%8vDeM z>JG~Z-u3sO6?JT6WC2YDe^z=??M@&YU$8B2jh~AMFc!Q{xLP=Y%5SwnRhTlMPRA zBW&lJy$f4-u*``x)qr_#%uaUu+@bKeI}TfK7I1~^@;*vhZ}Cyds`TF`_Vc>5-=+V3 z(z6K24ivM}ZpFqq?KSh!b&S|t}r~ZfMT^?3y~Z$3+3fg?6E;0R{OLz^ zJaR94d3EO+VQC|!P1t4_A4Rq|DuPoyhg$`Rn}jr>+XZ;m-f|ujzJZ+GEyb*Q+G)#4 ztCzMf2L@U|=lp^n48}6%??1Em1|2<=A13xFV?+iL1o0Fk=O0KN7I8G52N za09P*td`f4BZB6|vqIh`FVv^iMyng(W9XCc1@q00ydkH5jR)olwfUz#ttmj~jF>9a%}HUjpH%ILVO1=Y|OxMhtGQ^TO{H##`?Mzc%4vbCfbnqJ^yz zCSO)|O5esvKBa7Z9aZ!@pZ~sXN0N-X>AyyKzdk+g9D&AY41DPg_}U3h4MQa&ou{Vb zWeOYx;Vk&Z$6RRZ{K2Y|!u>CGyXIc)LDx7l8E_~0G5+XvD*-S*D?n+S4_>sP2JJ{j ztV=#4erPaszmKJA}iK{QiWRAuV)qb|rLLg$B zhlvk(8nI36fym;gI`5V2Vtqk0$6tNS7T_CLcGdry7Jc?FExK23JyvmRYOAkb&QBb4 z{%9V3tSWN@y;pw&v~N4gvnG1B%MBT#N{9-_PjZ8)%UEc_Htcl`D(R@Fc(0t+R%o&l zegGstH8vPZ<_|Za-Dy(9i%L!Y;~AfQCWxm>p&9Mzt}q@a;a-j{+F;gXF>}#0?8h!f z=`?3f45SJ3*;ZVCmirUWEbtZyu1ia?Zjg{=nJ}e2Rx6|i@GPlkusn@#+byOdrxmDW z4vLJSO#99DV=op_GMawtz}Y8alwXMxL4X=nQbH3( z>?4;}XK7OXLdO)W3c;_IWB-t(yO2!UL!L{$a&FSvuWY~5Es-E{tF(pg6I>+~rVV~v zNdPXf$Ihgk152L%N(-xHh$ICWdzL%Wl*IKiW0JlIba9>u$j>xM%ET|S}qV=Z`6m=8*~2y=dxAaEw-S8{0As|2_Sem__) zNm_=+edJ~P-{kJxh>>W1dVeTt`%Z2UN<(&@h5}e7rP)@|wo1!`u@{EeObgArHK^_{ z0K6vy+0oYjl2N4D{a#|R3H=f1w`;8<_8a#iT|y7gAX>nBcRN@pTQtij?^#1J`N|eL zwN*H){Tr=tj?kV4xD1FhFX>(uhnU5RTf!Pv9HDU|~KUWq7wc)mrOGJ6FC>^BJpYVv8 z$XREz?Gh**ok?#@xhQewKTnqsAqc``E3k{QZRHJr90bSZ2iCGh{g6nG8XC z5nuf!itl*WP#B^9?Q72Cv>HfgN$VACB>0uH7~dH40Pe~cFzNlMeJg~BjmaSJ2wZ#_ zZUu+X!6_2CF!%?DA&Q0AEy-qeNXk0+1G)(J<%p@JQL|`-fbeF64ahyLZ%PJrekOKU<;e&^;0Fmd!NiEvGGU8U`eUh5n7dWmmpd$t9~bzh;6u z7p6XaK%AgL>-mDI|1NH|k6K?J0gca_^7&6<7b1&A5nmomH+_fxR70n-a+IEx`w2)T zT-afgKA&Qs_AWWBZ^R2gdM zu?!{%*$X*?TnzFZNS&u6w8B4LGVnOTCDqw~K5$x+oA6pv#()GPk-5zQ`0oU^gXz1` zkfvGku=x^hcfk1}M)tVCR~^p7yP4I6twI9lej%i76XkDYiSn4oYBDe2pzFPG_ZECE zwW3IBArb4D1s6MU*nQ5-N!&cGP&{9SKOQXTRDTtS&#LzO)27!g&(947Um6nNTDUuj z2caRm%>&ksUThBiEuaPXP49YZ&36|6AK3fdO)4KhOktvRh`WKZ0YPB~@_IZyoNrM# z(-=sJ@;UH6N9ZWl{rH{FiG*$fquV6ku7!Y0jJ7jnUso({6B%~eLigqhXxByl`S>xtzAgzU;NRGXG=ytuN4GRC5a#*_c zz>hvRHpMkoPKG3#0Xh1`S(7Pm?BXFQOvzOxp+PC1Sfwr?;5{j4W}QQ!d#7IstCDL! zNxAtc(ngtGvIUwZu1ch@xfxqz;Xp7R*4;iOSg7Cw9_>0aU6sN_hO%I&+oL0q$q{e% ziJS;bQ1rW$0cbO=1kG`t1BGb1y^bTtZpI4{f&1(n|2S>CNNLeuN}nNCsho+{uJPB8&OI2j?~2MY)}idp7L1A8VgWbM*T5>AUX6{p zpluovc&>XbXvo5_FD!n>+v96Y0?BRcq*0?K>Swc|;@+sS( zRvN?_lN^}_>$^QS)j(=c5=v}5cTslOGs9j0(Qev1H1oXn>AWTVJLgf~xxjwu)|Ag4 zxBrJ6r8+|KpCR>$q#2;@rpS0|;Z}KJas(L(TML|oVz53Xtk9Y98B*wsWSVkKpS4m*?NS5-JDehpteA}jrPqUE4hi+r;&OSRFDTaSLUJ2 zIp$%`pnXb|!=w|pL|<+CG6^o$z2)U~j%1ksFzY3i5zQOO7bOw$&1ctd5k?1m_E*=Lxh-2>h{u>bV&=&UP}Y z))M{vBQN)H_wnS8XkbhC(p8+Ox>`ytc>(v!nPs=!Y}Vj@9ZH zAvSF_FM6}(rgX&E{0pZ{iCN2ViYb>U4u{edet$A1T`KUGh0;}P7pPgZb8lKt1QZ!- zK-uVYm39a*7Q-v8iq7Mb?%J$GDxNR+YXPQ8^_Xzv?cD!9k;!Jb(EJzP{cSDP(fu+O zVcB?1eX}^sA6LJKSVN3LYL6a3Z04x9mHEJ*XZ;k^K+g2%P0qulp{D;=hT>dsyu^yC=moWq}a^NUE7D2^z_F`01sgI;)Bsj_8dXEhW zUVppe<9v(%)jkO=bL0+a;D!XAJ;zEDEY)N)|D4XGt}vnB*N$jHxlgwPx@N|F9!55D zBqEMvn6|>|fX`b1t_23&DoIPvp0Tp?u61}|V&BB{8*{Ny-9`YDHzIH05LkdB!&sSV z_uY2*%`oHZ3a=gU{blgmTUQ4S3b8VA`nj?k@mmL1RA!TKY)je?9s=r=RRNcE0ExOC7;g+B>;maybrb&10HGv=7)_B&_oTcOe7>68|Kk+`p z!zLGR?EV-chnzOk>&EzpH3-X7UXjKbOHQlacA#5D0@-dQ^!SFm&mSL7yxlQVpj#Anj{Ow?_hA;WExRuj)!10||bm)Rl(T*|5{(NSLT-b#|i^B#UWgwjndQ(E0;9HUsOn+UM`QJ zuEy9OR5q$5}b3_~dZhZfywSMgT<-7TPGRTdpA7hGV zx;l~%Q@;BE|%TH-xQq)prZ-6f(Zx?XK;VEr-ilH~jGy%pgdhVT9p zz!FX`5Y@F8L&8)lw*N3TDyQu>$CLd-=&ujHKP@8}k0gNuVHf!0girU|ys&{0jXjS^ z{Itlc>>h^wv1KkVU#-rPhlta6KDxe_5@@DBr}awTU8q>VXXtDPG@t7S-$>kXg#SQ8 zQPlM5gdHx+3!gY|YZQCI2aO^t-q4&J5zEeA`J>BUI}`r#?75vbSyQPnnSDy(+5_C zI#1?_iB=AW-ct#58bckW#T=s|M^(2sU!Hbw34+{pr!G6Vc+X1Plt8WVZhC}Q=T9D7 zFkVXo5S9PoxGUSn=2rVgp^vo7T0ry1?YiEeULcud=vbuEW@tLo(w69@>8FB+iV zzMVOe*u={=(N3C(;QcVYItvxHJpMz>?9jm;N5)X)6?7f|e%Z@ar`X@H=LXziI%Pyf zZ+}wC=Zk;iR-j3>CAWS=YANG3E-Of;+Ck%4gg*MCj90tAP%-iPiwB}8O{;`G%UlW` z4@qxbx+Qo&O=<3WGq12JCw3N;+ecRX)NX#vH7umjv8VZMy0}Q*gc$osjazaV$NQ(9 zCgUORZ`$LtK&qO+KZKKa3uHiZpa2fqADxt@)hMK8c+t(b2ZHyf0F;lX+@<6@pVz{* zj?ULd;$fz*^>Up3bDgA06(CU$dI z&&+;-y!##3NfbVyNwpTzhj*kimS(%*c2V_te@-kPp5;j5NNxJmGne0l$XpG;Zu#>L zV)06JfTrED{PV_Z7wQppsuQDe)nDz3wPZR@hGl zeaZk2mC0MDC%Ieh2f)PMBj^6yYxdnnuT_ovKhb%dDkQ?FLcZO9Cz|%PL1IcY8R0|yHnYa^#{dZlf`=AeV#xNhBhTuS zT#H1hC%wbPi_FPI*kZ7&|6L6-tEyeDp{zk4o;HakmmiySU1x?Hw6}Tx z4lN88vJzK9F+=v${ExJK|Kx9Bg`Ft5xZ^6=nqF4R+v^3Rc*|oz*Pgsz@Gg|@!<;&o z(YR95RA>X~DW?G@5?902HTDW~nm^AD92Ot>WlNH>U-IR{=&q z9bRj6wZPyqS~s0%OJ#~!Og?!akqhgD@JxyV-P{ZyM!~}5aO9C%F6&NS6ZptdA2Dj5 zmpv=C38YKHhb@cA2WL02(vOx6t}Mf^`_@R8My&|zXH4#32C)f?vx(YYzFgn*#rC;` zzh)#T3)2u%yf2}nmYy;`(q03O@VC+*-@bjn8&gm`Qxk^v41rlP&am#{g7`*R>>&)M zq2USOy2#jYX3b$t^kzuUaa|a-A2u|CuYB7+=hBzd|G6RL|2r2rMq0@9j9mAnyy=A} zh^}~x(_(^*5+SL7%WJjpq~}@INqr)DRE=lbZU|8cS@|(2Jp)P0<=hbRrZil-U7kwb zW3|$qWI<)PauHKj(_qGLxGC_9J|s31QK}b^71XvjaoB3tz3CcUreshle2-vBZ8W^< zQlCIuowHs>pr}c5v}HG*dH3)9LYaED#S_!;m-m1B9QhDbKd&Tzw7uT+UGFx4u7sFq zP-ZNLG=+&5!3_2c>7fEJMZ&M~9q0LKm@~Q~_kZqOivP8xrPT{G@83z5{j}|G2&e|S zAv5(NAhaRHxsahYIK@f(^1gUyUr1pvDOIz-$I#6(>00!G=p{_`R281D^rJ1^cSNgP z@k&&gnSBOGo_iJpU!D79pGr?_jppDZ*qTg63e)#w=<=;`FkOq+)O{LA@hts!-eNbu+-VNtL-9iB9mlM zZ~yxdm$lsJb35bs`cAg@O8ZeIx!NONtCWj@Vm z)cfy+STOlzHm;Y^RgvOU-nM+e8gAHG;iZ_~T469*H`*G7B+a)5Pct)X86MT;^S`xm zh@K2GSIo!pQ_vbDO_+Al?J0HNj}3B$oQ?!d370J&V8z;E%~#xDJhS(XA*C7#opY5B z;Uiu9#5Zi_BOSnO6}vSV;TtKmsr*$wr1RNLB>e?mq#+qL!A?nLj7dtp$Lo_Vbw6hsHSH4+yz{+& z5$s9=1f<+(koJk|e-4&+tP8#^0i*=5aJ?nQ7S(gzV_WAJG+Ffa-}LWD;G33K96VCr z4t7KDJY%${M_$b2-roQmP&?TdWE2m)9l!=-qml-mOkWf7zL`NCn=WfoxQA>P&7~LX z{qbd6P33bp(lmJ_<~u$x==2#oD1P)Y`Y7(Mh{kcfVno% zwx1&3xjVcwiT!xqMu=&j)C?32x3#`|JTDo7$tswGbKMB24gnZq+SKDe^)@MN?(ojP z?26Nb$8e?T4nO%N@8VxcMra-d5|ZsYRs*~3zIx)V=36TD;eI-CLw+-6E6~fe`l+{ z3cAOW$y&`KX^Rq^@Uf0D?G)gmDn_cS8|4%1%~1zhYi=>0bE9$^>~;2G&?zVST159E z0~v!Mo_lV+AIXFisiv{^oN>G{v3v^q zuxK4R;z1E~4UOkclSE>z($ek_9@%r?106WRt$yh-(E7@$(mSsy>{bqi6U|~RwA1j- zEdsxz+y1%XZ5IpVwdAB566dL2rB|0Ax5r_SL*`50Y8)kUH7n3uYk@-pC*vCPwJb@` zL1wP%8T0D*5>a6bX&97ul@Y;Q5TO<={Bl}4_es!OyQy*K8>h!n{+W+vy(cAXK%9#0 zli;)e0y_1abXJpdT`ryW6=vdJISa09r?PB&HBvBqqCXiGGI;4N)qV?kfmn8EB;hb( z%Of++1q%xt_ai!ljVQkC(ky&Jy2q)K(~)w`4j&{vX${;dz6^#S#Yk7xZ@aB*3(BrR z`OQ?ZCM?Q|;4_~wx8rVc5oo680Q*3@1+lx<)ZRXuEW+C*A-@T6k4NPR-(NogL%lI_ zs`RmNXZAMcrE@1l%fkaC(v>XxfAK3d%p^DwF0mV8hJ@WcxB3fl?ht^Op(g#T6$)CZ zRCf+9$vzhj@>_Yn9YuE&s{AdH(+0E9gkBI?xX2QcwpL)C@qWaA4m9+xzzg2Y0(yHB z;n2>>Tr9+Dr}y_GoFjfx?{{3dP7i0@>e9R=o><1#Pd(xnJAoy}j6(d3+Stq-1k+HG zUy5HKxNU%F14g}x`Nq8yef`a~BA&Fc!*xP1b{iqD8u@#L>HDdDk=_y|PKrBqxa1Vv ztMt%wG{RRHhmnN42HQnFP&fUJf(l2dv>=D0T|EqcT z1l=F}*Zox}+BOw*Oz(X7P27+UNz`G_ZpUJKTaq<{JP7>R+qXbgNQbGQL-k#f2!HYz ziGc4Cv_mRT?5jao-Ihbjj8KySGb{5%4OEn8B2Yr|ejxS(U+;RLAdodxQXlHv1Oxws z3GyT1u2T}qKI4Nz5w=A#yv1JJ-c6N|CV_psgs7|vJB-4Lhv-Qwj1LF54hw-uD2}+m z$q=WG*1?x??LSe$V;C9>vG8u2<<*f7;|w{Yf1i<01{LP|idgk0PvBSjGA+Hl+ItmO zR4&eee5CKoFv=K!{4#bL0*bwBO-v-Z2@N$Kp-VZL<$q`N9BpF=0IsLS9m&&gDIF?0 zk$seA`gdc(12D#WHxZp)&dN~6_Ewn z(6{3|PXht}SSz_)?+9kDN$-5E#irOv(VEqcSMKo)lXZ2spW8#S`n|I1ydzOW)9>hE zLqtbd{n)qVuiiDGY|?IVW*bkUJMQ2td^L zW>Mkb2Dkky{9DBXbP0;`-!2ZU4%?nkj&|XW1fZWUZ(Ch17H0-MsZp}|Vzyfw^PQ8f zVlH6oc&}4AYSG@0%xu+lPRFQhtZ72FM`sJh9Gw9nE;Jy?rS?q;^GFuNg7nD4VxDd5{?8bA`l0&I7%x61{ANLD z;Qx)9-qV?o+ix0iaI>npr?msKs11(N0)O24{J9pdv4Jdk!2%83T|nF41}V9A9u+us z(o@6N-HzwImyhjJY6Yz=+m-9%WtbKll+s0nHM9+oIu2gYh4GT|l~9%`5%-1Sx($o8*@s#;lkvpUMc5cqXomjk7hk&{ z{K%Rg>AqKhx#V}kCrn>&i)Q~-X=-&NU$|0 zR>IBv;f%F^MWQ0Ubn(0b^G6mRW2q%u_d~Y42G5|AJJS^Ja?`}ZXt%%9dZ63>^TxPL znwBJO$$xApJ5|S|&v~Wl9m&gBuhzs)*o@j@tl42(DSy+R`EJ21O(E~r9VmSTHD})o z<YY48}^se6(MK1a-W0*U*0zy~PE8$reGIx>fs6x!=w{4~ zFU*m05QF&S@5C9A2buYn8*v`dAm^21YHfVYDr-1>5^k)eji3;K*2ex%uW&TpqDAqw zGGauTgaoJLSnxPLv%~L2_sx!WIFW`l9=rv9??!#tRRj6B42j90_{d{2fk}rhvu#o4SQOUP){qfmHYVPIM z<+Au%&X3xw%$7*E8pFd)6rXgf^myabIjfewL;%Kn$4xEJINhw+%^jbNzVBm@&f4 zCaKo#qzI47S(zTIJv)uq^mojg17%Y2F=3~Fa3Ovl4S6nl;T&0X09GAmkM}#zcvdTD z-P%Ki9)6EE9%sz`e1mHeIn{eyv*NeNVv<~OF4F#0;=AJp@=BW_rNzhd4O5gCTuF&! zUH`rED^C9f-g-Pv7FFGq%>NG0RLh=7*HPUWm~)4+;ZWW8;@TC!W6`c;Tjok!&L{4F zk~0Unig<=6ZRmv`$ZzUm#)41jW*GIhZA+P!#R%ZKG`D3HI#>xsS z+(;^Sn%l$rH1q~?tl1QN&cKXv4M58cG^Yf8m}p@Ezo*$y=n#>d511}^c%AsHl zm3o6w`TH41WB5;dT*JI_*ZND+r}t8A36653y-gSeP5I9p=Yz$65*`zq^j-ZnjSto9 zQNDLG4W8vI^Q(_&esXr46lam<0ds6zEma;S;B8pKApm`d645cVZW zwS{^|S72rYdFRLAjA`whX*tAac<$Q&MJc6ue|{a2fj*u*6y76*=te#JSfaG_76n~< zJf}GhVbmRb=lk4g-kDlt+zQh}qDjWan(P1sh?JJ!-2wXZzxbP%*ZMRc!OAoXvWSDV zhj|ojw*9FpBHa>AwKI4xm?>;rZxd!s)@i|jnjcMmG)9cr_s*NaNAyQ( z^meuO)dyA9P@K8pS$tmPU7!0)H?Il+!-1A{3=e`b82d!QEjk=)V)7&e`*bwk2 zfH}O*xWp1r)iycBxPSO8*W&6cr{8(~*ZbqRp0Y>Z6yA2EXJ(#Gq%~q+(mCkBWUK<* z;o}W#bVz%D)iLO3^&%_j*_b=|_F$D|*sJ#PggH zXxk$&)XR&+l{P^jJJ6aJt5>)e0)t#cLi%E^fQjb6{4IBEds+g<)|EzpAfB<8gy4DS*xox; zs*)g>-9lhS&+L}w8kz=DD`CcD??&H_h^g=qy@;n1bKmI7t&01KdvGh4mOb$Rz9FdB z{lM`-2~r?TN};qCHypznYuZ?muFn>4{lHDePoi9k#~{%l&NbCLr7Gd{!i18YXPx^~ zZhPrt2fSXvKgbFg<#hQGTP~fvfjO-anf|E$P~}W&OdhnM!Z`QElC{pBS#uky20&)t zx`)j%+me|XMQB~ET3S2}FxSVyO-kTNNsDIp5YTUtUoreE6gANfu2;VCN#*Gf)hxSz z%P-Mb;n%tD!0o8TBE@+1k}~@%>7m?80XJu7r_`!v$= z?d;G8=ht20ODc;@lqm7pHiHvy9d~R>+-K@laNu&4BGMIiJO^vrK!6oKLLx(|CR)J! zmK*|n-?6Vptm4n8GkznAb&2iXznkBnW-5ckAdip3e;0@*&0_7@V(HF53~p=-7O1kV zEbDY?Oc?;swui2Rw~~Td=YC3eLT#13WJPH`ig9+d!tAH@$AYdu*t){tuJsG})E6Oz zvY`Jk+-l3{g|dw{aifxj6^|O~Q(dqtN>>sgzg%nPc+ENFiyw-od^szOuA0F~w{eax zXXSa576h0fn{P@0`v-D*=D5v?BHDzvjL)M4yoIVqJP5GMivP z0TvftFbF}kN%U>C%n7wXr8r0F_>=6XC%W_2HPi-XJ6t*(-S3P~4~Gb_{HTyfBv~%A zR_Rw0S{|?qB#%E;NO;Xp&BbyV2tz`kNK-a@B6l4Jub*uj$z=o-%&P;ZbwmU`A_>5% z1eALajy}FK_Wxk&oc}rt8*ZI#+n8*-Cc7q0c1^b1xoNU(O`15_HQBb!Z+4xY^S;?y3~lgD})Ocj?^PU1O{-RK(v>Ef^gVeHXr z0HNert2X726bthKTdG@&_x0yNmJIYzC@}Vm%{fby6sDivAEzDKf05R~|M2sUSICdx@&2zy@%cvL-MVgd0Gi%q_W^;QVM;@U6mAszAMNoU z6c}xubnMe9&*x9w2c~ zU0x6&di6~jb>GLKb2=8X`S#oWL65wb4tmS^T|e7Z^{B>_H!<8&7tMm-Gt+F*kh11>xaUvk<#9v+-8hf}Z5 z@W{@^er%FmpKEneI&{3cE2l=)M>-G(S37h)>p$({Ub?XoFqs50v9l)=eGcvnaUmnn ztmhx|XggF6is~7vb5)BS)}IbGvOFtoaDn+bA>EtATQP4h=STT9YuD&o5f$8OU+!N*B^0kj7I}7`+HfMy8>tZxW{{O|NKVJ|; zXzPfr7tr#D&W5yczVi=o&u4QYhAzY$zCPxbOZSdjo9@r?2>7!hD!pcLwzHh4P?2yG zJH!J)YCeAjl>xJBgU&vIAO_zTko_g1vWuKok;WdFq3bLo(bo1E)hCZnva$|G61v+w z0|V(6D)AcI-DDfHP|9IassN;FneM2Bat9sIp&}0@W+Ek;H46gnr<= zL_T|xgSAo#@*#S6>kQ42229VyqhWu+76+JA|Ne%{Jnp~?LXOjl;ZSu}K=ZrMKO15- z=ILt?8fAPL#e~Hi-YV~`5h^)_qnGCgiwi<^q>WNg|Guu z3qCQ$2xzr`cgrd;uEs0qWAq9<#VMN;UbNZA=c7$0Yl$iuOF>f+w;TD^B)L9==Ot?z zTpIeA$Z*Xjr9uPZ9EhLQMxYsUv~3PAx_$@&D1TbMW@z|t#ju?xzD{)3a>Ujaj@gIG z>)=czjqUevyqdj*63ed98l*L}o}@ZbmDoo51281*3yu~}y5hD+hH8)O5NaFng|12U zzfnBDiqLD6C{meJ?Jyul^Wf4J?d<7^)z~5~F$M0%2oBn&!piI;rkCss4FKEF@g_~i zSAB3u=^zuhDQ^#=$PsRB0VV{rQLNgc`XIHaW1n`+HV^N%q>@Q%Q9G0{xM&9$(o}W> zK+Vx62rGP}^~T~zA8Vd&zewkn5@aDr)>$}`tq_0Bt75sW5_Bd>-+z0EQc6zrDIcCa zV&ueJiH>`WF^=m`e|hTRG_V*{qqvU7B(!SMT#E1;O0$>ZyZNa!m9guTf3wIoVN=Ex z=jiB(+vJ1-N8*(YhoMix>_c!o&6PL@=%jFZzxsK3E!!BX!&eKaFFNUZ6#Wq!wXp$n z!M8-nH58HOcj`R7Bmw>$mn{0&IC@rj2n9EeneyY8OoRp#bC2yzbV_j0Qzp14ex3R< zsiglpCPlewltS&5QstFCH~wzV+u%3$dUnbiB#~&bSZ#R-$@3YBT(_AzJhO!YyyjQ? zmUpK_DqQ@L(W-baQ0^?^-P^EB%Lh>Qm83dXozfcQ(kX_n&N*j%2R(D-1>JRB@|sn{ zNZtHXQoKn_YG><5`;xpgV$A~JcaL`!EJtwOQ4cJ$_4et5auL-`F|vXW(bw)p(^-(f z^_GZRAOCDFEGgYKR2nUzA$Gz6G$TKX929kn`OcetHD)51dutNuf~4eskSvJNlu4Wh z(JygV6{W%EksppD*vpq&7x4+dxAs3RrnI&9x#Pqm{pJ_CyKzoMZ*X}<;xiWuuiT9E zPQ$4*@{h9|T-}?Ryq6%BxbXIaL+fA8>$HN1ggjl#MCq5`o`wg`2t4_7~TQw@}#%&bL3KPd1Bp4+_g*sZ#=I-`)m z#pL$KL|Oem{>5%2t@8XCp8UAd_j-LyUpybg0;7d(KW&Cm^Z_lJ3|8GFZaTyx_j|N> z5I~ei4LIV_+V~i5e72W?Q(ft_KclIrxowQBi{s&i?~`W|d^x+odhqO|Dq>3&Ck}IT zx8VrhO<~~oGYXhonm2Rr{W~etw^(PDateV+`MkpKM<1P(uK%FYtMg5*?8BqTcAWd$ z9kPQ(ySO_Gqs3_O4zGgH?V1J-E!(20T9)>VNxQBt zpO-2Hf<&ILW3NHLvc%Ii&qh3tfj5UO?$8?7mzM>3hlf^S-V{k#*h|i5p}f9{<2eGG zfjGj?>OFGHROIdh`cj9xd)4KI#&>FP!Ujv1TCTg>bib+GvR2KgaY+~vUi z{^Qs9PxMK-`M~IO5q{|?m{iRDQEZftq zAlWX8%#Pmnk947}f=c@aD?ImamDbX^*p5229O?7bKXTg9Yf1s^NF>>-_m)WRwnQa9 zyTeMb=lCI72UFz)_lWzKlJ|e>YQor|s*VyHt zVm1a$rDoJ+#X?K3i^$=Htqn$y=toF%II?Lucn^T+pX*gokbNOfu*BUl$}DaEy%q_{ z0T+5`!u+e9QZP~=s(8D@@M+X@78Tq1w|89#*)vdmGhIb@qMSr>G)SVk-Cd*md0DOtK;srtwg z1fnr4=1-9RsQaQb_?|m+L4nbYQe$zCOcXB%Bt9waqJ7CuiV=ALie-gIyxM8 zL@fTWo8`Kq+Y?|ZXU(*c&Cu6Vqp?tru_pi$=wT_Wh}#h4tAiIUGMQGo4_L!3$&VB0 zK-V4r3@7w+Fj&VMv5Q!Dn_Jo=^CC7?0-QsyFzF3Kt?+I~!50Y9T&ds%sG%(x`?i;U zW^_cm{@Eoiqgb0c#x6sW#i3hHs&S*B$=_92*Vu?zr^a#Dsb4Ocin|fBG$I`BUf~6T zl|yEC3S*4Dqt-)OF5-iaO)&^m7(C5;R6UWU*pZvmUI^cZqg4zt^8Rl>eFUbg?CK z7{m5&Vtt>{1N3LN;RxdpW@jg$i6kREO7|cdP?E20#nh1TPwV%}K=a*gzntT8Z11yW;BQ`ZqD${=3w>um@mTEo z49t}^E%;b6+b~rkCF+FtmEZx03{o_K8{wf%NAmeP*eF?6OS~s{Jcas8#`1?I|KQm9 zBP$+MgGj#N*F%o#^{+OAi4^q@_p`18^Z1 z7zM}-kMT&a>8laM)v~zLBA5C%q8oY9XRucHcMcs3#!sL(ROo9deZ#lU0qfJ8`=|Tt z)z8KWN1zgfT{sHT8j;B2KgGpMD#6bBT9zn5&eltJ`WyM4&QO2&w@s@DM?$~HX%B-` zQr|+Chi@Zr^rV7$f{u$pJmhV0&IE|y;uewU z?TREq{xde7$M<$mJ3Spq!Gn`7S&7I5oJBkL^L9n1yMiZoNUo#x!{Jq2MrVUuP^9|; zQuoFmvF;x+Vhx}{I8WxU=%59e$-V{W7x-49WgbV1`cr;_As}zqew2~Gx zug+69Q+$*=<6e|6LYZJK3_Oqwvo`q`oJ`Ml&U@Bv-h4Oic+AS;qrBZcDNq;cJ!8Sw zOD9PmT*oKJ0d+3Y9Qc<^stmd1k|LmT2qkfumSk`&EuP+Kd2U7oZE!FB(iO)ri+l~| z0FnvnUFHf^lnKgZ;%y3+scs(&O!z-B+5adw{#Gvs4lYJQ{2@HN$G$s<_C9oWNv{a% z*MctM=_!CesMxM>bJSn;FRDAJYl%#aYO&`%+b#hXvCIdAil6@~lrUu3)}12el=jV4 zDoxDh6~DW}259!74NQ>`Wz^3YWR~1a;&Eq#XwNEPMOPzaQc~HE`?SRMk2`8=$Gafu zQRuW4bdqwUtg52|VK`+;T>=84>-Nw~;7f>;R__~kXlbwu+S>E}sxBQCuXMysmgwX* z8$kf1()5w=O!qNY-K8hkcfZbZ4s~8&U}^XYW*H~^hf|*Uc+&%mhIyH}RLlAC?kPec zp7&^vz!W)hpVQo-y!78YqC#gG#><=~g5dZFFlP27y-7VWq8QGv3s4C1>4P9PjCeUp z8u7~*2Qgn5A>Amw2L!#rvc$BuNg}MQwf_JIw{hQZwNE*KzcrrJa_Rf)XJ=p2{eFxO zG=l3Q69hwkA;v~jv~M9uW#04Ma|-K36OOHps!9!&o{Zw7eLj&4hT*}z$-^LWDJ4gj zkfNS|C9q;lgqJ7OJQ<~kQd$&-@6sHJxQbk8SW7Kf{KS8LG$g+qq3C%=QaRRqH{?!0L*A^W_;RwSWe<&()MH78^M00+ofxYX3NUK9! z#62yiqVM1jN2t`54lXN?Z^~{SFckvN>mqoUZP=+@D&S`dyAD?e=I-`Lo*nny^)u$h z0=p0EOS!c0%ig`!DHErR&}Wdcs=RNvk-_Y$HP|h57p!HcjbY&dZA#~}&J4IN7Ru&| zw4F_&qLRupj;W#|QHq}R1aZlgmDc86Q0{Wnod=tD&2gG%zHu!Lf_wc8-F)KrFZ?`Init3T(JZJ7dsTQ^jg5k^2m z*8TSxw|$`{$7$>M8jl^@eL#}ioB4q0#91Sw=*zz7oYfacp;bYD2i1@()`RblGlD8< zZk2C(SsfX%>Z1+)7PY(n$Uk?^ZbPr)Rp3}^j@s~zZRj7?iMQBSv_%OsP!4(z0xcsO zryumpjFlpl2;quWsg#)p5nZy*iGYSg9WRCck|(Z-de^s*e<+R@^?*Kn3xPDAD9M1% z8hioh1%{wke70e}+k-g`wu15aGC*Azavncqyp{mYCLCOPsth6fFtZiy)8+F6&ITCp2*6PgM$jD`NfWSWnTJ!oWJ`bgFSE)$B=$33pk(NGd(>4@732`?+yTME^sdy zWp}r$9drJCH@_$i1NS|T+zhrj-I|Z4_px3ETmS3_3)fB&09n!Gpf_aLeiQa7Qu_A( zQZ0OR$28HkFykNP60iGrTrDg*R5|kVrM`<9_q-e&gRpGn>Rv5#WS$lA^6$;(ZJ5F1 zVu(~YjOZdDWa5nLub=?$#F}QKI_zaq{g`=_vEfoZjCv~nq~NJ_DT~DYZEdPb+lFQ& zBXQ(tB(zQ}FyyW$ZrRhclg`GdzKbeaZ@&~o{@^{OPvfWhl8W^wM=D@e3~`tW(U*_B zwX&hM*{Ev!@xl^SjiUO64{_@yL&8A_F}V>Pr2Pw&Ao znKbU}94r$8UBea!Hf3D{_7{N;pmOn#$Ww(pwz;A^Q4a4E=F z&l1BBnopjW5%wchgsFC$x1cD5Obwo`Hj*Ma@JzF@93^rA17UG^d>oh)d;`(4kR5no zxyGfg3SnMyQN1XT;KOvqh1JC{biW&CxJTx%*|JNV!UDsejfS9AdSBzX_8<5cPUoE= z4W{IHqd8A2m0l|&M~hqtZdDiojr1@^{uEP7(Ht`++8=5&!!gW1JSY7M5D&l2B};uN zg-*p!fjaqB9W{C!z3jwh5tXnvfnu2;?Ret()ZE@R($#T-gUJF@I>HDw0>hg)n<7%b z!e1tH5-BSkqoD-#EscP)qD1RkkpdD$9`tKKeyCb!GQTYh&vX&~(svOYAVG%>=_s^z ztn4{h*1^Ho?Iv;~l_-de=W;j8iHuK>faNEt$SnAgRYG(|07^HrX$6QiX}(t46*b)w zD=lrW1IknRuMTH4a*cbSf8uvk@oYGmQi!K4bM_F8Rp2o{5K&R!0I zO2%D>7oOs2MBe$eE{W}zos(?z)v4HBk2$8635v?mvCpe~>Nu|D&AjNF-q5jFbAxN0 zI1NM&Oa_N@9!yq$ZM`RV=7400sA1j1ky?rOzo!%ho`RxPGHppnKzSM)RGTh#${%Ii zgg_x}?CejmAiz5maiPxS111jtApU5c;hqWGq}j1b1*uApar|8x!2@b0&EPveCa)cO zw*gd78MQ0Sdmsu6a7%>`wtZ7x+lC>q)W>($R4{%Ee@{dF(kv95sHYLi-`( zA+Ys#s|)9uaZo#gMM|fAF|R4ZrSs-ju&SBgy|kPomnYv0Ku+J0sWf9=z;iPeJve%Zt5PUV3=;t0PAy_8=1{%6*v?`dvXnh`F5Q9PjFB4A>RHAuiV z_9)@n!ta5sr5oyv#fL$IGEr;`-&u+32{cTw4ZZ4Mn61+H=g&V zwm)~eA3S~tFhV?^>0%7))!aU$KUJgKgBo}D&-KV?n>aU+GvN}dREzxBRhhSTG!^n= znJe)JrQDFrE&A&MIp}s)n~F0C#7P@JrRe0c{BLYDqEVE@R3LRM;+25NZWQLP0#~KSPd1UV z*69T(`oC{(Bo8X*Ku<8Pj5(oPjko!>fpsrejO!BAF}P>^y2NnTmSVL7c|bYpe2|Ke ziulYpIi3!1p^w&k%sV9`62MN>*ziIAn+Q$;H<>bkGS%Jx4kVt>0GQWh_wI(?NiL=(XH=1BB+$UEE3is!URauYtocr z-BPdpI%MpB8>j&$ud%p(yeWzvQz7M8v1LQQPp15K-=wBhCX<7m%Wl&(*=X|ywb}t)$@F~HbyG=&!{CZbe_+Xay{g!NA8UV)#hYNh~ zSz-Jjj||j>3&BFNu0|1zaUxLm@ph7JBfRD~FrH$`Q&y%NN5nXZx3Eb`kVr-S@5BDs z1Ue!TFvQ;5ccv!$>(cT2y<^j7l3S^mVdHRkj|k7=@eoY+KEkG?EcHq#(J!v5A&|H; zEJC?Qo&Pz!s=%0W9N-O?=xR(1G|y@3jaj}o9qbv~UlQeDI*-!*ejsAPngn&P&lR4O z!E9Lh8gpZr{EV=hA~w&eUr+4(omZ-zIb&$gmW>FTecO05P2Ct)AQdI6nSA5{Z{yrt z4DoN41u3GCMNQMIzzr3d%CmWQ7`4?+O-52@hRi)aHyi5^4B+U^C#71CoN~{~b77-E z;r&KDSB$YkbS1Y3S1q`O=hit&X)KKXcbhCyqbm7fmpF49!5$Yo#8`Bb4`MwL*#&J+Ehg6_zBipiozUs^IJ3&`7W7p`O9hFUiG8yd79q(%L zU7Ug!4#WF$w4ew$X?YA-U?jMi{X$wcz|{TvcRE&lNbS~PrtF)q zw=ts8fIi4|ge4#siC9T`{GHwI=g*?P1slY#FbP!DwE$$8Ui1;<=vKefLTg1{vSWA8 zFnLH6T|1KTK0Y~L77Akat=!1zMWt(ay(+M)AcMY%-?_hDr%MM1f6Etw1|c$VbkjsB zDzqBcze^Vh=W+CRRsQ7jcp3Uxv)I)-ppaLW3tIVu2t zaW(dP^*ZOojK)mSGTufd0p;y!XrqG)879*ljUweBkSiI7y;*_P?Qr?3i)(#v<$xUG zpD7mQ5knD=Z>bbtlbVc0_2rDcSk`)JLK!VKB7g(ouGdc~{l@FOl_e8UOXT$3a zI%F&hEyw{Q1P`O$SO3+zwdfi{AFW+Qx{Dpj_l-E>{X%vEoKIMIUh(Zj6JqQEYdo{bw!cL6vsEt6^2mL)S{X zN3`E}v=RgtA4FCD6KELuGGA%>YM|136s}Ns_Mq9C_e@Y4;_>M8iB{l7B-1i+Cc(pc zU>DprkTEkn=_B0Ch)5)z6Dk;&Bi4MfMLn!!?f+cl>H=};?qW5@5`622Wf=KG8PB~RC@B1>CIkU^#oXiIEbg3czR$u;EZfMG*q$tX@WW;A9c1@VUP zQjew$%(sZHi&gUz&NLaOJ_tYR`G35xcaN`t#n6FLq06GUJYxr7RU}xhcH;Z;nazIq zUtAhPz1D5^y?^x+>aOg&_q*M5`go5{HFV10KmhV$pG+j;G|+yQytnEPScWG-Qy+QM zcm9M3)Ed%)Vx3Gu$`CKDeE!&qbpCFZg4f71@c8OWzU1trFk=fN^HLQW4USIf9h&tI z4o%nfjisR(rm;wN-gu;L)eaKWWw}pm%=SM>dw6!VRLqiy^}OeN_rY>9cj(3N z9q2iAe2-?PR^pfq8ue>>yRm{P3NIPQAE;x1NenS}v?pzBIOOu`^%OQ`rU|4yLS}Mh z4-0h*3PBp6XbI6#YjS<@b|tw_k<$*hPn`~;({hPEE8S?!`X5EpCvf->(IYyVw-z7L zQugRj{em`VQkbd9+yyRmF4ORv`(9A*8Hgp~>-dCQ{cheBgr zecJDvHl%*;{%ueF`+>O;ZMia=63D^qxqt~onI8?`K!T_=ND3uv3?CCcC8|K_#60g% zt2KoY&Pph~k6_7DHFjm{^}!_1t_GZmH|mCZM&7)^NdLDD(|zH?J@5f;cdxS^{KDAu zFzo9X2o3^;G^jy}Uj#>)L1HdII=*j0UATO1LO&pv&G-P8vPL+lAfj7<-b%}!cPLKzliB^(bwALpAO_WLqnX#3rgDf@cLofITK_K60aI`n z|KbqYNdI*DRX~%(7zv?L@F21BZ?kmkXY-qE^d>Z^%q`h#fpx{Ms?n^0l588Zha#mY z|4OnJlRcycIs#lH(4<~Q1fG*~RCJ31Q>iT1w%TcA{jS#_K=sd4%wZDr(qDQiOMb>t zHM>ykC8@wX$2ps6R6f9B_wPW>)CFOq1fjs2Y)Q4-HOgI`u41!L1p1CXa>Sv6&R9^m zM-t2|E3QZ*!+8R#WKiGQFy>Itby6w!#0LrmmEBq19r@s)Cpmdhr3@hT%mrxs?f`xLQ0k z(9M!YQee^sby49$|Lk@g9Y7gaZ~w^aENy7naY@t_6v{VOZebs=O+5!hX{$F1xJ z6R(N;0!#6eem6jE<~vNN+6O2Lz6UMyQN%Ty-GIq4{YE(p+8Uf=AmENKR%gx}uK9(R z%&zEQDU1<^jy+fXvMDaQAyQMFTVqWt{3f6J5`XLJxuP=(jwU!)ho(Ca)gL)~z{O*Q zFDhp%YC_clN{g~EsJJsxCS7nNG>fUMgSt&OW|0kn#tYyx_&uYI(`Fln9Dh^+zL}q@ zyToF%NZ}Kv@g#gQREgU>=5{yroNiAfn4jP)VrA4VvSmk+04^6*3tIa3Xzr-h%owY` zoUi#JI0qg5LQ%``w@(tEh)g0NG2dNmGUvWQku{^#Mk)mil|XFLNN~^-P5D+5Pku>; z7dHhr=$pPE)mvQ*rmS8i5H3NPjg%>~x3eQ|;Pb}yY1-3Il~CMx8jkfIIW&CR(YoGx ze>1rIe$mtMf*~d<>pkSI4|Lsn&ly5WYwkrFAx$Mpy{`ORK-F|F+h}zn2d?^3b12c8 z!1tOosnEKipm=}>o>T%lG;V}ZkByVE+>dv|kJRsUXaxZ9FfbVdKJ4;Tiu!Kg|FX=o z7de{gneG|&6M`?*k4*q53It~}9XG+K$P#L81L^gH|T zt!(uT-&{#uR_EWKS^gEd#6a(2dE&YQ!t1{!>Sm3Ln14|?yIbu?ub9$1Uxuym)gTZ{ zqV}HTW)A`9{|9GX{i5aFPF-^$i$5p*UM9M#TR+@J4WMTY_}qVwEuICeox1pkRWY)X zIGe=R%<-$HZ0_DS-1g0MVitw}5!HeoB)~onyzl3?qm#ia|dj*zXuaazEzBj26lYt>Ol;&4u zzGL020NrFb2iABc0u9}5F&g-=-qa$IO>9ne0Ui)$JEpJP>lD=4U`pW>YS|Bjc^*Kn zR9toZtlC&`xh4oxIGsI*!n{+wso;-VKU|QW8zQbR$hWU~44|g%PQX7yh0AC9}SDb!};GES9fC(9{eW zA@_}Q*B1jzD!TSP`i~gO@mi}LTHnM!@}{lkgKd}(-FW1w z5Z6#RI#}aSTR8_@n3^N?m|dlc@7{FZ2Q24WtdfbYR6II;h^x-<$g{T^^dEsu(h^Ls zZ;j3MG-j#$OKPaEMB>pUVY3)LqKfZ^GZChOy)e?%e54IqA7XFPrCBW8;Kdegk$cOY zCo7-bU$ z`AEMn>B1WLRGU~o4Ux8#KIY2twX6F=8$caACv$Z3g3#d#;-~3jo2auKxN>A>p`=*a z)bVu!$H6f2usHgGDhS)5Gp|dT#^D&u26GW=ttgbY(|k4|t=?6dU{eJ|?a{ldyr57K zURH5cTF%K4J`&)kaIIQXvaWmhl?#Y=5XnyUIE-nJUm>P-jPXk$dYY>a-=HK9_{**k z_a#Q_CyITAxbOX&IgSTgS}Sp=TVW9?G(ddn5$5?D=k z^FG#;a{gP&_~v(v?e;x!Znb-tg2b6z=NO8){b%%8`j&<-m;>C&z;dBM)1%VF9uKYN z5aA(p=}AGR)=KnEhb(i+4;p+bLUh8rhC0V$S*u+b{W_@1gRj6ImmFh55DI2=Akz9$ zcb8zoMt^>C{T6IT!&H?VFK1#jv4%%x^wpU&`@rBPcH>9!5eJ$Vu+Qkk+z*L2r}qk> z&$Fj>CP=-MbOxiSLiD!*PNJqpAvjDksj^NehS~9LZvSTa%%{Nr`#)3Gn$h9uwYZzS z!>yYG?<&eEDsNT5_qfWTYkBqc^JRVwJrd_47QtXQKlVe=RR|MWJx+GU3CSbMwZ!?t$t?xAw$3-Z%OsssyK1#lMKl|+ck zv$x*X6)!zWGSXN2uwM_Go44?JoVLFH%LJ$$I&O(uK5j?jY-(&1uwna{#5F~%#K?Gl z$D%}<5H|5wmtzZf?fPi;#wZEfFExDUXruUecC;`%x0(?IRw91qp3K{Jt+bBf z<<%v{x&7j}upEP-UdDk1Xos({3V)-rS z)h^?2E2pJ(yW(pHpWr3g(N3%!GlCC}S6GWTOg3!TVZn~DRO}p8#6?|&7B0%k{K`Pd zgo$=AQ>Q8fJR#PZpb0+_o*+qcn=nFGlGt2IXtB)8!!^u*5pXvzC#MzUv`>&?=fY@C zBS3|fX^9?=ctF{uS(EG?z^PU^ty6z^ci=r4lS6(-Ixm0Gj8YXUOg>qF(Be%v4w|i) zgT!k_82iRZ>{tBLyiMxSx_FW?vV{9hfB#c!_Ro>3nHA`IL%Rb_A5-? zDHJ1&B>MODQy>;j;3%u$RVQ-iOncOw<98sPjWrmiOc^`ZDv9r8nKq>(i)T|Orny;a z5rXaSuFmRy5e!U*j)J-*?u|(=J*b91Y++ldyyz|_W1NzXzh&Ur!=qBmlv5gvuX_}o_PA>Q)j2sIrobL7a9Im}{7A;YFxJ z@QVvDOKNg_C$J)DlN>HlfhJ)ZK)9;{LS419*BdWZ)f4Y8a?P;O1Aeo3wSMgo+o?q zdT*VtR&cpYkEDoYZygk*OzxnA3&Ra$8)&Wl-W^+6BGH`aT7IZ=ci~$jADb2qoJbLq zF?A@}5;O;JhUakNQwAXtG|q6O#@d^jqSk%l<*6o;G(=s_fdFfH6KVG&VP?p5USMGm zOJ{uqPj*c?EssT4Q6nZh6LX}N_lp{lWqAM|C6o8aud*zy7`q|(De&Cfl}jao;zi>4 zE%=#IhxX5e;FwUG0!1Ck$goo{*+HZH>V6c^ecd~GRA}OitjlT>1Hib`!hJpvl2t2$=N0)!e*$ne2KZ+c5Vsa{Bq|x@jKA$Bafqw40(M zmvI?2k0rOOentDF>x;{@&G-C!xd}FwWT?u9XW)-(RM+VZha4^P@-*WekovC*KDUDY z&*DH!N1j;tyt7tU4z)gq-(dQDUQhR8{>|R^OYxhgiH{$~xB-S-oj&V%T0oP;ZkDZl zPG1MM!}ug=$`pF|S*rGR8z$BOkFCgURWTA~!jHJvO~3PS>DSE!5&x?{zOEmB5ut9@ zq9dOq@%kzlb-$R9Wi;EyqrjaB#&ZVKs_P~gvDWFn{#S)cxGHoz-++=}X!C$))FM|7 zs$MbJ&q|z(TVXSn9cgem?|N&b=M7xGc$`j~AU@(eeMnDp(OG5qK6<(-xp zM#BFLfBoNJV!J0qJx}^FGKSLRGI*a`F+~%@X=ZM%YDDY9$)WcF|HFt&w$J`OWIJo+ zt=yX9tl%+MqU>^gAY?F_XK$O_-C0lY^I_}P`*7J&pEt|_SwMKm{r9?WSVS#?X~3*Y z%IHiM{2?w4;sQRY;+A}J=U)Sv?R8WT@<>{ z&nFcr%QF{hBPgXZ$XeUKT-nrlMAbRb3Ymqa|8xXgt`CK|M3?`Rg18hAi%cu>AxHTZ zZ#_}`V#xb_eG5?5AyebmNwe?$LWH0uUUS_W@6^gO9Ko5zg69D$|J)TAnIid>yQ+~C&-Qd@FPDJ&mwvFYDtU@rS@u3+p##hNt^7U}M(GM5L-Iu;E5t+5 zAf;Lxcp|iqe$h4n7L=$smH6ETH68gG0v0%W>Z*M;a-WvR!h*?6H@5z7=i9wW{~u=dE-fLG+9wS z?FL{0Z-QbrP=}6kGXW8~H-j}_1z;WWifwBelK}^m!GcQZnX5&)jzXa`is){f{_;49 z4R#I7DScWR1_2hFtSfpA9H=fY#~m~Q&jA-GgcYh-AG?wEr5(mk4vH7PJ;jj^h3M8J zW&_vzBbHDM$_Q3r^9s?YK~?3;4262l)G2O#L^X9VPlA|5x>4_%iK-R?-c8+&avAZK z2(bUOEFM|a9{n%4FkAXd@_uit*jV;G9ik(%OQDfXwX^XPeTxJP$om7UZnT8M6y0ps zwI%15rX+?tIl)~3*5wXSbxS2j#o-~rAGFJ7q?zQW%XiJADuQmg_?aLQ zJ~X-n%Xk!8N5Vr<`HSAOU9*{4GtF?;TtFvBkR!;~3+K?YRI+PqGFFjYc0;c^c_Of_ zXwj*9wr#XeB6B2OFJi8`nBs}RAD&FT!T)p^Kav=Pchq!il( zzR`_!P=arZ(|E{>X(FD znPZtnnSuN{!@u2|8iUU|ugG^!9cd|G$-cR+gffPR;guco@( z;S$gGQwhI+t-5cwFKFv_>O-Zzf<&mYXI0`lr>eCSS)bEvJuhD{)nKQ6`=1v()yBxj z-~+jX+M$3#Fda{4Zk(IEMe4|J$8W@SPV;xRiGNTR^+8)*GNm14X-E7B&+6f?jGbVN zGN9X;O|Ltj>jU}EWkOOvfaVf-`ZMP`y;TZ6E&Z?|a*`q}y{W=Z4S^UWyP109T z7xKAFO@4Qc?Ye}{dRR#d?!Qy7w(U|dS%+6+VakOm)VUwQ??L~YGFf*j8FiwuEI|5L z&Hxc9#d-x5iTr96KDNI+FcYB!8rbk0IAdQgePm<+v#y+Tt}9)JKT)w`I0QmqpUVXv zYs)%qX$EO%fz=B^if1E!l<-)dv5K{)_Y_U(}p zG~^@(W`TW6FYvjGmmab06K1>6iG_zP?u9l=CLPpc2dvBU26N%3F;5IB?heQ>B2x!% zEchA&BFkc+B*)Sc--~xxjI6)L{;lLYpOeLi?K1OWY4=sTwEDGCqjsx5?}X8?*F5FB`14|HG{acS zcs_nj5CB=IXPc;J3UGk939Ke;%rQ{B9mR<7GtR=INf2X zPXU$rrD?F=6Rxt$C`2#yuD-Dzh%o^+^Rn1~-=+-N`yk@s|>k zu9mz?@&pp&{#`7PTyjBaXGzihR@p12+Jtc^gE8#!W$xiij24<6okBTSa=S#dH32&9 zG^Bs3y&)eql*i5!365lYv9XYOA@Fhz5U_hCjdHQ+Y-G_u_>bQBqs#C=`T;%1u-@}l z9(63d-6q)bE3$&eaGvRs8LKsbN^G&-m?gGFe#5RMCD3NnBQf*dJ!`yD6`AcM&dEp> zTm=H|jiI&&9(ZqbFz(&cf*Rs@D0=q;K8Pw*c!FA~kB?a0gTzZ9n4|olxJ_06Ren@K zK|unkAQ3c9h&!HC8c{S8AX>lh$u{B)9q&cc3eMI0ayodUNubHTuk}|a2}-IkENhdg ztiMNVz#7Tu*o5!C79)LazhBe59_j8EC?R<-@GotCqGeTtIP`q$5&r^+Hw}F&-$-t= zrig;2QkV{+~uPxwxW#kFq}K2X%jXQg3CiT z^88dk+zxFE*dOhWz!%$J{eAy`sJ!&l#!#>}9q8lroEC5ayQ^5q2yT+Y^!ww&643te zR(^Q)Gh=n@`LGcmrjDNbQbm?N_Fi)43oH6|r(?Fx0}j;MENep@{Q6w--Lbu4V~wsg zY?E<&FQw`4w81{so?;JfV#mG(xmYtiN1_099t$c!z2>JfdmRmr>oHFs1%>YIKC_5w zkXu0oQ)Baw4Bi#@wV9={NjoEt{7Vi+CtVE8&7v~3#Gi8-L20-9M%uSpRtn+he6a$3 zV3aFC2L-Zypu>Pkv36G9WBe#n#9*Q5+i`6j(V80-XDv8&y@|R}A4jPz_*NknTX025 zLW^D?acnJ{AF>|0mUp^Dnaz3PY@W7Wy=WwH30xs*WEQ91?jstZoHbe*g^98hMcJfi zBx+pzsoMK~$C8s(ku5!z+qf6%6BT^IP}UwDwf7 z-o=mQNd*#62DV?(T*$=`wI@x+4f%wNBEtNfQ$zPZFz2Eh8P}aw&^Nh zgXlF?pme+M_}db5Cu^7=tvitFopH9nVzaa!vG~H>O_pFkAlg;W!q76CiK5mCEbKh3i?;< z4P)H5Nsgm1Bl%}>Q5XSX2}7SB3T&e-31&aWVnmdr!9PWK2CB}yrh{>x!Ps&@(eThX zrcDBdX8YA4HT(oCW_?n2_rwk1D&P@?lMC2pOo2V=ZbT3C1r1 z?wHB+bj()Ut z?N$NbDTjA9sn)`xW52BoS^&km7GuS2l>*0IdxOwU=03%f?{PD(70(Y)PG3U!pe3!e zEO1|vVQ!R>Jp-}0IYqJ57BZz2a`13?0x3g2^Zz zgIKS0>S^kFaIbz(GzELiCjF4H-|S@SdY`Y}pYDTYTd>h>GHjh6C1%bo9S^Wb(X*W$ zJbBIz$+k;Rd9`T>DBJ~RFD079E_bta7bo{EMSLYlgz(F96H)a2Wi1L-6j#n0j-AiP z?v&3?^!^V3!$3U0`ORU*4%vgJeh#{F*$}CBX*@Ehj749$aAaf#u;|N|5AO96>ki;X zf!hu#!SPizKcjWKm!I)OlzulYi{#m(9qo zbGz^Wo5)h%JF!I>(5Si%7-3lbR@vw=L4SF?ynnT!v)p!r{^z;P!ptu|23cpc<$?c|?vT`Iq4dupMmD|V zc#4D#$2On_EiA&m5$2Us0QhvBuG4k8PS?Nr+6%ny1%WxR`l2B59C+C{|EJ$yb$^E8 zs_!of@Xo)#ZjgKbyG3~=V=e2EtDqMcZ2N87&i(jCb=2qVRG=rDx_=UO%c{aqRN={J zv7Ibocbh1UlW@>PBXkb0hH>Uol<{Yh6ECeU>+bA}I;P%$zC_Aox?m;|lJ|N1gqoL% z%?xs?>@7%;scg~SCN*&vRpA(FDNvB|>VSxt7 zA!(iFoHOW~@nL$Q;eU?0a-yr;yMXh`pq;CVm%_qNHM#|5VCsgZtD8B#10^ z&x>ptrXCai!ZDXNRfojmfYc> zW&9j@bT8hAQc+C@9h{+rVD&a5@B#hM|840+!LTa0f0y!tC)3%bL8qWEm6SzjD6` z2WHnY4FQY|TMgRl=}(ohxfai0ZZ8{YiU&nI+QcMNMeM4? z4}d2@i;d{Lc?xFmKn7+V2JB{b=Xf~fi6)cw+Ss9nK)#J0mQ=|`HjM93!uiPYpJg!b zaA?#wmkDSS@Pb#I&kl!1bd3xT2+m226*@WSCV%B6;~8U^whk?Z5ZHPP(S^w1} z&fOh6gjBqv&gS?uPj4P)ayBEnlE6#id*BQ_(RV)wj;y4P1)deO2e#8^2rQFX(+)mzJnBGh?v9p#uOWyFI&$UE zJbwi(>K?d4I&TEP$35&WWS>FsX2`fE9DEszNYux#(XQ3R-=SrSXQn?B3W~;fTcpQN z$tXb+$e@66gSi@dO_LYL+9r&f1zOt_oKODu)`)hD@GX*yuFw_~i{TUW7FnW<>mzkh#U{`znKy4U;bU;WB=TY6uQ_1w90yFdHv z)6bHw=)C%{buMm*E$5_XQ`SL-4_sKxhotzVl3y_)*?@^BeNJ zyT3jLfgegS=9KOKgkUTVyOj6u{eQ8;`K{Y;4fs`Qqe56p{jX65w_If0s2weDf#1W{ zl&m&wt*T9c!*mU3>VC{YT?d)l3<6sv>@|YJbt5vAG|E1(mOXUZOB)X)fJH+no0KKN z6GnGobDRRer|Wc`uG4k8zV@{j@SUH}_fU=vldBx|vm@%NVCsBNcMO;p)qlPBeeYsL z{#EsFb~wa@US%)fT%44mSJauGBYiU_pwW_qnG=(Z`u;wyb7D0nJqa6+6&wgC7wyp8 zXgnF#TNI-ST))u*`Uo=cgb71>RATxJ*M|G>IE8~f%G?a2?*!(=$PAGsNG@iPIe`zmb;jH=O3{Eo5S3RN+}aX1J7KhdVlZQ1@p z9t;HNT=NB|S=)>mOVEUs+B1fu?sC}bG-Kvsh@(ulXXSaJg=kBI{-JA_N4720J4xG9 zLC~hmW9M&?!wTIAJ%7w?7zsRLg5V8*yeRJyL>n8R)0qILPuz8AsX4TT;9H>w)(b|w zJL3+t0U0xoiB`zq%m5OS6F|4Y{gP)|4fwFiec_SLY9(cS!rk$&DQmKy7I+m&4JFR= z5peHL4o=0J!BM3MTB99b;Z%W)^<9twGQ4lp1>k8p(@lV)g?}7ONR|2n;of{F0377u z3>UyHB1{{h6s)4tGC+|!L$iFppZq(^&~hfS`WdvOPt<6{r+@{^ULiV2*lB0IBP8*W zGAL|Le9A2A$7ds5$va|!uL$xvEX^_?3ivaX?NZM$HEJ|=gV%N=*-<#13X+vutzy>ZA(5(k9);*5jE z`Q)(x0zqgW6YO4(G^J+2dl--AsXBuf6alzaXw)B%GBRXkBbw~!r>qJbVD9{ZW&2rI}6NMSk_mVsLfzN?Q?|-Bk#*THNv7>PUjEY^6 zsdLO1Q7p7e8@(f~=kF&?rHrLuIHm3fY^`NGNkibX{DdAw5KXv!&_69KiTQ=&5P{+b z{ok_RMz|hbs0&9T_$4#$gvNcqu#nzWg1vt|8-Ia(B(GAJ`4J4K_e@D%ig^%z;Axyu ze8?cQ@xqek{Ezc(txn=dHwqA@|iq5 z27zC?b;s@dCVTvU=5yOdyTEg&e+FI`CEecJS$g&m;10S3J4GW+`SJ72&)vG^!H-hjOJpvk-f=fw4HVUpRUt&x=z>W`kGfd$aQmn@A->@x5ks$ zo#%UH=C(O_dsVwLd~?10{#<^qO)hg4EPMT6<-h&jzlo4LF5gvxqdGJA?4$}Cw;i^9 zdA8Rlv?Id7WEIuPK}~hXcT6GzoPS*NtoTRMtmaC%=gR-6ZYQxahq_F{5*0G3fF}{j zl)#eCG1>3??)kv@&PMsv2475XTbkqXoOc2<7G~OyZ}O*HT3eM2e)?U-WI?0wOg|m> z&2o~*IM+F_yibn|f1w+)fd>tV*9o5Dnv*65xKlt!(x?u4lqqNx@Z(;NM}NXn`LH;N z`eW)ZO*Znz&ZRWn(QQd~1TqK(5};isywST+pHzMT&frkd+9S!~PkM|=5oomlLUPQg z-oYr|S0e~y8V7>iSUe15f_Lg9(*o9lTHP+DkO{X}$;b@W5vRmluhO@%FpUXgR$XMP z)w3#%aR9omjFJ6P>_W&l;eWuhY21V+m_o2@>SSs&f>VTa+*SajZnBsl$9J29s$4xB ztd?Vp?mCTxqoc@U3u44-Y$JBfML;^qDNFGr5_E~~WL*=F5giznH$p(}%+gsZS03K^ zo>Z0GrR52~zSG)o|wH{PZ%WF`n*tM>_lL)FeAy|Fmh>8p*7HWo=f6ortoF7O6}9mf*9t z6C~FJC+dQOs+Wm0xqpgET}ClIvYm@mpPBNr_Z|Vjzq@Wa$YX7%L?H#xanIV;pTgn!;`ITkA(7D=5>`kfuVkfKr z4Kf?t8YvhhgS5jj-6k7iMI4ao__GY+ji5z~UBnyy_r9iMTz>+02=a_`XA$I-NXZP{ zWwDDI{U^=fsi?eOK>m%!2vV_n?rmI zd$3?ZeOQcwMF+0OeT-2K=K|wQU^C+Xg#z-9!{lQB>;*CqfE{Ugk8NAgx@ZmbsgC2| zWt=;Vl;0w8c7JxJ(suY+Kg;M_=dVR#amu3TgBDzhKt?$15h!t8Rk9dalwAP3Bn_N- zP!~JkkfuB2z>+o?7AQfdlt7Gv`bK+f8$_ye3O2HHz6tFdwQ&dM1*dE=60rF!+IRdz zr-azjlnFVmW=Vb=zrH(nqT~kIV@S(|r#qMS~8?_K08SU-`?YfO|z@jY6AhRgsP^;X_ z(<3rdJ%4x}TBM~PM`m88`e~-SGnrY%rIJe~2_^^<3xS9LVmYRL-Bdr-Pwn^(fZeah zA%T02A9ph~-A&a@O<#OXu@(+I$=NrTSU*&({kGU8r&y77by*=B)LMpAka$%A9cs_gd zK<@1&pmTS1kN53xKRZ23UI5=}=yb>ntZpt#veA<1;99{#ti)CHGl)OKE|>0>iebUm zntuWo331|$oZnbqtdsts)nbO)1s=4AA5F4{<0iZuF)yx(DmJ8ZppS~?WmmuP>bK>! z`+p(tfBLWT-gN?)`NHj&r~k3oc7ChXTA}Cd`u+E=6Y^K`#*1$St%sL;PDBEy@Kfsi zb*GOy4bs`MW$)jpMS)e z)1F-J^6KDiK@0GuknNFq!oYrSgCNNz0erbG*X6ogm+LEDoV0x2{iEk9$m|zS^q<$} zO)#wetjXZ@oudTp3Ipz~ux6lJ^$ec#Ao|U)ANAGSB*PBNE%>{`Rg--;^hl7QUw^IO*`J%%LU?~{(=_4bx6;D5(w#;x(hYLOKrE?R?(%y?xDv?2gt)Hzx_n(Sjj z#CWg;Y`}|22PW4m1(o{4WDoMq6LTfJVRsn4S>ux~ZwP>DpOVJpyI$|XU&}Mlc!E+e z2kxSuRa#kU+h?sYYxM7PYfNDAj|OBlhAv~wEL~^R{nSKLg(q!c|}tN{poWhw9d58*+*Ua&14zIT+!p4bav`RTh?_*(&_BM zdN*w>-AlABfj&9wi*6ag9ybziz_+k$)Q(+?_sH_rE)QY-Djm0Ut@qOgC%+YMiLlnC zgq{+##T=z`!e+wmV4RxBJb#BlLjGIltv|+H>k!}wFQ7=h5dH2!oQ?R;!d_U&W5ZAW zCmq+gdq!rf{slX2BxoA}w3IW43CS_unN~Ci7#3aTNt_zve4m3=8f8k^x02F-9LV(F zDj~pU1$#Vfq)ZG-4wv0QpuMb%QBp2pq<2_`kQnDgTxwM#Y7oq>Uw=t|Sp}q!d%uPGh|$Li}e4)^+DfdF7B(s?i1TjCSU3(g&yoebv@j8P_L8_1+!IQ?&^55TG!le!mmkq_ zCdjV5G70Q6$`8(V$A3tBUFW%0qR&W~Z$@|a^hNQg<5YM%IvWV*jQn*HU96M(ZYCusxnOzP1~ z%uCuBlUg`f{KWl9KIceQrjbaLs|E?`J`##PRu!C?>bln#LO}17>agS)hiTN#UAGdY zH`6{@(+}Vs+<&)dd*Aiwx=3q25Pc4VeRN(=JC^9Q924LI46Mho3v;%FGd@*PM*}zw zzAu{|!=ZBCWPn0mhG!vEa`&tRZ`AJ2z-`>G#VD27ZZ^l3zCs3+XyN*%5@9N^W8mdV z5F&A$3z(7yPKVLp;EDwjNgHc0WWp$HmTE1M*x&Q>e}6r>`{MP#U;e^(|1y2Ifb0JK z`@RF1Y-;kG(9ZvRUu!rrS`gP3cAM3?-mux=0qBZrd-~t~hj-=HHShg@|JVOowvuGT z${ePAyDrd_i6;NEc*RIuSv$8I`1J3-J96Y1yb^sv{!8a5RZ=;ZfB$Uq#pBQA&aGS5 zN$tC3$A3~k_Ayv2!kO=1m7m~~XUCB*N~?r*+83)N>i+K(I7O7A79~mfr<*ieAncn= zyj=x9hlif{mGnXUb1cMyuAaIJV|Bf3EDt<)+JC=wHQlJ~YOD7H53sK(@bHPq7kT^Z z-;i&7{afm5j-&%efk^< zGN6ek_<5zSwl;s5X$Ad!C}9M#-{>8xZkQK@1+Km9$& z_tmzgKM6me<`&Kzsq={Mc+Lwq)xyqCddlUxT$k%|U9QXZ6|SQM==1Kcmr<~+0&4c$ zHh*$58qYlMx#z)jQ$OsmbAM~UN6)SIZn^+>2Ht4Gj_>Di&Hv}bMai+jZ}BAk6T&O6 zWY@Fj(^oS%CtJXJ20gud!}zjg_dwY3T_18E>pYPPzY8n6;mp$Xo7{H10(V^Sa_^2D zAUJgRuK3N$NnEc=Fwi}2pZ;dQjh1#JUw?8k7Y>}$Vw0kC(TU;NF9iMg_XU!KK^b~I zkTvCxnvNn~0U@*q5@V>(MrRu4WP5>||1!*H{LHMnj!OGq=`Jpf^x)-0mS#Uj;=^$S z%~tH3=sA`O4{>j5=b%9(REzf7+B&#QNQR5W@!5p+5XM||NO%;}%>CYI=mlI{k$))X zK~{l3rdh_k8n9i5WpLa)(7y#@5O{6?#JF-c*xd2(1F!YII z!K;>kTl;x$m3jmp<=`wOV=X0EflpBO-#1<4h9I`uiLNaFNen_P$*>VWv;3R!E5tWP zb`7+#!X@`trRTIsWCb2MFt^BRYk!k}x86T^fVw^-k8+#fL!!8eAR*cG>oR6-(j>)~ zQqS__-I_d@EZAvKGMs=BRZG#gF8u;fT>>pgiexa_h!;$Vl~ZF#+o2Hdi=S(y#fwb1*=!``b)kT{Z!&~sCK1=!3Bmt(56MCslz+S!)@Wr` ziO5#wjoV#6OHUePI~uG0>7Eg+{wjiN)mQpUh6Ft9eY1uj& zqN}f>8|zNipZ)yLL3+R%%zp_d_?(!}$8@5AzGm6!45)*)Qh1-J+)x%;6J7+}OUK3i z7<>lpfN_k7kKhBq~lGsD8u7mO#&wvLUtc5ly|KWG2 zX2Q4a%A+wjA0ycrFe{zgs)U=s!=9KKww!@$uAfe!8}>6yq0a!HG`$qGqJ&@lb-4ha zWEpquGMl|8Zx*~$UVk!y2)#N=ztZkl$UXD|`-PCVef(&TOK+ECOVS8F$CQ6j^5ZRc zcb~hJ9mfWJkUQ_3_DGg;61C>wR*8$aS1^{&@_C!(A^??I+*D(OtK7{#1%BWynlT8E=D*`A0Z4+?apWX z3Hkq{>oBQ#Ioh+9gsepXxdKsmk%FK9`_YFV$#=i|-P!-X`xknk=3ypZ|Jv6lET27i z5Ibipu?$??zI|KXeDh7YbN}Vy!+-&2#7~sj;`hZ_uWFEUAJhKlhu?WaUVPz>{Pb79 zmq%Yb$#IYgqEA>ylZ*UAnDBS#vmrH|=6&UYJiNzjzyB{W)GtVALmM4M%o7 z{QS{;_N99-mAx?E1mCv0ImQvk=eB^=IaKZ^{$OHHWNBdpz}`B%k2D znXo0hjGe1BpBg0l!Pme8+e|E0!@G%_&_BCPMsjz;Gk==QO&mlO4aY6)2Eoio3fT9y z@7=jC-+t{~c`}~JKmFk+0aN(M?%2&{lma{04U9hgc#qQW|N7Z`)2DgsrEe^LcrCia z9KiT5>@xI5$V5OOZGH+HC`|j>5K?_gzB?BHT*U%=EB)TyRm?@YvIksf49 z*#7ToyMGn$%|lPR@1MSsrP_jS$oSm#=UZ~QF4yI{T$k%|eTAz}vW_oK?%s5N{mY+^ z-be7_=$SQHd(`%yJD!{1Jc8}Z?lF98BDp7p`PuNNlwm;IvW!=kzUx`E5x>NJa%W~R zSEG$V&zLMO@7V<>fDsOr8)mLYvXg zQL^tKXUs$hPm-mJc;f0# zqIPf_f!vF%bp)Misv>9nQiqdB?ViQh%h_*?je!x&>~2S;!R0kO1bP!aqfJX-k&r!) z1AlU8YILRzrYECt5J1A=*FtdpiDkDOWCU3CRL5Hn8u@(+FalQb z-D||DLL70koS2={FlDbjIOPq=mWC%$W>w6Zn*1AhwoS#Pv_MLPS-Yj^Pm{zNBcKIN;}=D}_=#W>R{05Owg$Fbiq}}v#>TK?af#q3^dNeTlw}iv)cd11rps8?Zo2$at11WbT7Ugf zNJJV&NL34TM9PpQ>~i)tx8i&~;(RSbrvhFm<%8gs2PIus`MIuZ$hwXW;?C0I|KEl zBZcT|QhE>P9a`!Ye}4?41!QFp(|qYpA&MR?NT_mh(cdsx|g54_z;+o@uE-4%8@9_K}ImQCU zuz|eu`gD5c!A(W!zwlqeCq-X=_Nl{qLNYdXKyA7;+p=AS1|e9A9qO|O9)CwH#EAWC zlSbapv+E$^M!`omikdpB7ASB+3Zz#h4lX8f9eqvHeF4CvCnaF9;0fY_-9Mi2cLth^ z9EQ!Eu(R(Wxf>e0nm{8YNphzwXdL`J<)4$mBE?fuI(qITvf%wi{vAGNa3ucnOy5y> z1^?i$%iUEb+!7BlFY&Ixny<};>B16H6KxMfKJLWs#!0!c94os_m)v}FgJ124^a3{WSNNbUVl70oA zayOi-m%xoAKPU7sa=;d`GG20>6m3U>kahOR>H%BHAY|^Dm+{PmOn>NQ~eQm zJYB!TivmMkCGK*;1Df0aH?);*#*2BT>R`R)51_+D@ATRr76#s%LZ%@~qyITS13 zXX50P`rdI*{=Y)4lYgA2-I3gNIN`b`*!>q@lviGTMIL_kAa?)$V($KBFBCRtKO1;| z_u+%j5+6tsz5Vvv^38X?Dc}9x_vH4i+w%YXhd)Rz`V-#~4m)76I4d+f6R}k?*nis- zzd!oUTQl+dlV86d?fDtG{N8GNqLKJ{q^_*09c2rFTj!zDC4V?iy_F;9<7mW49xck% zi*PlM`P(FIEO5B~@3Sxb{Nfky-fQPd7xBVIr>(@!-CE=@_~-{D!m;3KlxH-aK6AO) z&-*>m0-Mx_q2nR}yMG?fo{@Z4tOdNN8~KfvT0VWHT2$q{n({XvjQ;i{{OK7h7D=WE zy~lo_hOTwl*ndjKp!ego7jE5^zk2gW@^n0tpS}NM`Qpjvnb0mjDbNtaK~h`DU-M^A zZaa6HJ^qtCh+V*cc=Y}}%UM{j2;!~bH>HyHvh1gc|IZ*DZCoP1M(DPmBPk+VS3Gt9PS6a$+g8DnWmjv+T zx?Gp*a$T-J>sk|>d>ti2ai2f`xqYok;I+Rs=zjDpKaXeDdtU~}^Dc(vdG~ls_1yeF zeNOKy@Avo1iRg*raA*{hE`ASiXg;eksUmSZelZkR44GjzfMC5FWVgDZ|Z--FKB zm8}ur?DE7sQ*EvIOQ%lx9zSyw){hS{$<>C-2*hgPg}=)g|GQJ0XVx*Mlscxz8D<1* zYT~uT^6=;tS_$;z8nq##2}j06BY}ePZm;99E7Oao~^7T5FegjF1SUOMe**WWeeD3_8pS zB7e!perr_nrE~!6FrteDz6tyc;#$Gq^o1mfi)T{)9iNcJn&?}5+cLQ8zcmb-k#?!E zv&~8a(i9XuoTyrCur6Z^4(ds>QbfQ5O0i}TeidV4HI}82R6&sAfE;#kwrJR z1q7fUfo#^FHRugTM&cH&?pAzq+Ey?oaDUL_)-6INQfLHoYymfOT7KVyc(NtUVdt_W<(^h7)PX1QB~;6oBQ z13ih(NLiCI;Ut3xfhuJVTU1V#ks0z%N1os80j-i1oo%H)uPzgfCKVtrKjd8gfq$pL zi4v0c~afoM{R|B`LK z+*XW86l#_?hCFB_?)KkKSsGH=eIRh*BXl0iWGV3wz_{{l%6;A<2=p#=5lV_<@j}|P zz9UtVj*`0Tl$(Ss6Z<%~uY$c=L4QIlLJNd=G8#CrCXcCSyyN+C=SS~no23a`g4M`Y zh^1^!-We2pR*Ugk(_zWlrn%D-dh99!$CI97d1M7_B5dn^8zoYUb4usUolLTj&gk4_ ziLs0$`F9!JcT3p_c($^QE}xa$a=1n;MnJox`Y_AgP3f;%1@z5oSE6W@`hOl#wiDEa zoUu_3%d;!NC@6Llp$yt&~rOH!)bMnx>*dPM& zxnp`sRw8+qe4~*6^6j_Ohn1vD)fuvc#2ok$Wt#n*%`MN-)}U3%-V(1+iG>5#@9HRB z9OJGksWwUrFpl7CD=NqHk-u+ypRL$Mz*uf)#pN?y+D0#}mqUoM0rAF?hn zCsCa389b2YS4qbLE^!=;Na7=z%8L|K5Kn*(2!76;kh0v(P$?0>3CZSjpjr(5cC zo?~Nt7J4A7ByhU{|H$3v<+u2KBlyhihZP*~%OJC?+ml}eJjnWb2!AgIFrsC)6+X$n zTX*ktPXzCOfBbiUmnGI&m+tTFyL+4Llp0C&GClZnbpI4o^_A=z!pJc5Au*m*L zV7M&%@wpRlB!6kku^0_3l5;F`ULwyYea2vjO9J?EU9QV@xh~gNyVk@dCsK}{+pnJF zv>V$zdgi9!{ZTtdechC_JxajdlnlNp0e#fQP5m6f*%RaYUv@Iu(`WdELlM1{liecO zHS2*>W91>yJJn}7DfQj|`n?*;LYEqFPIM%l^W%4f9Dj0wv!^uyCO?P*^h$2niYIo_ z`n`m0ADd!q6(l!;RhM@d@^q${Z^F?`C(xKTj%6eREYvISWCd#cxt@?bmp6Z}P~Q3K zCa!|Vh)KRJ7&VjbaGt%a*+%WOi2fTAsrOiYuDZY;6Re!Bqz9|5VJ8=RuWG}GRJwh(535S z5#VC7R1-&)#$q(lVwjY9 zWa(2E`DfxXniKlQe{rjjR{X3aD2uGe%&a3oN`GI}!|1Y$XW)XOm;Q16q0N42^ z@!r`~&@uCk@N=t;;?@$j;B}?soD{!RAWZ#kBAD$WC-b!9Mi#BJ(5q~SRMLrB@IK|t z*4bEK*qhk|813XDT8pe`q#*Z#t8UI&;s`G)i~N)PwReI@OkkHh zSnT7Lk|`~^?4M7O1jHf$PYP^p_cM6UgPNods80(yLQr<%jm#7K0wc?FBk;<>Xpwb( zCt}SuTI$u9vt7UcEaczc^@R|+k$?PS$1!xXGHvb5CxKioo9^?y9bZ>hS3wF(JKX~3 z`?Du&enaV!n6pCvze*PeMmc}_UBHq z!x*-V6QMN^@}0|*!_=R}`Sh727w~%|CZ+Jni)8>Sm-zKH8(Zie+QBvLVt>j1T>K}1 zu__c)5dQk_NqA~o@21R+qUp)s%bll5tC2hh9p^B|IiqL;N#)qpJ$d@N&6#W(lJKK! z>lTYc&_5^gg!m|{Yy`ig9+N6lp4APk5{0ATEaaAGEwSKcPgeQjhmlnQ+FTj$0EZ+q zH7_k4nfBF;+@7-2u+I>KtAE64$>!vxYA|gZveE@3c-^M{hG+mESb*Y-WH;%@0!Y3K z9s-ZN{q|calfT%{{(bbo{S)|>67qlD=Buy1I`6&n&O7ph@BcvFxK1ALPd$D1RQ}-~ z|6YFcn}3l{KKWSmYj4Tbx8BO*WHoSxg$J+;uBb}F$J(v#P1z4Pk3a`!s<`@28=Mm~A;M@A3k|-{qIwG@y^%e{+*ZP(bEUTZ|wGwBQSSB!-3uUynl0kLd6=9T+GTbur>z^ zwi;441nE}zRjFH`4<~HUMWqK?j6kI9vuZ6|fMg776(R5;uPBcQS%gWA!SoK6?f==*_;7)4Qw_9Afb z`{z!xbohf#6+DvcM`)N6%1 zg--ra8SpM11$4nj!r2KUwR@;JJ=zU+C?Sd zCdpZtwAwN2x2&Osn)A^fc6^XHmDGJb+N^hBFhn>9XZvV>jZmiVe38jMzxA0*11 zfi%wu%zrqLH`&h5QBp#yZiZgNq7r3U7Lhe!VT{iXc3r5*z5GZ<_lnRd78Z??28GO%&!-q@uYHV(LL zGJj&F`MWO~SXLy!HTyT>k3syDx>D=hNw#O$xl+H8p9r5owy=)Yj!0lz18ZI~UiePj zt0tbd2u9|@l)!bb;SX|Ii#?ER zxr%eAQEqbXY{m}Yz|sE872$QZkqKf=_Qy_J=UGky36)QE9A~gGt5jI>3zBD!&s@HQ z&b?hMo{&ncKyK2ug8uP+o>#abm|4lclW(6&83SKxD}$)XT(uLru7uf)`~@prsDCII z8+g1P{#BGNXL(8a*2#6yIYF6aMq@4$iB}`%d|2_2qy`odt}aeC z8I=}NZptwYI#seA31QcT*xj}#ma3$)pAAllBrU#6y&W*9)~L*iS4Woa9`svSe`9ai z@=qOyAlbf=FqJN~1*S?+iPW5U(tC{N|D$q8o}(P^S29V2lOgamPotk#?SFW$aaaGN z;T{X$B=xcN7J7RojGHFRp}I&XX50gR;fKm7Rbe8EHVw)gkK3wN%cdrQ9Y zjd$kv@4ovzdHVE;{Oo5xmEXSi8~Oe3-w(KaoO=77)NXj7+N1&G-*kYBkiRl|bzo3k-8j^6jRLCKk5V z&p$8yJBOAFe@cg?|9>xTN84ijgw>K?@Gce=IxR=}eQTmv>M~k{llCBJkqK&bzkbd{ zEJ{O%tecX}vCxfF7~zrtzFe27D({YVWZ9xRC-@A1tC~@sLEdoc8fvbc^-nTyqKVNUIi@HBQzb8 zs%_#}t+gJP-lW3jZ86Hu0md{374W$BCo7jou>b<~i~Wdyh#;1Eo#lvFu-u;He9 zSQA`GfW((RpqN;3XtE&nepM@oE;JRbwrYaPvO^?lt)@aC)a8cC1#TanPbjc*m1y8*jsP0o1I6MVL*F7ifjIAFJY1$L?kNQZj^v*nBn7B9CseD)xP5|0;#i{rm0jym~d-n;t!|<8tzQqk7y?po?Xtu#r<5MvqR=gG~5ap3RtpBpp_ii}}K+WQ~~zXj7i7^d>94qQ1Wxcn!IPQvHI zpC1Xe5w27+q1}8zEm?q!z-i30C$%)g9LH9AI)m&<9=D}_5QPDMZ(JvRzxmF$t`olB zmD|_J%BfGT`SJh#|M`E(#~*z-cmBes%N?A1`*-eEnflWp&|yFD(bA6A;z9rYKmPl7 z=ePg#<6p+zN*E*Y%lwtQO~r)gci6EDe2!BjKy8FfxE;&0P;u^WCe34IR3y;q>|G>? zMO1sAr_;0K1L!t?m;aL8Pal0I@4Wt(a_iPDdHUo@tn3@vwhzWJvIX5Tfh<%~`})z7 z49QuT0aw+R8?(~QZ3q4qHgn+{CHpU=S1svRxSM#=@B{*Hsjo>l`7s9J$E)klP%7;l z+SDNu_MJ*jt6WXlgSy&z?v0n;ly_eFwmdmKmVf#17oy~UFG6m&TUT@E^NjzEsG6aU zt9j0^RiEYG19m^xcOT!AXOHE>&p(jY?!6%|-Ts<<_T*FP^B$pv(}*<~{cic3fA#rj z6+2-)A?asjS^HuOUx*NHLoF`Y?S>`E(*6@mDeJpZ2(A+${}aLCe@XeHBhJ9ivz?WE zRj~|7%sU%@Dj)?m^l}F9<+@y#>vCPLuXwG2K)(NFLHMKZO2){nlC}Kz%i3NOscRe0 zgW;z8e@=pVeTVPg^!qx-j*kjB9!=Q&ezl43Robtf>tliI5ROUP5ge<~X(D)dnNOYE zpA8R3lwhGCG$xec7zQF}1o(T5O3}3AFQ^q;<(onfV!-+_%xvkU$eSR_; z8zDgXVA5q=q6hRuAF{%y`8zwpiHE{eI>BPa=NQ@1Wrx3W@Wt{0#CBuW5uHfF)nQ(H z=(37`Up5Y&+cIebu5-0$=Xrp_-?>jqda{1dhQm&BlK-90mh!kY*$#|jRejBxR#uf| z9$X?9!G%@7p>$KReNAjD*icPt(3^KEG*txpg#PhhXyGMg(@c(&hQ}9n6J_71ZY4tc zP>44$n$%`R1CrBDi_X8vHI_5BdLOc>&Mz5%P*FV}SJQVBiE@~pHfxh?253c5r2}sm*`43*{IpJQZPy6OX$e39q6c3QY+9(X;hWsDF?OGf}Xu8 zyfdzv+m-|$=`y-kV?V#Ry$lkwLI5jj1$)UFh>?ycd1d{l`LTlE(#}YAu~O;Xm!+tG zzUr`+I=KmOiA@Jd-_&#P7P>PZKvD zFm_~N!#xLZ;W}C3(q@9#%ot(utVxxfEd7x*D1nsK{u^i_R5CbZeq>UxQL%kLvY})# z<^waqBe6SLKod>Q!A?8hO`s_wO1qGMuqXfwHt{=b@hzaQB%f`5nsi;1!F@oYsaXXH z>)ZvXNIpucDLL@BA;8LXPyOsWp?TwBEl}{p>(rT}cD-g2RU#mb9hRY6_p-brlA4vI zfjo?C%W0-*PmF2FyY_{4fSNVChF!JTQG(=LTZ|>dcWvj6-@+N0`|T0CQzUnP!C09C ziThz2a@VGEvN3jog3ZlxaYZN_VNY|!Hfvt!BkDSJ57VxyvU;o65b zf=)*8*X6Cn#*VMM|ff!>r&Xb z;ztu<3nr){kn*2;)e;_w$_sxQVE$@E!-PD=;?%y4MH?O_+ntbz{-;%%js~^an>!k0_x9`9IzP$I|Z{-Ky|Dim2 z`dB{r;Qhq8>$K<_l_~DNG&`Dj4I8lqEM-#!hBWPj1>gPf+i%HBFWi|se?R}?ahmrm zhLH{asEm_&6_@(565|Cj#{FxKITePHEM z(}$nRH?I@GcW=KSPp@%WuMavF0&uy!XyDTIt{=Z}iiHSMR|kIw1E!6gyZ{@+wru~r zB8YFq3&g&n`X ze|Nhp-}#I0$>V2#U&v4X_3wkft@&lM-AM2cDyeQ!N#iu0O`ALhnk4!fV%w=f9NCKKitHI{IK+m9l9GN$8s5WiFR3u!*6pQ&(+9RUJbAs=qar_+PU&Kwj9PdUWgWU=zaqH;o97|lr ztD?m_;{ZqP_0ydcusxw{ytrvy8hoP`5Au|~U>PM=Y%QbvZaQS#&%l%8zOVCGTqkQd zRlEz17gfWBR}+NA$|k3b6J=-ehiO99JWMYjBLB;#D^gfvA@MUTW7E0BERsV{noeKtUA=H#X35 zkH$CEjL{GKM+i1#iq}ny66SO(jR>?RXuEvAmF;7D>NYWz>BS+GVe(e*Tm{!aB}7@|d&HsRn#kRVaU2kx(voRfHFO3DGYXx`j`( zkSr@45&s1s%5&%go#bRdzZnuFQdi~nDwN1^IhkVQBa+h{>?%{e)r`S6&y~H+mKDrW z^PR7Y_ej6c_?Xy2QLZoCm~_GZ8zjMVg*9Kp$5dATdge&m)Z-`iKp$%_YZKyU1O`K| zyv5z|&5!rW*c?1besj@WiKw>08ns8y0tKNcOKHi#;7yi0W&8b($A_|F;2qXc$j++M zZ}N}LKJg6jYc0UYP1MH~Rqmrq%z>{aTQs~VDWJEiX_6~+-!AeiJTKXkY$76YZ7PLS z+g|$Pm&mXm6qLtq??=B9xE*Iw2BBMVPk5N4qFfBTM5Mm2km2ze3&uYN0}DS0w@h4B zoiMHd>9dvo*6&T_;>oaNeR`4O4`YyM4wmxF#QUvhmSK%{S4B;y-yXK-1q89nMXu~A zH$msDO~)jq(rglGla#0+VKB->&QtvW3@YGx$TxydV zp59(2weT5v^6{nl9+kukmvv5D0(E|O-EPO=nL=HUERb+ z!ydZvgR8Iuw3n~dbsvV9uD4th*deX78I|s@#AZ`6L5`EN&cc;cO7Of(y95i*4R2=`HJB@;X)hyjVl`;#^fCoM>4 zUi4L)e9P%l=Q=aa>nk(J+C8t-?u%63`w9gT#)?^0*;#m-LMhl(F_tbs!pX)VL=F4E zY8+&*Yc3*9>V%+niT~Q2K)AbHeebvdm{9K8UyCu!&I0NjK-uIOMO7!x)8x?Ia~EMJ&h^f z-=_gh)R(0a1+!i&*H=hH`x?0}YHuTd3MP1EUtiQ(CFPi<+B~V=Rh?)j{+c`jDrGto zhHcrC@1ZZ7P?U$&*PXFs~fsVlfXy? zpBu{Xhv@MFU07|)#*BN5IokyY%5$OGHrtXLvk@ab^_o>`k?wBm1S{eicqA#?vMu|u z6VVtOb_HhyZ`fU3$C$ROBE!HBbdxkgLM!{0{a*T$6{Qox+Cn#?{<<-f`eL+rHJYmV zgB|aWzy64vP**TC6(?h+5NWi;DEmRlU?kvF$Go=)Ny0X0k;CLj2t@_f9RisUTQz&q zpkbqDXYtRMXWe;TH`Ks=_ejcI$}%>kF6Z~O1#U+5G*FuK(c%_D99%}%r&%?)+4sQn z_RQ1uLJYuCvk|Bw?)&_EPaRK71?eJq8272JQ%Z)R9yBV;eixQN@Tf83^~6o^8($xd zNwlbO3yc3vzTrbq{T#JJ8d3#aQ|}|WzgoM&Yn5}_xXs8P>&f5%v)BWH&|j3$z)sjdliI1-FL&~+i_|&oP2$vnj#=QBDOkqzAXmk`Z~CAB@l7k!LoDt@ z2dVw1ktjKGtp&u@`(#DGJV#sN8Qy&T^X1-lAr^@iOcA<|J4UcUq*PPefTvX;zPka*7Ylpnn@)%o$bmN%Omd|UQ`}jHQML5GQAKgREXqq zXL(CzHmtdVGU_z1)We0erH#h*FP6PV$3vd!4@uM$0|M1J{>{%EMe16l+g+#+F6oQZ zg(PSBd?r3BzH4m=)y8QDLHSVyjXqI{l6HFfin$3s)-XDa9GD$itATt33L> zGh$=>#dg{y2{?Y7A)*(!;CM_zW`(HhH=|A4Hir07(@mDmv&L1<5JSp;3b0to`qTov z%)*-=_zP@S-J+#BdU+OaSNkFxYT?Kw*xqROzP@nn?CmtxLq{~wWRCjgc@1uH?2Cp5 zd8`I`4-~5Beo;zgroI)IwsR1ss{Aj-?@oSXCOnmU)$C3&ib#43 zIb1tH`My;$J1R=)f%nO!r+cbZTfcz^H;S=tyk5hSAQl83UoFAfQkHxluQ}H4Tu1u# zrUyZ}y#3b*pC|bz0p#4Lg#e-C$hC7Kr@9waF+A|_U6_@SSgOB_kXp&zy-f{|8;!Z# zo&RFK-0{yn2abn3uj0r0j~R&hg{fD{pK=OCOTN5l`T^8OOsE7GALdE7V(Nj7yTiY> z;fmMku}as<6ZnZ%OiINDuLghePxJ)7;on*@7R6!QlPS4Y`>58N-zFc{SIgJqiZ?@~ zJJ<|^hFmhu8fdB&j(>6}f`@olIn?Iqx&yupiu|+e(0GmLDbPMqn${Lr2Z^FNm>*@l zCfN>cq|VU~rA4~jn#>d<{)7T5@pN zEn)$5L^$PgBK-+_{ST`{{a1b95b!1qp(Ysb-YSzf6_2j-P|4sdUTy;JAYb-R;klkr z7Rvs|DPqq7=N;TwineYmvA6o8>{HvdJwKkl4z_E5COfNYA4?vaLm3N?kym+5wt2#X z^45TYJ}rgzolF)iWZC;7!03-gSM+) zx*v!i`l-cTSADO8oHyG7U)G#_Z;#AZ=J$lcCf>d#)DPXqy?WU9Bd*Ld-+8aR=3H*h zpI?B-NK@g2h6iaHYrS}E3@z8M?=x(NBXJLp4Q7emr7=WmNq{%|zodf>lfS*~kZhkW zQd`UqBDx)sp}O)~v0EygW0Hte*s4cehzB--?LuU>>eb zMDu!AJtsXl+oCWyzaQoOS&0D-xmrWTJmRrZ=cZefju}vWOsqN5q`_mg4ztBVT)v4& zDA`oU3Je;g%K+>uWV&Dn340NS_j{(|(0z7TY($T~mq>CQ_YYK37N?@Jy{3Tpm#jdP zmJjBLCy${ODFKip1mBkp^6R_BE*8M=+HFUpxhi>W_QAEwlue|#Aq7k}W~|g!5v_$3 zN;Uj1!vFB4@xFPs;t$hs)Kwr5d*DK%+0i~Il5*dW2V@52^%-i_P3i2uPf_{r_UEhq zT;poj?0934k_|CRK~H+`I~uio$mj~A@X142o$=41fz} z03A+YYTXKm<}(^RHlYoo|NWt-fn4R(cEpYQY*sJEdu4bCDI<#J>d5oIvvIWsnNjY9 z8yx%OicX{~Ol0qGoKp;waKSoTr3cQk_`*eKaJYtJEK}fd(tJ+K24trV_Fw4(x%;B6 zCak6I!^CJl{^GepwzIBN&EJe3bG)7#0pUZge=(`gE@k*7@?qjTW2G{09kv>}Xe!fb z=EC>CJ*<`pF~>AXos5v`I>073ZT}|Y;{{ptg-dp6EEf7Y46-l?IH7+ZRg_@-=`wEl z9&D99o>?gGe_rbLFNveA;mQCYNbyy!n@rwt3P>a9kD$TrY1oP?Jt zy3tG-{`X!l)`^ng=ve`ydG5wQRx|zsf zf64N5A=_r#HSKiWCJWQuLa9bpN zYwXg$aXlh&YHCuy!|{CSHfH~-lr*(Pmi=0tv5F9NUX9EN;-sES~nm6vvRtLD--mXz!7 z#;p$+mIWK8@5S;8n9(;c0d|P1h+=P^cr1vihJJ5urnFWLSNj~ALSp?{#*!2~l!VI7 z>=+gA(pBk4jkDgrAc#sq72epbH5$Hsg_$H+qUKM$CC_Wa^fT+DRghq4p7vTnD)H8` zJybic{F9zr`W&y^wr}LGb*)gGOe=oAQ$?8|e=3Ag6CwLWaW{23`xecwd}HcHhrvC*R268lwDKeT-_&->NQi|Jt#}?K zX!G61wCO`(30J!*;H=vI(NqL>x1R8`$+ggIf6%1lW6a`RvFbO)QS?z8!|BiG3)DZE zVp1f$H%tT3xLLs`?kFz~eDq_ACK|rQ=Phc0b)znx#ZyQ!|_c1#tE=6JOf70yy?JA3WLb zfZ`w-*c0rI6;%RqknMgk>GS~Zem0>Jee84NmFF51@%jv#;l$G&u$lR-?rR$FP(oGb zv5}ez{8fuqYo0!CZsPEz#t2r_U9?eyKAr`lT&CDe?VV%XufUV{@W?X0`7vZJXI^iH zpj>H7GdTu`P^|vZj%6h39()*@JW)05+_7X-gz0bT*iv-JqX+$*W7wk-HZZpdza$L{ zcIsQ=ck-Bd<#NK=y`1NF|NDEtw{hnGiob!eU%-x=j}iXeg#H+PAK$mN6hHJiYFeOZ zvh81cSE4F7YAtz^XG|7$C5#sE1wI(QF)#i~ z8v4VgVke~cvR_$#=%Ypk`2>rS8U)AQ97KV3XezAa131&0DrzrDFI~70u=P40h$RIm zy!js?XLK=ZX|OTq01X4=zc}TP?I3ZocgI}TKXrAp27P=|zlM~Ct7D&2+;=wx0(@T3 z4fAZ5{J6Akdq0<0{1Tf-nF{e6?^_m|z$9{1)376kRR-|yd^ppbnqg3S5EDbY- zc1M_s!M{(fD(c6G5Qf9W>Y*LMNU2#0sv3jep-?Na#WDmQ9iH)l3yBsof}S{FdNM|S z;H~n@oTKA_YeB#Dz!29y#w&HGaFC@hMbx1K#bDRp-6fE|>uBkRcj^cZ@ZzGRz}M3Y zp{u;EUHLdBbO7`-57ho8#z!CVMjQ2s(O{h~h8{gIW#FXM zeqvkISu;=qjycm8@y|`cRoN(j@7<=&KkT)5M@rDew@Mf3N~~JH7POHO(1rH_58$v% zms+K@3p@YtP!A&3`t&Ip_~PC5!EUx$pYt=Xo5Oph5fwfFGa5+3+cQXdHydlJ$ee6liXjQrOTVvUdq6j zmHj<7y?NS$i0tj!@a9dyAK|4=*#_zB=MT~*t?0e3e#eP4-Z;pm1A%HnY3Nc<%e6@i zo6(Al{vGvoo;gwMHjQ^ke)RkKo~C`a`uttVRw2|y$xFrIdR!OnlD~IdVv|G=gtn%l z7(3qPm0!)#VHB#*I()xR^Lvj+n0->gt@yLb&%Cc`hsINzQ#i^)RlTnv)rb+r=s>nV z#j2T6j!Aon$iwHJ0rGf4->8tsg zXI&IJQ>VI={kXg9N)nr`^#93rEJn@zyq*JsHn*ETcy;ZW_T|}b$rE+e1^ZG}aTDLs zz!^f5zvFhPlaYZBIGYQq7);w#Hc}QuByx8|9ZXsxj9VDJKSb*7)$Q)ZoG@H2o_EQ+ z>ZfPkO;uY09n^<|FBI#0^jjO~oR@V8f#X&+h-yD`H%Cj0HY@!KTN{{&I8LXA9+p}m zKTb9Oh6?th8&8n@lp5FP zXs&~D=+8VOB4+fdn31M&nAZzjfVGsTl74UB6Sf2ZlgwMj_kDq8yE|}gQmtOx4~S%| z(h7Zbis-Oi8dyo1h_c;^Ey7HkDsO-{bxM4xG44%|D>=?~pWPi5hsDI-Jr;PQ~Xj2tF6t`o-KXl`Nd*R;GB}t zOBQ3oj)jv7!wR8fN&mCWY}Gz)X?(ffg%UMnsLFuDEr1*V|vVc;29-k{a{*+_rjA zNJ;myVP)#tX2}G@?94tc0nKk@JNpW(VWy$8ikPvJz5MMAE9>oX~V7Zpfa= zvQGRVR=AX;QENnC1XS}=o8_n06Zl0@wUkkZ#+>(!};*;Uw9Yv-!gZ9tCARR%e^_2gu?K`>XDu!5+Hqpb`5jj9x4d5?1C< zQJ*Sh7g70&$%Z+LQ4vFlgvYXseWo?YB^yVpV}QJ!;Co=Qc`(w+-GCZWGg2KlmGVKGV7G3>(# z0urK;NB@8@`#|#Wy_*)0OU!bI3cYil4T~TiX1ml!C&UelW`CvYh`3%6B2ULae3QEQ zLuR3=kD5}9!Gm}|6(NmFgxOI?J&a!CiYsBWA*bDd{3CJ$0-saiUbP`cpy7>r6;Fkx zOG~8=&!214CWo={?`Q6@Ycw#r%mM@~V~MZND8OYU$?l*O^RW6Evc%wp-C@J6RNj+A zdK2Me?T$_nhe1-08mp4Pnx<@?SRlIu^lB6I_Wm$m+?T!kc~HXFkfTfwXR zl=u@c|2nI5;DJ=O{3o})FUlq9`h%h~@#gDW5w`2HnY#u32|yzH1Tp1g8(4Ykz?uf&gA_hjZ==anE9e-XU*1?y zH>8>%A@;0wzR@L;U}{E*B5@Kudg7v9ND1sig<3n*Nl)tX@gSGoJfoYK)$GfB(e*56 zGSulYEST+~CCO7;N#N7pZZuAqM4%jt;d`+6!Bms#`^^YrB$YX$$PJ{9#0%YvPNvO( zeGw0oe>G5u>8=go-Mx(4T4p%AJ^LM6z94cm$q>Ou?VUf&Yc2tk5Ay4X%j?!#_vqLF z=&5Vr^(0Yc)E46yz@;@~l;9{L62?3yz;*$yZvTsB{L+^5RsJVYasd`n)F73DQV zU1b`V*diP(tnBR6{8z;wF=l#D)hY=vKi^PJiiNbAa;*n)v+?T@B23=5ZGLX82xM|s zo5HhcH`)_wM5g?*!ncEiKn9<{hW&}r z4IJ=b1*RI5sDVN6>P^))&l`zNh#~U!G4ZjV1=Yv=kh}gh*@20+g}Ig%%}})3NE${u zlRJ{;7q6q_nGgGlMf-Y{Y*7z{n9p*cd(MaKE9Ax0ReZH1w}p6K%~}26ea!z!&jUk) z3UTk@R>LyCPzFi>ij&uY&Iy3&1(3FPtu7^qU{`+KA;Z04xG9D)$=T9UmV3DU6~FQi zWk0f9DFq`#*!mBGZR5_vvwKO&MzDbY))ZyDYuI{!0Sz40uhL78^I~ngT)Jh0{t(`T z&qNS5I$3-f7flvsi@@osxCKyLTcD1UW_Z@~M^i`+7nLO7FU$KihUt6#S<)2$#w=9F zlPd~uXJ?`c;M&UW@%$5}0?>e!y_#o8U3W^u8%a zFeE{~o1wd$ABX%quGzbeeo7vaY1|vA9&`5*TL~Jb0E#c(5$L$(9X^&UlSGs!>Hj6IftLW+6uQH&YZo(Rkx zcJ(^4q*=99I@HF*_B}-|>-^7A-JY^zZ4vg;>-j`c9 z-vuo6k95hz$B6K%@C5UuNwl4Dbn^(6g;h&Du}Q9JC9kY5OMAQgYGPL&Yi1 z#RRAka#_^nJ17scM%S$)L6Mbfv1W^G39qD6(UBfM=CXxqrSpw0Z)`>X)q z&#*7X&VFaM;UE(U3Oi5CEM~E#XOx}`h~LUw3inu<@~29-ys=VsWjP~Q;O#(e$_}*l zJ+{%XM6uCfl%)RgW;C#hr%!$wV_U#`JU%(_^tQRwoMUXB>H4F)4TC46Cmd*oamWB7 zsj+SkvhP93H4^>A*j_Jox1FquL4^hc=0MhpA%6tXw7aGmsBDt1)TiJ4xBNWiO1Vj|Eg`R-ISF7yvn@dDbA18U=< z$iDv zpMl%0U7>-Rb(r6(TjryS#M6^-FUD6@)%^9ADxK8u4g6mCjSY=bkZLulO*%%?3Mqh< zn=F2|u@`kJu^bmHQKSmE(zjC`(r<{1_SuKIWYTavK4vyoM@qRVIQ`vHr1(h85mZpbLM0x^jGZ6A zTC?sS$W;D;Yby-4Bd1nPWTbNOsU%~g5$6MLqk~H79nHQjP;a&izu$O@6AgTnfxd3MRng%3p$G1r{aWjW6~10QyPa@+O|)I;>YuaH)gLH$RA~(; z5&HcUl0qhw6#VXO*kvXoHrxv2wt-1Fku zZrA^&nm|=}?EYy)!n;FZDv_};rY8Wi(;^Jhi=wStf*pv!fs}YUsR#@pbXVT;rj=Nk z_7y0as)4vgUp4Js0aso0rrq~-&*y60N%spMfMX4IoFi4mnfVOyb{ql# zcxQL>xGsQ3G~wJVy0|neL5|o@^R~+k&xDZ}1MS!~l@!I~XZ*~P6I+0}i4K&>N|}DB z&92fFeZ%v~<)e-ZljA~buB)frU&`3G_s`6sPS-d8cdygf&QW_RR9L`RPQj#Oc&hSZ zZT>>VDd0}WwaGt>!o$%OrsGPUhwP@wm*}M@p7LWE8Md#8V!u&TSb~h#; zKZ#%aPkk48tc{An2J6qo+?dOWYsM1w8qQ4S$jEjrIpXN9IT)cxd5^EguVKvPNud3lnQsd`FOt zdAJ&bGx@^jRU=&oFsKiCMudakcx1)QcZ@Jg64n{S2gQjY&>~h`wHM65tA#4Gj0vR^ zRIw8xU6D2>oxvdkMeF#AQFi?j`1S!wHR!6zI_yHyY6}kv+K&OV@`v&W#)(=kD|BIG zit=UM3j`VOeMpsu^#8ISC>4Sq2GJHdVPmyq;*y4M4_F?W0~UU*zraLi#8CR<#b?Ea z!l)z&o@d8p|2jIH)t+GSIIQ(SEoPl~<3(+o8 z!}86a|D=!}R|b6GS>o`O(H*I62qss%P1`u=dS0LT>Urvq+ae^J4l7LI;f(!~dOz5~ z5BsFAjyQbTHxf+gC2Z>z8EpFV7k0-HK?&t*OXIPv!&g~`shm_Y(`bjXCCsl@J^IKG zo`mzPCe`NjfS^fGk^Bwy8(mFwM9VGomn&7tsNdF!W9XZ$dS9m~DPlSsIXwjP=f1t7 zrDa88@q_$;Pb${mBZf@F^|0RUNOZJcBo6M9vWQ0+M@LQG54G6%7}6>Ta=XMGTwg zx#RPf;Iv*Rc^RiZPzDYCV7>Zw$4VT`tK_bD%*2_J705XDC$t`)(L@83c|kL|#l45@ zc}T+sKZK~(E`QNWeNE$c)@QjU>8n@&UVG9ThPi_9npeI1ZRA9uyujY7C-RvNhbzpB z-w&p*2=F}~c(_G8l2tfQ&*~Nc=@zO6ipW{`=cx+V)i&-XC0BGTa35GSRBK63?2^2y zz2ymU~hNGO~9~wr~%q`Z+li2@5%I!T2 zYs31HYyA*k{CqHZ@!~%UY$H5(I`I)FeDpEw2fD{3AmG(=5$X^0j0THqHIqGgbwcSk zCt*bL_I@lgJH8{NH+ieymR`QOq0ZYeg6u(VailgjySAau)W-2ctgu8%^6=p#5vK?| zx?}XAFj};zB7LSW1e>i>;kiY1M@h3PX_6emVHxg8SZVN)RU~%z><`vB5*0dyc%Boy zzz5=|<{kQd$Yb}b?=m@cf4-iP4}1RkpBpxMQq!5_GllnO5w_k9nC6KFCj0c}pv44U z<)4cL>qE!O72%J(UhX%}46k2Yx@fVxeb^^ys%7ODY$D5gd8O<`{U`gp|eB0g#lr}K@Ppn;`onE&nG zkB;kEHfJu6$S&(0(vF?VvfrrTqoVm1!_0g638G8H{zeaY@@U40WX0X@rrQ39M$?|b z{^(r0EKFt$<;ZgTF0E4X{HKzWwlp6IcL=G5UMb|Qz2+oL{p(0G(D9_$OVNdcj9TBq|}5)Gnh&oQOF zaBcR?zixc@=U(GuxCh{}Y!Lv*S7FXB2Zg+?|K0EU(h7}@c{`?_e0#L~zBjsC4m^F> zxS8xQCPMM)bAP}sr^LN=ZOvjvqrax1>0PWy`wCM-?a}+0t*%9zdvUG5MrO{<0Lq#} zehU|AI{Y}>DH$Bj6=mo5;NDj5vSp*&8877KbOuo7(O9l+8CnkTr@vl*x0bLG!HB@( zFR5U`xWGJcjlHo*Wkl2$jni;mVAm@cfR}uzN;$K>keHEV#BQljgB3vaQa;=MLWM#D z5y&fUMKHh#h+>|zEm0XqXVLMD#E*t+BqEXRqgP?|SQ{s%If# zfqk-0E4ccLr4S#0$?&B8?De(Z1N8jK)XbepY505na_CLKXi|Pk&>*^YQ7J`+-#|x5 z24M;LXI)feq$$k5?FE05-A1yt$tx{=mevl-sZ)RqGlfn=QMUnX2FZsAkV%O>dsln_ z=q!f9*w*DC4JR3{==YJTg`x<#S{xW_AL-e6^angU3WOVA8{#h2$|&QiR}e6sRPtkD zBZcrcDa>9?ytA+xy1czu*vlt2_#NMFM*;Oe7gnJyee>dB*6~o^z%8s1ZtHl~I4eXa zz+omGEQ4Oo1UsVJOxoaR(ZTQGb@>r2(ER&@ESbobTaSoZmaFd!f%DfeB9a-Cl*ewO z)GEHb0W2nQz1bK{OJttgkg0eU;oZh?l2cTHRWvb zak(R?&9C&L!PBq|fXh!8B}N_xLC0_CUg_t~zfukK49Lf&NMNu+94buaRhZga4&=_tCN7CpL zP4*jpKb;JKLFBn`mAgTSO;i@M8Ag|XT5Si;)5XjLn7@sPU~>!^wya2P62^6i$P?g+ zq)~`u)2rkpy3j~H+NfEy%P{&Yoywp+WJ5^1Zg)tvr9-MCO}De-SPR4u(#e-$;2Loq zwH9EYTJ{Kj$Di{g%O<7Nsm$2zR3Noe-?K>}cAMZ%M)5$3>d?8eB|mDqIE3k332Ayw zz3OaFMgml1#Rl#KQ3rI?4>Ua#rfcSjDe#0!@@ylCjLM{0?axE#y^%im8S!lQq45=c z6jJmF!E(D}AvYz<-S5upukX^^mbr}UNC3J7Y5Nsq9AW}0%?Q=L7L2nW^*_>m5#19U zYPorv+(>>FEsMST%h7%%yf4k3ql3FArnoG0@o3!^yUtvriAqY$op_}T%(w?9lKLXz zs(t5A^eL`xj@dgVw^_c$gf zL9{lCKJc4A)?V{PH_YN|+i1Q{3IXI+7<$B>RK4dq(>9S4-j+I>)>6n59`A3{(A+r+ z(wuvU_keN!BK#0%WR>N^b(pyCwH}oORO)=~)s}gBQ2QKu-~HkOb14%DzR2i?UOz)$ zYjaWGm@Yz9-$!n2cWSq%l&rp%=^U31V33QA-2T@O_cB&WV0DDo#9PJON=5#UjMfAg za`rZYr+BugnIf&?B8FO@_-(JR8lNvNj4fYpXWWWg0Bd(};7F^yGkO=-%CEgrkI{_- z8*GWR_U3bqL24GVki*X(-k$Tk15%hmKQKWTrV*WM4V9#RcF0Iue{JsizX zn&mT*18i5lnM4u_|Ay6`;o+jw6bHHb*yqTPuu|K2yj-0B5qnsuxEMW5pdNh{@jL{d zlB0%IG$IM}T|%qw^VYD0lIz|-I0cb_Z7lQ}BG(6h=$U^*4(Oqf4fx^%yF7aFy@>O7 zi8>IPEisLA)LF5N^I1BA0;ZgAly5n%tU2BoP065nm5*9$+)x~?=Gri=7p)9`!#k)~ z<9Kct2O0KZBqL%VjMiiYgybH7wP*TXWXp-(#t_W)|CaIH14}BZyyy-?C3ai?{l!1t z9XL_BSVtV2sXyt?uKq_FEh=%aiV7W z`s7Z!Wi(<>Rro-ZF3TLrFZ1)Tl!<4G-T6X0#7_hl909v*_SPX1KhuJ`qIlT1p7zD| z6IKoc#KGsN?;a1Scd`{#M-f8#Wp9y)wEkv);IqI{mJQ||(Tlg9BMPqLM&~SJSm6ox zstK>^HzrntXX|n2FC8MryJ0&ytMapdV6)A7a|U*G5d93W10PmPTN)OS#|jf1D*1Ya z=l^AuRN%GBx*(0Yz567EU7iCPTs*0Csk#^OaCXbz!^+)%KWX6NG~Bs|VMyiFmhJd9 z3_foFA`eZFmHSHXYnqjZn#C`o<%3oQQhBje`?NKi)ACn{OTL%mvlHEU?Z~#^-!BJn zP`J9H!wX&@R@pT47p;t8WGI?{QPDpJ$g z^Cr*DQ4nVu9dX{`p_$LZ`Z@=`x6sKm`qKs55y)UdY%W`lB@R)zUi>M?`+X~x+_?8` zWEWPbw&&$xy(THdDEvnC zb_CM=lDb3K&6+Yg8R66L8+TBzEy+woq;F)iqUF>h-sY;WO$fI(B1}nDoh)Ih2eB&n zzO@Lh-H7C{VnhDLxIb8wJ5S+dU$4_?EP40YV zXw4X4T8HMJrcO!b|8V&k$DbV;+=Vvh){5Edl97hb@y@J7$aYPV4u0>U!wjV;?b&KG zksG{TdnAF-cta_H|1KqeOI@fjm>2S6#3brf$ZCteUJb*GIUZAFlz#41KbypoI}UsJ z*Ko?LE#V)aMTgd64xN&@u;ZiFaJ9nPqwWI4FU4xvJO(Z>flKQZ-HnMTUIV4Wx*^Tb z%qeY0sOgA06K(y}jAG?ur)C#Ipe=Yeq_<%B+jZd)N{d=Ml8M$?6#Ui0sqB;q3*Uil zUCxX0i|opV{dWJtB;xyILrAwaKM|}ouW*n(d-^%dktcv)E!)$5T#eXei4NA`K?t<< zJylXB_bT~oIG}?4Ghn&Y%~gAo+abyWosc|?guxUX!|`TOT05Hn$Fo2efwA+Q%o7P+ z!(+CxMX)aStW>e3B__Qm98`+awVFjSA2cO13p%Dxzk7R~z$^T_eN~?aZ`a_EgP2+= z47^R&*sjXXX1_;Q-0{x)dsu!;11y=dC13Ju~; z>EYS{)S(m2n~X+SzT3hdI-39O9q;bFr}fJT&L=)s$hOIRzY$sunQ7ig_4jrLs{uD% z0auVR)T;myHlfRfsgQJKE39kNI6PZpsO7yggV}Fs9dOgBdqaLwg+bjXfH9ooG?~}I zt!Te5@=Q^)6wdNIma1rms?G6w@x56B)l=)}YiHvAUhQh_+rVCjLi-N=_UXli!jcVE zy}Pd)m_Jc3ZG)N5Io3RLPP=CGAU;Z$kr&F2+GEx>j*sWKK0G`Z#+*X694JN> zPGw|Svd$}c(shB_nnD=?318UoHRSkkx!sfoV)cZQ zM|EPG;0{PnE3zB!Ow8}`lX&2D)LQq=?3e4uL7ys5POizXP=!}v&Tur5;jfZF(Mt4N z)`|&+Plc3mcJh=pwA(0Hq`5_!AaCOnV2p{bBP1j@SpuP6=VA)L+tcaV`~MUZ*%Ty1 zx5WV#po=A`%|y;AP#-22NEK-M1d+({G1=VaPjJ1mK2!b)qKu1Y8?ar{AZL}Gvqq=+}fZ0*miqC!6mefnG3uM=xl#MFOeHa*GXJ(-3#7^opFy+doY z3K@q>hr(~Dd#Cn>&nya~fd|SsjXtX+1ApsDk<@l{ttr^RA6+&7DxwC4RCkVbY=)qV zxerk*rcjH~|3Fh=Cwe9@sJaDpwgT~G)&-MoM&#S7dTQ$A2H(RRbMg_`mnFd!gWeq# zZ|^85XT&A0NR4G5|3v?A7nLLS{2Zi$Bs;Z43Uhm>?U{CmehI?VIb7!EV1=kQ02or$ zO?1BRNYD(1G}3kAEN#L#9>o3Eo??PckUV32HO7lwr$gJ|@gV=Cp^IGh`BE)`g04zu-39&vf`IUH9wi7P>LowD7jos_jcfG*0ANEFA zyU zgyq$5w=~|hi{)x`*7(kP-|jjBEPCnOM8S5}f*6ZxFy$qX(LO7Yev3qTt#QN>aL4d* zm_?~@=Ps=^-R!c z?@>G-4OVR6A~x zJw1SHN;rK7EyOv6F2>iJ>?eP zEAF>0>&PCvRg|yOj^&=S(TD>l5h}TRbU2q|&1&uTyYH`~+N4}v{XXT!#3>)#60R*vB*(^TTeza=gd>Pj^la4-zk0d7?q=|8aVM0JdEIQAfm}O6m)dV9n~8 zsj|s{t}muI7tH?5EGbW$85f&r)Ezga0gz+qhX)r49Fy_gmN*+V`mvA-`~DJ1Xuqx- zC4*44D7~(q#e8n0IErJ?Cs^jryS#7=5*Se-j}g=IC<@O$Mp?@b@9*(HFD{dRFqbHp%BX~u zh}dxRz?kw972z5^5$m3X@(ISZk+;tL|DDdUF(&<`MW~uQUp%(b`OhF<%A&tdi0h zPzA?!d$%mowsorqW*w2nx|%HhP(A9yoyAI(S5?HIKNg-X+Sb%HLQx|Vs|Bb9S50v@?~DI`S8vgQkh+o%}ELA%|q@#Q{}<^(n(&} z`}RdQQ?_07??>S*!~Ckl@WI#a1plrK^P9ol9z;g(uOoe|m@$NoqaUzvv^*C6;L`1H zk$=}@RnLn0MKoO(5NPe9ZWkga5~fo|-5v9W=SDwF&5!<&4?{oNjRpb+x-GArbk3MQ zF|oMhd>hPWB9Kq*^An@LJ&4ACmC9Y4naYMC%6~D@)i1U<^31^Ha@uXbW~Rz3v0r|m zTgY#Jv_sVKLv{UUURBhv^B%qoVbJttLvJWWlwz|-X~7Uzm4Z#>@>Dh{x5}dFT1B7TfM`6|XZO_2^dl52tqbe+7>IwVo8DVX(Pb zkjf*~g7d?^s54U3t^(TdDAw59N`z7Dd1GH2hVsGmWi&+>-`TTs5BFs)Gkui(;6MK; zR-XC(Pds!T8aFzcHP*FzEz^b*@cU+2dzK}z--kho8F})Ps&*)7n|LvPCv*b;0ekUVEFyA8nf2{A%>7lt>iV8!7t zjGrpBA$u(>r_1g&szry(=#%ss(A;vP5jqH-|-&tiy z_N-wms%*~Y!?4Y=P`Q8-CCy`zMP$ zhTm4fcCULjy6=JdkHMpnyb$f_8pAF7OBLW@V2w13;+7#1Jyh!9W|FCL+&1t*v z6_z70r@GSD^N$ot9L1BdwcFAhnSAuwtF(~{gfzmYySoqHH^w>R{SoV#b3OCEuM2c_ zmU{$uKFeS_40Nc%oV9&^dyE9K13J9lUi-nbAT=^Xo(qP9^NP#-X1?WzK(Rc-1<#we z#}_BlCby)nr-5{m1uR9_+lP{L&;r(HpsflpHEF&HdA?0H9gD<#&3Mj9LVSOYX8I(a z^en@mIC0WPB0S-i(^Db(u25`RmSW=zJYMz+WHyZE5+hgJE*zp9eG%ap?J&GMKnkjO z3i!ZFIB(QWuOaL@gA@_&ZnLE{uwTA!PMc2OyJrLXv;TK~XGoYf`%a8dCjkHfS@=qg z);$aaBsh3{su8-!|KX0!Rq{QI;s*0gFEhl0osJks{8ID=y#gy4hOV(x13eu=yWAv> zGUh!Oyqpe!mlK!#am*!GjmOylkOFf!74hSP_9UH0F(wr6`n(rA~YXFHDq0cbce$#X9KOJmzA+8nt{n>`I}Fg4b6 zWJ+)$foM$#$?qz&olDGqzIp95N$C@aut$sfhMS_3&LCpDJPS*ZX5I-HD`Ze_5X~82+FsOXMaQUTTYAUo~J*u7D_SM2dAu-s%84Ie$3;0;upBWCr84~%@*5jLX-+n}&Xw9hQs5v4~u`U&Ptpt03rZyXD z;P9Y(BJMtn`@W$aN=0Lc@<^V;KL1rXj?hA)#lO&W=6c)6-{KhhuUOv+VnijPHVkGCcwm{enM(QIvEHN|F z;#tH4@`cD_0q-PU#my|>j-0!Q5S-(91cX@gD;^%lf{hae<(=dQ5GcxKz9$vC6?-eU zb=uO)KlW0J(+J$;e}xlI{i}2>!u-cLumr$*k5(`oA6AkaGS$_o9O6bruP&91RgOU# z0{)hK`er!8AZgtz-!9%iG^wqKa@n5&Mhr|D#p=5YUWwY$%>=O_3(+iJ6QRaV3Jzi8UL1fU zRP?Yw_`u5FG4AxE18I%2wJ4#(wb}S7?EkuV#C#OU^5@3Ser6>!zM2urZPMLjXl+tF zi^!!CGzDMe7?_sUNAu=6cn%OeWw&!HVk-Qx8BQ%)O?SwAG#TUgM2e`7X2=S1MK9-! zCseBogverAWW=V4LYZC(2&WUe8lD4jqB43g>9ION)1%kF>ay6G1|LXUbBoKFwFX|}N10PzxcWR9~!kyF=iEOMr zNxsQYhA2IXdRkgGN!&W6Xor#p_HaM#fs z?TlMXJd}NcpJz`0ZnG19R|1y)jOa#Oa8F8O+Q;hwmq41#A;;}@tNl9HjNo97rbK`a28k6P_@NIuknzrul< zX}N9PxTxkQ*BoYWG(63#cSIxv_ul){Zfj-TjGu)EZ4doKe1>aj{nQt&hvSzw2CcdM z!X#HXS{I-HRK64m5pO4xV?A%l7>v8h|J~%?h+(W%An)?&k1wc1*}k4In)}as$K%X_ zlE?QT2LRIisdAXoPtl0S*jz`VKqM9$K6V+YQIKGpV+`Pq}-I$*PG#%B>}5FR-I zZ#5iT{RdP3aXw5cJei-D`WvXWjprlt^z%KoKj0<2y!0e%xF(we`>9hp>Vn2+II1hh zR9SVT@_XlrXi7#OnJF!)(z#;&)|kY>^bz0Z*gI^qJ12U(Afm~-1hnmKyk_HHS|NK& zL#o8Id2?SePXvsqkIMzK|vQAG?Ai4COh%| z$OI^1uyH;Y=T73m@L5Nz;-ie)442|egy(5*1o6>3J!Ja)3d>rKTj^$zBLjah(~466 z<@aVkLN~X&WOkvP$n2v^&~iJZAgQtqiGm&1NpFPtnLA0Rst8W~RXZp{_ty7~GNS@V zw)`K_;3o?dbi#AifJ9gN7MC1OLPLbgYkT0iJO=DwKl1VrEiHy6hWkZS9@TEr-Jv7i zD>=5>u*Sg2qP5px|D??;WiUy~aMz8*Ps!Q&9a2oz;{%VO`?Lo8&vdeJff2aEi%L0YBK1jbcQ|~9&lPd5GqNHP*IxU%rHe`3SIEYVFSE3 z-1&6&{mVN}v}DrIE+L5R{)3elO$W~}JSU0;6c(UqDtHE!*JwfwWO&9xGxL?-Ng~t% zx*S)!(88hi<93ETrZsjnN#CD3u%CrspIobds};Kcm?ye~wiS13E4QciTnzU7F)+;V z`=aSPUT-^k*C&leJ!{aCm`Bc|Y7D@5@kg~>G#YC}Jjh(yvRYJ!+O{A~L`|Olh;DKM zO@#lez>F(evsK7vItMEzzBfM-u%g7t+lp0U=BF^Bt0A9g!_!f0>$S`<74Z+e1Co}J z0q2L?mUY`8TIXM^Ll{K!##7cNuA(75E$2O6Nw)L+LxRQ#^tb!t5=u*Sf$_j?V*Rql ztNg_jkxIJ$vHSY+bHiBtXFwG=H5B%xdoBpV^?qzGI1(O`pzE7unf=oC!c4! z*aNKpZO3$`@{xy;?ZN(#O>H!%D5e~T@&=x$TtarAC%;+|;FEqiSrcsOB} zZKvxkAK5Pm|IkoPf4yzA4-GsiM5Eg$Jlx(k8ku1~LZO&g%3Ex*#OvRI=3FXk?os!k zWxhyK&YR2eqgq~mw9DOEQHH~z`2kd1e45rC*`hhJ zFb?p2VidE}#m4{L)krehT5G>HPP2L2?zqrAPIy*5-EXUz%s)(?h+K7m)bfXc*LQsV ziDA>u)B4lK^U1&yhcQEV{y0yNCSruDr5I?1&2~;7Chw(Xq3iai|J_Mff<)l0df>xr zq^;Me?)2Z!A>`{M(ZE=*Zr0vb@0tyF(9uA}SZNY>+xNOE^S5B0{Zc8^&sd=4`(suKNp7kXy=|f|C(Q)udlTz0x#4%J z2;zF=h=-Wt)5MPAxoSY@xklSaqSG3i7q9e zqt6uf^8SXP9|DLB4x=Ve2_!e&HsX1U%=n~r>P&knx@V}X?Zz_ z!DwhYmx!oUC855$Ru{)6Qr>gy3@N3|u( z2fNy(Tw`DPhOFK76c`mxtme_N(tukfSj39qf zEkc^xRyZLv!v+pCH*S)(zPmy`kE)u8G<+oW@7)`SsQPG0V_ zj=^H`S`6zGkL@GY?GP?QH}FIIK&3YG!^=P79G(=ShR1+HsLX-NA@lI)Db08A{t4oQ zD?3$^IP4KvREfwnnk&>q1=9e2K%r4C#ritviD{$M&H1J2m_eL}A}K;D@R&%uH<>nI zJLYY}z+rKRq@9|1g5o@GmJHJ#SrM1?I^&Ezi*W@du4hDaPI1^s2p|MQ9i-VOUtblJ zk1=#Qr6gQHZPT|05hy^gM~rCU?~e)3g*~+~YYX*Q=MxEtei)m=tovwfqF1-w4`7@k zDLV=M+C)}ml&0JWIQ{}_%6S5kAiYZ8?M)6@e(Mv;D3D5%;upZpyYF!vDTwC;ghGYL zX%q;a4WD=XS(yk_0n)`#&0}^LwhbB;{eRVCe>@8wd~)bGq}$Q-Wnp-%Y0ulFUI(PB z@Xr2XFwVp8fUJ^mia8a$7MyC@(1jD;)84$3Eci7>M%6upiHh7*F1%)hzw^_ShpiHe zOiIHl3C3~%q*?dFx0^a8O|rj^Soz#%{xQ{p+Y8O&oM0o=0ZtY^8SfXnj=|~Y)s5l6 zzjt6JE9eaBH?r-c&CTH^+ip;ixo-zb#K}xZ0@wuwV|R6-ynJ@~ozo`<5mPX!SFnf_ z*LKN+*wcdsJSGjMCCiiA8nQEQ%H;k$PT$xs^X=o#YYt#O0q-3p&aSTKOO~jW0K)UW zNX%`-x7$cp;7Ay|ogtJ`{F(KE+A9?{KB~}AHaQ`ays_i@>K&aUvY=qa@nTP_B+77( zGhFf$%-A;fi*JASQ^c-YIBDLQ_gnW5dg8fq`;m@>xP!?I*Ah?9JS~2Ec~rKCo^!ocQ-?H?lLcH5*+uQan;nj#^`G=c>3uJ_w<}Kmh&A^06Rr4Prl!5uglqQ zL!_)3h#hPX8K0DU-K*>oe}2Y%3P!zr{MSUE^S&!jAse`F+zen|1U|3_`Y~z0uLGNQ0j@ZO)TNcxp;z23=-;^2islK*E>qHH>)?*ClBG z|24g`nAr8E;Yu~XX|Dlm10$NxVrWJH2beOzbi?q>4Mrc6p|WE=d+yga&H6qQT5Y^RcaS3&yDkU{i%P#6#5D6zOycRzmJ*QSK|aV1FrW4R(7nQm(;o|B8mdD1d@7w|3oJw!P4fW(v4p@-BZ4lG= ztFugZ<;0FmO7O5(tT*1c?Dh$19*_TAHa|l_J<-wlm7Hzsa1@WPW!_=&0i-BscHmU! zix$&QYr11(?RqXt^`=}G;W#rb?Z1YQU=UNVfgvKGt5Jxklv;0+yber(rzWV>WR#Gg z#uQOxliyUycRS|f=-Xdn`&o|g5S2*Rmx;-dK`gl~d+>KJ9s9aFF>2fiG2Pka=vk|@ z>|}V-Frzm80jp!FmyE9WlGQhd7}PeS$gqQ62qhX<@Degzne}0co4hWO+cf)J&vh*+ z-sAl<-PPz5vM_{@?eJ(vZ7hxa1Ej_`Y%07omed&^rn=Pb zY#LbQ>LsdV#avwy?jkwVq48D;27IlG{$9EIL`O`Gy_BFYNhFs-L%bS$-D+|PgpUkf zgN&ZQ{oQUC6rIZ;#XVHLqUm?>C$32@RpF;*F<5$Bg-64Ebii=ZYJmL%?Gp%x-^NLQ za&Tqa?MJ|B^h@p6u@K4(T`G8t>S+s><$q%4WY~-ztJE&=SH%^5GlY!f4mHWfuoRI$ zCfobP6c*o+TE}vNN`c&}&3_*zN|LV@^0a;%>tY}hX~T zcCUg5%U{;-+5%}HzC(mOmX)A{MuaL$FLO@#3z;4V+A;lBjq;OQJdL<7MoIjmkx52} z^FFw3C!)c0Rx&F0MtcUsnr-XfF-50!Q=vnQ|G2kqKC2|^!|)ua`A3D7b}3y}G5g;- z(>wL4_3lZH@a~VFzKLK~UZww;rB|-U)h@oUX&kt;ngZ-DjEgtIcS_HS(nQlm@e}6Q z)oC}VHGP%@ulTe^QjPz>VnhcsR(JrBVbnN!|rUTE*@AmOo# z?cS8B4JJ~0YeQIyV;kl5V0W&4x9>B}1Z_i_;G)%5f>o_L%>9GJOO7Zt=)VsBS^0Y+ zJwvvL$pA343O(HR6EU@ma2lcQ%pXKf^!$yxW18NEGdWv|N4&SY#oGM=ak4Vs@AAGz zG~iOt`w-O8}{&VO#g&@8{i^C;iCI;a6c;4c@ODhK&5m^sI;Qt}CmteKptwP`0;ExvE$!qJz zj8~WUTy9Ap>|{FDZ|7x-nZ9$1r39Ut&UJ=GjZ(|MaF@66x_p}6WORnb3J1I?h`(;r zJd~ZiI3F&(@VM(AAj~j5k$20Yu9yG^dF@ZTc9$C^nA^H%z^b(Z?SmWY&g95ZKlzfk ze9^RC8JP$HbxS8{%kPgc5=Mu_3R~Byo*gdu{w-&4;;}P+Jc*w~@?)QjL3Qy*KHI(!eGUaQ5W%65Hy^L!9rxTAc(cR9`dohyN# zKo$m1o}2giLN1Kzl(*rH*jmvo$}itrs5IyL@)sRdGhr@*Nvua5FJwEXjYLGj*W8&c;Fjuhj*q6>PHK|kN_!Vy!0Kh>?~v2A{|u0ySAjHj1$dmT)%P^>`b?OeI|NVdWv1Tj>0 z-!hl#&HK}%prk#Y{dZMdG)I%J_53C=P^dHMtWf^k`YY=01sX3tyGqf~rJjpgC~&aj zgA6KGlb(c^VpQ-uxQnmaRoeExTS;Y4UFV_x*uzYnOI4kkHEzo{1jL78j0 z#e?X!5B-|}<|f9jC7aNO=ND&n+Od18aUS z^*2^0A&Q^rV(!v5^PojaOmKbyvp>v1qwR6&2}RIVoV7F(FL|N6tj4@c zQNLdj3C|GHgZczimEMU~z5;T4S=^N<_?pCxM+nAP%o=)+;&PFOi|rVmd}%|m@rQdk zj800=4o*d|Ti+ojbXjgzxzsCzV4-KM>{gO6Y+HT8_?#dm_cL<6KJX|RAh2%qky=9- zj4~KR$=fZI|0ym6OyA|i?nf#=mwHmrAYM?{fv&U?Uh`=*2<6F&z!L*C!L1W@EsmCj zLv?vWB9gN)vj{Vyf0KcBJS$f+ z+e#pVJxw(1EQ#Bo6Nu*NNI#_bh{F~DWB9A#cZqKa`F9OZd;=6v&@b(u12_e)heRxy z5*uzv4Z0DG_Y$w$GA&+rovSTwCEk0FXX|fUZvcJ7-5DljV3zm4{rxcHn{!oV7(nWc+xcDPl$SS0JpLus zC~}-gJ9J}|2IgOSm(M^*nIRDkv;3z9TwjcxRImFc!F*rJA>~uM2aWf`QJ|Fh%jMEu z{^Yu8^NaMbCadxiZdZvgKzQO}>wrYx)0Kp2{z)?*XnPMbFM z;ll&sTT=BsC9s^s8I=FI4Ls{bvJq@?CPDCxrDae4%L0^r6}oXG&5)%TUGRMhAD6^jsOR3Z0t3w(;*%9Ci>QBjv?kz}nPGtszmpH3JJvUwy6 z$x^_wo+^K|GM26F5zUACoi-kobnM{`uKbm*vm65cT0Z0K(obCUw3Fc`sE~+DDe^Vc zC71`G4HZn_HjHqUj8gR~gtn;lW~_S7nbvE~X)XV1*UqLF#9sLN&@P-%-J=LlCZ(0J z=UT-C(l+}ZDda}J6p(G#du6JT^^6VS zliq8bRX&b67nQLjBBNC>oNEG5q<2CQCgdY-@Tr#Yg|953x}0>{n^$~0X=JdS+?dyQ zi$YJ{wkcK$!#fg}X`}mTR6Nw%CXRZNm8YJA6;_tC+kjhRXHD*Ke={_#2xHqVNPnt} z6@>A1nhN%d3Sl$^O|=BmUH-&-=I;=mdX{&cYyWSn%kGEXLT4VZ^wvK>K@L?Lh9O%$ zq~O734e^@6Q~F)LNz8*%;$7t_^w;gY!F~hwTsoDWW69tkk6(-K29#Gwe9vK8swnk1 z8Lj!PQ6tK9uR|#AZ!jaGG4%XPQ_I*#|0V>O6UGivueTgIdR@?^E+pFyApjiUSF%xr zzimPDpdd)LRt^`Je+K}9jfF2b_rxWO;e-kd?eXMg1Z@^udTolrf>#ffXP*pYJZU}q z6-RVfOewjIct7mmb8HA*%#Yr|p4F`J<`^12qKMLb=3WXt$Dr|qAwJNqQS|n;NZ%O@ zy>z%UjFY+aI}s+2Y6_$ChT+G*^?>4sw(m*bMK*y%f)NIYc(~q&1xC_V>il9Z*!r7P zq4Jm{d@K?JI!ADU@vtY=v@4CKgT zEp`wRN2@rQ%sIA&Z?suLhqeyVw1)Yjyfo#C4Jv&aT>?2Zu^kdfYUeFaBcF z>G!twQY*7S8Ti)IHHt{tF46VAz%YlX%XkrpYF2*9+%yDw#b+?r0t3cEd(l z<@~cQaRWEP?2r!lNjTiS@4v6L7n^Q2-v5m&S7*sisAe#Tg(SM=RVyLy|DSmELKriN z;w>-*BH9XW3tRA7=)4yJcD)|%3i%fTFIYv+1#bMVC$Z6vtju0se#d(&GG5mMtNBjI zzqr^0GXlJ;%~~->t9!6#?u}S{$kSd4&vmk|b<1z|r0{*$A#kZ%vWU)LP0>ZVU~SK6z%pv(Jf)njy020W!Jm%*0V7L zH??oxfsANV|7nfE)tA>(=`osm{P=Az3eyUdimB|)ET#9tqjsu`prx&HH~oQ6 z+HsZp5&fl^eCy|dyS}G8I~YaGCs56`_iHfnyc`p(C>O{F3H}Ef?sZeS?#LdDR9t9t zu6x($;~TApsAU=C3&(pW(~sS!pVH2hG_%3>?@#KHk_wrb28l5umAI-7Yga~?{-Xm9 zXTp&q->FfQ>>G(j486|3fY&D6$1PL}*%)$Y2?9O4XS%zzs3>$jW^n7pc^<5` zv?H$Bi=$m2#>T=flp4QFwjxmeri#g!;i1Zo#wSNCd?fZC-$$II1`cyH#yo4T^kKY^ z(H>G4rF7dUU_t!f(B|#+Z4oyW4ez;M2Bn&tP!4e>7*CNtA|qa-RQ2Z@?scbPhA)3e z{C*kBOUU`NE9(8JMTo1XS?1?YoF~SsbOmtBk)qqGjjmxDejxA3A{-@j7t)8x-9jfg zy%qN6f(e}fPxq8cNkD^}nIf7B*?Tir=kwP`-lVe}48~L=O9jAwQ#eWZI`9#S7u>zL z7sp%y<)`rW$vD?F?DiW4tW;pgN-{-_oht1Uf=Ra@Yq}Vhy^pEIcyY3d;6V_?>D1G1 z$32>#d1kjDVS_c8LLpp?tFERT;p#&^pBEuDH1wj(s}cg67ce9>RH7=Cq^DSN%4zDr ztgmPsu6Xx#xUiU2VW!m^8`|+)Poi`<36W|nG^y*{nr*}U#R+7Yy|whgzml#*`r1Cf z58))g>h_7L74NitpkJe63j(kZl9)*Xe=4q%7 z@(c=TJj9247X7LgQJgcJ@D|$vK)13AP4;zenYEVgHVV)SIFyT<$ zQM;)qrSzFIGb$CeH!>>G!dKYa_HDC|7_(W=G&V1zPXqP!{KZI0+;ri;QymOk;dY!e z;Fv}+M+PBMq8~$eV1uYkX2p2^iT!(7S6tW3G~Lzp0I!QB;3JT^U{1Nc`GJMB_4OZ0 z$4AR+#q8{#Jo%r~g1hfYizmkzlq!GX;yJ#^za1L zpmW2~l}K?Asa?8V`qTJq7XMuEYjRpos*~Zo1A6=@ubl^gZ{lM&3Ji#t&Nf_`ECq-`%AQ z)csAz_ElBjqm0SCD1|OW?ArJXw!rY8B<&VSB2{1qyZ+e~dKI3Ld`| z+5jANVi`9F4g}0i_m%N%_Nz zUL`bVy-y6c_IdwbgYhXWv`BLOz$G!RXUbDu*2zVjI0Xra{E4J%?O>@74(AS0#2Km) zV4oGd-hWr+gw=#$uh{H|&t-~Dl@;%fe$8y!boC(W1WEHxgd=_wTT{RRIs)gH0BZ>EY5J!Zv# z%H|)0KI)XtwvoHxob<7*D+&{Go=b4kcRpl}|IF(ohpO}zMW%Ftir5$H@~f6Qeli=` zBEvp<5VnX>gvmn&PVYv_Ju*BZIRv0Hab=C@3>FZ|OSiFKox?yF!bJV;*i9Pl5|>p@ z-oU@wPZU8%;B7ulBWTeg3ye7fa=PN!RyOsC`NWb}v0)a&-DLZ5Oaq4}58^p0f>l0d z664{nB3xn>@DoEsh9gddu{*TG;WbOx6TuRhPP4(mXJM2C7K%jf49YaGTmTcw0yHQp z6cU}aevzoi%t{i%W1h12R}y2A?p#_J&%x6FKshyJDd|JGo)j(N>vQ>{vaz%~18nhC zuLP{UJdD{dzL02Z^bz4*wus&#<>n~0!mcCYT>wJM@4&bL1&*ZJgA6TR(Pnhen9su3 z+I1%q?54VeK5X7@{tGvg1w{U_IizJm@mp7aFR)_r9DWkB%5|Rq8|e9yE*ah^mMo=0 z#hXr1k%AxRLXnQ+-yeP}nD2A|(!Mw(sE-7Cb?}*kd;hDavy_Qa5Xz!Z34$eujR3NR7|K zBmu^L09;(=8@1h|{RzvtZsusxqY;7_4s_6PzZRU~g3mwMNfGYl@MJ{$1RTCp{} zE6JxZXwMz9`iC-!f1}eteZE7jtaIt}nQ`31E7PaWt)x6kE{$SN&yZNM;(VJ}Z0Kc^ zQ|~UyS`j)h;T2m8gLyh1u}41Ki&-5)2o?nz!RDJsx-SYg4dBQ^ZYqvnaY64W|E}h( zrQBzLZSnS8cRhVk-qCR<7Th{R;x822cVaKe)!*>OeD=A2Y1zv^xtr&nGX4_rQoYQS z&au9}cPr4bp>o*`iwUPtzYM|2j(J1~V_f%p*i>D+&e9#d$!^-|%fENO{GD#uXig)w zCkE-%PHQ9D0O*ccwbIVfJ*q3R5bt5$Cf}6OI#mpNb55ggza|IwNm@f{pno8-{=0z3We@KQ+&{bb3C)`)ur9T}}U3T!|ATdgj7|Wxl z=jVUyk>XD?kAgLgH*&4JMJSI>;tN8OQ|;^Ds-1a4X9bZEljcGce^ zP$TN+y@J`Sxkbht0=4P5nehvUF`n#3Z#AY741aQNn<*Po{my~gIqw#{9N*L_wx40) z!c6YUz&_>A!|Wrx8f;t}2f?o(f!G}7Nl3cld0A=jmVk!$@|N76v)C_j>zqtOk-&7( zVDm-O(jpd&QiF4np1&3c{MD!5qPtSJ_ERaYO@Fn7pe)xA)@&Mx$RM#B8FN!G6shPs zdS?RTH8_%dX)3o0{z+sI^MoI!&j+z~Gi_7o)_;-w5*shq4;}T)F~U=aLItF`Q7xHXBEa%Oql*@<((Vq%S)JY2Vow+6ah*?#xA$4A-!|N@LjjONehO6{};5eLC=#Df`_@pK@V% z85o8NShX3P({e^@q!EpHJL>Rl7y(XlmNSpzT>arOgEO(RnpZsit9+fuCXsirq(xx1 zJ#1Ao(K%SAp6Rly60$$m$+YOv>LY(i%JMUr6jXgsOtKGno$C!}4#M(5t&CJl{H0WG zLbM7Indp}>xuKbC-~A?-lL3KkEXWT%o68rR6GD#Z-R}x=H7cn+pZ`!%-&aj~;R>x4 zAMu@&w`6oipmP2-$A#T z3MA4^+E`3a-=KcQm+idBNu74qbRUwLz=dIXi$xzRF3u?}Zb}O(6anz2?l~Fxwo#U- zhL;s0#9pmO4kzEhY1@1Jg8EPA7ADwcV-U2Q1?aH!X8TOrkZuL|6e~v3EW@Dz-3${X1%$Wy*Hh6vDOT7APM8 z@2W_Qf5y>A+)+SGKI#urM~#FwT#7DDdI4pE;4(g^X@950zMd;kINuhIo0N%|sd_pd zIvD#DiKUi}51w7`y$*eCuri;COYz_0uFdr{aA23LjuGgB7y+75HJeLvnRcb{JiD#b z1^CV>XnZxnZwg(FL25BpvhXYmDhPqU4iMt7j8qJHLrAcd;osZ{%C{=QbJqMuw0CM0 z4{4@R#)+2jZ1V@tjCM_u_HNw?g_L5)sg8K~w_Li2zj+P7b zE)hhjq+sKo7y&~^e6-Ejy>~h9rN+o$0gb5Tu*fb^H!J0CYV8-Pxr!F%L>8DgH8xXx6t9S9P$ zs^`aK&ZcZ}zFF38amH#+KW#~&TgEkgBJeMuU_7yO6FmV>lIecYCn2qlKT?gf&E$`z zg>T}oaOqa7;HS3L!@=5TG4ZUymVGhZqz?yjp*tIagUyc1U%DL)D`=Fjf1fa=Acol9 zXaMvs;6Of0;R-@t6)~Y2k{y%yfQxz4f|>`pT6JshzsHzpyv8lGFa@7T*qOZZ|x~TOi}4 zAkSX`yRPgtNHn$GAbVe|0vxy6L5<7HB)WVfU}R$DRS;Vs zt0*3)dWTw~keO&Ni@VwZIVnW_&#@()nhdeV#cMTD#E%afeBB&2+FxUVA2_OU{!$R- zVO`e>JDK&lU)(x27XvxBfT417u4IZ{64VPtPw>4&;PZ%=%c_&z{UO7M9_ z!O)?V0^!z}P`F$T*{JhBh$m!!Y}I)-e$Q32R2J)_$4LCKiXCck zBez)#Kanc)IfBcj8rrfS#^W7IKL;1NJ_1R|c7`!poF21W_ZSKT0hzW6Q4DgebqbSD zskNcDqaW%xDREo}saUIojL0FQx=55d&?Hm17j$d4l`L&jU-HJ+kZoZd)b@3g+zr~3 zXW?5_192@nrlCz@6*qq)_=>(J5;bn;#5Mm!} zS?U(SZC-PP)24#L0YaqS3MxpFCCXq`tvBENwbczK=xga(=}AhyAm#)MLy&|vL0<`Z zm!9HWr~D%bY|2E)i^*PASIO+Hvktk7L0xfM>UUc7;2N44Z_(f@0{ti1673HfxRDo1 z`M}t*+VPC(8M|}sbep;NCxqm}vws>H{ZQF|xMqKNW?HXRE9ElIq2HAK9khl|3y9VmPC-l<4NtSc*vl^B| z0_WdrLIFGn1?#7Gdgu^s@l*NrAWm`wlpVrMR=&tV<|LcT9QoQrE-+kMCTTLJoZXn*J@1#p4fR& z*Va>2!7sHXDs;~FBcGyK1ulPT1Ro;B{E1mASLY1#TGS22M0#FQq<9iKdnzzbS8HF7JC>9HU5F=-M>*dE^0) z@(zlm72Tb=#bPEIkvc5`9|)&`cWz`&hjtW9&%91*rOb9 zqtv%v$A;Q&*_n%kwzY0Y5TuK}c7o1*2`c60kVaw|A}@^fhAB9Fl1XW!dosboNcCuVZMBW+P|+zSz-T)nDgg?BOaM0!Ye53WcQH!;kZ$Yn*j( zmD>sQlsPcn|2xe)3!-81_TW7pC_1m6_8La+nsVO<)|J~{gIH4?e*Hg^zEy;H2UMNT zrkfYl0G)))r@tEKE%)IA)EX>*DqNs{ZHbq`VR9f0suhOA#AH}zj6q?uD^yh1q!IvD zDaBGisH`fRIi6hErxwhStT%+t%%hSb)9!8km5CxI zj*mb{3d_)y&xZ*@wv9bON_HxQNMkj$rQ918j(+i#w1km@bdQfGAmViv9pDYT)|Xn9 zMjJGT(-elD@S^0X`DReXb^j+^rUwR}yZcGGq+06<*`^L!E>8xzBn&gz<_8dW=?BpT z3 zH5zy%*hoWpO>JUNjPw+z!nzw)=3JWsg3~paFxfX-sXWl(357+ag*iSuU8m{v4 zE<*v#TSK-Df5&T#Q>FNd#giuMQjeXuli#fIbZK<;#H{5+hKBAAUwUHm1u6JKbyoJK zTsk6kKHEyoMD0k;6oZa`qOG$#H=3kVlVH;V>@q9ElB$^ng~teaew^b zLkunzH)#2V$~&JRhv=?UAYn815kW`3VZp!Ly=H>|3a)+Gf^KbE4ZCEcu$CLQAhsRaP0+iTQaK~>i<4ke`@(#E30#ywc&psS=%SJ!ev7R zZj3D;-Y!7soM7FFNWElwm6vnCVn8eeg#ABb_>J&5`SiW_uM~Q+Jy##)!CFQei}Jgk z=N@lv%j(U6R4c7pj5+;DcCg6wD1nr<><*B(0Jsctd~Jf1P$5Xi1|xiyCA?ZA|GrJi z6+5=Q2Yrb*J^5ftqs&lr>Wj_5TR}0wj$Sg8EW&C5oF@uJ>M~lwl|c_Qdn7urrdh#X z24@4m=4V(@+tXn>RxbBmFhf<8QrZ=C_)C*P3Bj#OWh=AQ40aG@@rv4GJ`#LlAdd4B zr3}FMSvdYZE$LrezSzY>-tim3QcqHQ?6Knn=$0C>d0BQNI>mZYeyH#wvlDt&!!!H{ z<9V%RwvN59R{;}7$$=I+_IDg`r28&`;J1L5C@3pZ85qoY?ARAOjxL_$19gIR!xZU< zydWRO&h83K;1i=UEX85K?_w1F7XJfR@yMhLyY9Q&lkCf}_tdu2TfXnp!p;{I2zK_= zX6?J*Tx{7~xx9(=zxlor0 zzD;a7n`3x1%qk16l~@tCZai{D4xpxT#@@wT3^PuISNT2I&Z>(4g(;t^E}&dWT&aSv zyCjd=6fO89@kscwAs!H?PUl$TpnQ9-8m;;trp|(`%5dA-G)OH%y1To(y9A_D>F!w6 zO9?35Af3`F-O`=X-AH${4(I!>eVzR$=A6%X?lFeRqxU%I0j3pJ&_EYmCxu~jt>QCi zxETNIc;rs&H2Qh2K5!vo1)NZLa@pTWdqs4)_udvd;9lEdaf^q==di|f)LS-JG zu0xnsJYhBth=lEbhPLVgUVzI~VpcNF(C_i{10yfwr1jG!0eaqzgZ^W%14sMy{ky#J zmkc30YKeJ*Cvx8&A79H;7BHzUb)nJ zp(erpz;9FUJiwag1}@IJ~ycmI`X^pg+sMI$~t|~coV9RYiL=pQnK4rjctR{Jay7i(%w&QLv>3C-3V_u%u&$4 zlMr&Q-CN6D^H^O9QT zc5_9DmECC)Qac(EyE3)ZbA9!R=%~Gkv=a!Y-w9*qL-Ml){fl| z_d+X_+-M$?QFVLN7QyIP;odARbFcFWZt%;l2~y(5-*4FeHx=1O}mxG@AH#VM-~!UX$aF6G>D34BFy&ZP8p#qnUR zJrlWuW}%DU6Ku0C*u%N?$paJUjGH_b6{xN;1c=<^OTfI&9R<1B2eTYjbEiMMBtXls`yz^;23{V(wQpmh6P#KM zgu&NtIXzUP_5@M2yu$M4z9X2BWGMRY00r|h`eZgqa4OmapoQE`^QF(Pd+Xl;b@ha; z`4)3J_@E~j2aid9C_#DI0Y|qcuAo%ORg%0-g;lz^HizV&Sr|6KU8$}XfsMobMgjWK zZ(M@3v%}c8R2q2mV~OUGgcU^Alp&17Kv?KkQ74sNCMrwl?#uyGg?VRemK|N5go(-+&n1>JLM(6iLV@ljDamCm;S*3lK zDyz~k4e!-Bx!g`pjN453@Lq#iCh^6Y;k0!@ca4q2;2RZxO&_gzBJaPDG^u2TJK1h# zF5BoEAA~yt_{6Zf64iy*?3q?_z=kn93hK*pgTt6~6&m|K4iO9<>6}fk8$JjzQF_Ff zbiiP_=}=eS(HMp_j4Sw?y(oi7?Zjy75v)XuaRa4ud=NpY^4*^nG=7pi#;hL%M!}F; z)<2eME?H&@8T3IcZqFpnjmNoNim5GGJ48Ao#SOxm-c56|+Mjs?D_x#XfE2@a?@0+` zBW2=KR>Qc+@bd-7cJiFZRi_~<5XAVhPKWf(Ki%8n2fpM`9iPZuvteh=J3)g6zPxkD zZr#m`F>!0-IYLN3l~&t9P*M;)@rf!+b8cj@+Iz3lIE>!5NPGY8d- zIsI8`p|!9I@^nc;;N_+PVnbJ9+FM$VE>5o5L>GwK&Szf#)Hzl*Zu+!Npy(``MDrEX zUs@d=?B=CB;J~@W(-8Y0PCm9hSG8#b7$|{Z^nOJDbX00cD$whpR5ZH3BzwDGmLpAE z5PtCi-z{71V(a9n63Hc?;eP34^G!Dw&jA=?NxW>OzN6Oh=E&;-BwCE-A3T2JnGcnn zxl(DSY^v&)dq(YwH#)%QC=B;#RkPH(IAt9hJR7*~?H}xRqTCPrVZFpGsRJOXB`(yC(5>kUM3A*PQcL};z_K1sVwVt)=<#=D?|5}i7nWCr{nVc1N+ZO=VCbj5~K!Tq9`MGZoIJJpPgPKp}-jedgf^W z7?D3(2vPHf*Qs5%n)YC{q_*;pfRix><9L)z85?qbmy3Db_^r2rbc{6bp4)$rXC`kpa$St~qJt%L^+Ve!I*?sr{Bx*c`G>gAJIRItqQu{Q zrbhv`+k04tW2F0jV2nWchp91$zPl!K@`V-Bc+vY3HGFb4xoiE`ipUxJEHc{O8$R|5 zz$r;4G!gtNuN~trIdIHjufe6US;`K0e3jp8U$&RU-)a@G8QAt~ofL+VOm=Ua%hb|V z%r!I&Lq_{M;Q1Bfg!)S}g7F6F;bA*3-Ufx7`Olhh4fSGYq zeB+k8GC}?|f3$V<#-0E}bMmb#%*iCT4`P z(kjYNq@lf9G*$+atxTzcL>b{tiguZAH7=s#=G>0F7YeLW=F}*en+V5wf<@!B@QL($ z&I_3LYg4Y@%L1ZFpEG~EKG6Xr21GVc2R~#HKqH1JZ;63i>(NVYHH>ND0-qDa+c66? zv6Ef;S~3@fYYQsG$HXQO_Q|nCDZ0|@qv|d$>;w;C1Nc9IRWtK`WK+-yR>bGk2N=N$ zA_dShrCD#dtRf5w6Q5G52jd)%`crvdwwz0Zk;ve_W=bgi{If?e8xR9_?1Z2aZYVVd4*?urLt&vqvR!HEzNyKb46rDG2dsfSeppSRUnhi$oW zhqSW2tV3`1;E2JZbst3@rsP8kGe2}D<--r*fnYG#42BE0ImWyICO=w3(%*KznIDwX z*XF}G_NI9?GJY#Ubf9!3tC1)9_EFO3|D3z!@GfIU74){*r61fr@A7pcqtm2gNpqwC-KHB}ha-cbQu^U1# zW`f3J2vL-)iOmPP$TB4Dm+hfmY{wu8857OXo8^UxNMWAMk&PTx4Qmz>ZK0FQsYDi& zG@RZ$56I+ZIfHuKE#Tv@RZ%!3Hly~B$>@f~)dbyHRg)8fWM`ks#Q4~P@$R|ls6Z%+ z#rGfLe(%}d;olQu+B=znbjG18=c-se*s>s3{iz;Oe-H#$H!CmRM?wlzm*=pAy#zPNqe0H;_1j@lD%~CYX0WPem09At=pNdrUB#r!?ho52BSA{3@3)<30ucHFzU%6ds zb8DX*TgXY-@WZ7Nm0>xf69Oj5rg>RR_pBY8Y*HltyhL>!U--LMZ9Foz#^+!Fbt#S?dQ988u3~v+iLKK?Sg+GRL%*cK5_aB|Uc)5H{&wb!a(T)S zLlmrgWZ6GFk&IjVyi*BpE^3#+iR6G+or=B<&n478*2th~PTl1Z>Qz3*Zy0i zYeGa6v2JVX(Kj$sj#Q2MZUn81m$jVix;$cCXnnO1ic)s!$8Il)VJD68QK`ik2a49Ry(tb<$Q%EcJ$!Lx(eS+f9EN zTAWIAP0xE{tbGC5)Js%>!G%j&D*n1Ja=%#7Gkb;gLhYeP-|;XQt71fW5wm*EEJ4+% z$C1kn?;laDtTv9^##3aO)iw)L+rdg_DTz8W>$+X%+o*EpbXtIH8@?PXbwqcftQ zkf%!%uO4~xk(HwC<5WZ6)x_3%(@4TiARA|4FfA%HQn~BT=aJT6&5qOiVGusix{*?t zBq@dI1WmeurQWj;i^D7OzDsvmLyE*6>Y<(m!cS+6mXTVY`&<7xl$6uGQQ9TeiF5~T zf;3rSMrtw*zXaf|hrh>)Aso&^XZS2B!A2xkCB3V60Q>S>*1_{roiH8Dhu@-o52mxw zv2ilCpYt~;k>)grl2`~Q;db((%A`nXmN4DzJ?x<___smG75i#&Xv_&aQsfkS<+#dV z+~VXDXTc>Dn7!e9;RDW*l!V@F57f2lP&ZL8&4~M}4H!V5USH|8X!CsYvPeIrJKn@h z);`Fvha&U3T=1v(H@2ZlB;DfG2-AFosKUvqY;y)(LMd|<{m&XhKTyks7pX2#pD^+v zGdQ_ULos8nc=q&^0!&4dMkWiic{pdseA(w+VpmfIn+Poj+x>4Z(L@V^7(`dbah)@3{hooIQK(4qG*8Xj}b54GC-ajOcn9D+>j$?knKx&+8y@N2&m z#zW62Xk_V>>p%xjx60&e!EAOp?};o>yjvUxB%r*SP*dza!T#vCc94L~+(ih$th*K{ zeoWZh6CVG`M^>}ywq*?7Vd{Ls*z9B_d%Ahvt;i~hPAj8;XyY4UXctsOx7e2IY^Ou% zcgT8gHV+?m1s9nRZ9a6p+Ph8LDAOAL>3kWa;j2jfZi29a<(K>d@^4v24?oE1cob*> zX^Zlma*R)fJMJMcY`|8rgwa@Z>sLSS8fu z%j%23hA+d#wa5B3F)`Wm5atCioAASEpkkUk+`(@?Dx{Ti2yTGOQTQ{!PP>TtV?H)F z@BZC$8eZ}nsfmB3!Wj8LTCd|hOmyf^O;50)Sx+b7!w588R_=0NT9l_7SDn8*anl&5z4gWTvQambp41A>Mu!l zy+R?%xeQiqpXbc*%1E$;^=uVkUJk6G(o1C1WwD3Vz6x0NSnW28QS0V{Q$larYya~h zGiI{1SAlVl`0oso+2%L%Y5wAAm!3!Ixd75WDh?1>%KTp!Zh6L*AhE#UaOM_%x z4c7-J)_VIyOwhsTey$Ke^9G`G;W9S;>Aasj<;-LHfkMMMMD{u*=0q(fyB73?Lx@@1 z;>5p?X4L(s9iZRTmzos0TI@-}3$p&afKJ3=$7?WWMoC4CYeut*U+~_CI%tw0M;0JY z8u0K1Jh-y%`rs!|3cMaaRni$l(hOUCw5p8#JDa@rM%=FEX32omU)hh%!jEqz#Q`W* zRB8^xJ>Ac_zvR+u8D=B)l~RZIc^c@aN?V4dkWIuVh9I)LU-#JYOEZ1SbHXlNeLpC2 zY727>FH-qR4mvis^{DbYYS5D8xH?;ltb^7JC%?a2ax1%`>BBKjmYUN)CU8pNAjp|H#)2$Ea27;dnwEfzP;aikrn{IE`4 zO}|Zgyv)-DRyAJ4F5KDHgm;EvrCyizd)WCK{e|1#>T2W)rf7K0;YGmrN(c_xR;K|Q zjERJ;6|=ycXyzVGwK^#`c%6C@P8P!fP1gNDbTW_W4If1T3(MKxvq#EhViOA?&InkdV98j20lWb3A5gVu~ zvxp9VYO4JFntc;9sgvx)`V+exiuc&)LLBsia32uY*zg21OMtj@(octl)w*~#nXR3t zBKOfRENuKI$oZnVYVoG@#s*Cf;2axn^kLDgFJ#F=W|w~&bzL7OHcd%ZMT(h8v?0xO z%l2TxvM?cpj?47eTIZ0q8{+Cu3NUv#N`2^lI;>ggT2IXKqe4#_&yNPTayuYmn(zw(#udWhDqdY%X0jgI@#XW(_#^>iRo z;AS-tTd*#qUalt6{fE4z#}n~PD{T72NNYQ9Y>%TIMeN^Z^Xgi7tXFv-* zpVRtH;66;tvSK-ci7t67=LOAXrR#q7_hYv>ZaSKfr&_4WlgRanPK&br__x^W9RoN~ znc9iQIe|DfycCqHl!bOF)-%@OaM9ac3Z$2OC`{yzwPxE7y8#?3q$_cOh;>hfEKAwq z_wtOvGs&&go$=q-CT~3`vb?X*3;{x6^tSFcgZjF}0rMmr7S0it!;icmHNk3%>h-sF zVb>#d@ZR1#7Km3R5QTMDtw^j##w~gE@u>@vB&TiNi1vyMCNJVVvc`_cPe9uQl@7XL ze?xZY`@AK-S;(s-nxcq^!n#U3ft5K|IC5#Bj>;_T)xAFYLhs8(o?29!v57x&pSX?juO{;(CCRw@qT7YKe|Xg!;H`o(b1ha zb>5SeSq7u;rr`m3%-S2ExW&$R^6|aZ^^dSJ-nZtjwn6f0m%$cxN0}k9L%g4@%6}_tQJ3fEdm+HQ^+a=m~^mx4{ zJMg*kCri2~eS00-c+`7rF?o0<$)R!@VfNEYKy_0Dq4{^0rUl9O%;6{2)a*#dT-a-_ z6ybZjA&SAi5lC;c7qx=ToLdc=u|KDU?&>=0jzi`<@PQ3lVrX9{{qLwk5xa3{X_xmH z8ps5npOoB~X0epDcam7w%B>Hc#w6KDkQcPJjf)8`_-Sx}jZHt?@^dvWd_qDC- zsk;h$M^pfX;55?`N-{-T(n~zDJ~Np1i$F0s+rtd|&w*$@TK=Y`qwfyJsnN2(o7W|I zCrpLdlqN2_h0WE>Ex+*Z6+BA(0uc$%T{eOaEYaz9^LV0jrEj#R%QLUvL_%z!NzFk^ zXbHSV4w5xL`wGp`!g`6j6*@QYDyu%%HrCE=Gr|F7?Bi8rBI2tsj%c)JJ*EX*7}zjm zP>OM|s?nFcGb)>4BKH#1{S={1rC%)Rvg=_{OgPIWd6>;>O=Fp7+<8N*vseZWt2LY6 zW%qaV;H4kMoZb|?_Tmgyml`|z*WQwJRrhfO#XbBap&dDDi>|C5^vSI$AB-8iawNVp z!lnauVYJB}jtLWAa>_^S!^AYMGCgYnqUpyq=KaSag~by2r&)$4v*+N z5MdU>V$&L|VrOc>veA0i0p}`lkOc$t-NP7Qjpi9hns}SL9EW|}Tx#88H=Pw<&n1@_ zP(;-{SUiK)CgbvEfDPv;PT?d7n(^PN(c6~LRFjo8O!<=x$ID~}74$DEsa45V*tQSC z5F5;P2q(#r`zSr76Wmo-UG(6n+;-^)+1vhxBCx-9CJDUV zovtWcFI07rcU{ee?=g`<7{UDoCbX&N?@I+ZCE86_dn@zX9vGDqT=EsLAd_QiAiNt_ zwL>TJ9xrnZ3CoJRDbnKAE2tf_GzR{iI8&|RD9*5C#}xQaC~QF0&hBzmxRzf9CflE% z+nob86=XqH3Yquy;i*}fS!;JHTA$SBU3bJ65PA%7y?xFwpaO0Ss^fiGjZ^U6H(f2g z7)xBob9REguX(TH2(iL*F{ALD6nyAPPZ87I^JUao_IPV~!+TzPc!gsv17J4RhnBwd zqcg|*k!UYl>f#<-Cp+Q5$&jHrmug!~k0ONgAzWogdz;<@VLbLaW%OLei;~)F-sJ=a zGU}N6qD?7@=vO*NE7~aZPx*FJ0B2bow)CH{@txQbyN%fMeqec1cCs8>hcapFMUAkR zCy%0x-3TyrTDa9<#9}mk04Q+BYC8*=%|ZORrZpOk6KQ(~Ql^q61!GDP>bw|wyCHIQ z=yyLP7WIbd7Mv0qFQ3*Kp#0t~PVe0Nuj8D<{>YNb^OqHH&wOR>;o9;da-So7T2t2G zPRDh2mw~g_+pA-9+KR~eIoR)@YXc&Lk6?3{u~E+TdH-^UWV~?-KwYm4_{CSL{|Vl3 zl-oFr3wie2)=rpnVjdnL zkYgH?VhR3QcZNI{sQuSveU+AKi>g}v=*#SCdv!{j-RPadX~OGFCkRhH(Nd#Jl)2h2 z{!fJkE)os@JjVf6wU{dYXPxem9#$tZqQ^p4+;`36QGI!kH-@orM2Y>Tu4Q{f)Umd{ zE*v$8EI3%~26lt)z(@>|ZE^x_>Ny4){mLJDM$O~^=P}L;yyiikyBJK67bZyt?suO> z$;IxkyXeIrD1cg(VTiDj%d_Ec(c+uud`vBqqiT?P5lm1YMQn~&?|?p zNfIZKSu*ck?CzG^K;w>Zf+HG@5WpC9NcJ<6J!K`9aQ>w5Aq^`UIg^TWQ=%@F_vV-2 zR`6~5$1E!HilyLtqos&9NzG#aA>E~#dN|w34HHQKffA#1-FgyCmTt>;YVKB;FOCRl7xpWN(O2PUt%|oZA{ljToQ|Uk54~qC zoXqbt`6em&A|8FGUPlb?YT06k4N`DFd~9-!@c6U(X&kY&?H$EzCntN6Qv-f<>;s6> z>`U?~qfKe&NSO^H@V_Zg(V)i{KarW^79M?OibHEofuvdQ? zn7%B?kcpORkrG?jZ=b)fG+;!4`MUrzt z-0LM8%lZr!E#42W_V%&6Uw&ptHX=M=xZ@HLT{t?*VkeOBj&$9wBm)LHsfcW%%(1K2 zuHY{a!^bV_=me_}uU$lw@Z9d=sw6s&fZSXs()vZ++Ni-<>y-FATk(V|t~=s?P#xV9 z8Gq%LN915rumTH1o{HK2Pl)Q2W<}3mKWeHxukUpX@N}kEIR!-ql{y5(=H#r5ejB}6 zcll%+3WNbTUE^I&B7CvoBtZuLHmo0O3n@c1A4sU4WAJ1oft4HLu3t9x_8^mC-4Kr5 zfdoWf#8i+#ec?Qh1)UXLx(Ie8*NJ(w`qbBNU5LbGW*6GBy^1Zb4=We#$%!-U*2T4f z!?ed}1>E^A_-l*@v+}R3-wL+)&IsHza*zftt;B(x*Gs=0O&iM3HTdgkrbJndmbY)4 z(s4HSsEBHFkBN?(j1C&W*ImQ4k`b9 z_V{R;Yz9lZSebGg5i3qLzFSgV3H3=pyK&!syWV`lMqT3N8~k&ldj=Fu+Wf(Bd$Bq% zn^!z=Y4T%%uj82LeQ;k_534~MAo8MDZveU2Bjow=ji?T3Mo+0-2M@RI4DLdv{D*@& zneNwwQU-=5^KXg*zF#}rNfHR;3b(H>hp0fqAis?+R8^=DG>J=vSi?rm%bF2)!9eso~$T02I~m}#=Lsg1UhM%3~w^sTgh=->TL z`EH4Fvkt*+EiJInw(}w-+5}CAvlho87$VH$C@`7P2m?X?CCTU{jOMSS9K^G4CB9`4`Vw2$&8~o0JY{MKZy)A^N-#1->0sT-$`?@ zCm8T!Kb?Jf6(g6My*(*(F~-6|{HVM0ofc_^N`_rappb@OD$DPyEucP^>-Utzt45@n z|K?&`Tw+YDHpbtFC8$A*k%QNci zTP&K7Mtt+8dAQJr8O7j)Oe9%#~=>C3{;(d6b?Dy?Qi>^h_N zY;dr_p;Y`a%nIciozyxMytqJRG;@|u<=dAr+&bc$X7j)f@F!l=xnSNET_W8aj{K~` zkU%<@kKX+!-L?jU3$T8y57-VxWYmM!8X5VF?0h3E-7Rq0-@F^(GeuC=#WzAB>wz?A zUT=dfMT<9sbYH8z!XD<&-P9mxi$hD#ftGhbq zG>TKbA)Z2;9D8g9I~laO-d?-DD@ZhsY^1?$O^zE$t}M+)9?vJmFC&^{f^d;!@z3uj zS=c`dRU%|h?H6IUY=^+&Uc8exqY(*bMmBc7BsKabs{I00)XFyEqIz7Lh{3z_vIZnu zGJdeGvo&uKdaEK%I9VxSzAnFS^&8QDK$!d?2F;<3|Cj&91iBwMg)^A>!>`LYEf$zx zlWWb(D}I~nh*{wBJ~*FqR0=t4}2U1TDTkS)Fo3*p3zu@>9MgErI3!Yzd}c^MdJ zpFTg-yd);_bmd<59#0r3Ou+$He{vSCCd6$Z3fc({el9&wjyaMGyC0**`~9~8{d!mg z#{@{)`HsNe9nZB8^~5W2=R?|GbnE0yOv|Ro^0qCJahZb_jiKC2@&V+qhr)Q0xufk~ zJ{>gw>#@dFc1KiQ;)>Ne++;1`i(n5sf*n09HKM@GY4=&}4{ru&eP%&#@5W1=KZQir z%eqMZ490d-H}{ZEr&P#Q*Mq^87i3~1VGuBZr0o(1BUw!h_{j^X$|*usPCu>w6H=f{ zK#4PYlNF<Q}#} z{&81TMWz@jfQB?|7Bz@&QO0@34X8nBmbqvUDvtjUeponPm=TrH=_7EW9W>p;OC^Hl zE&Iy$Xv`poWk|`1kUbJZ{=Tau$zj}!)boXJpSZ*E373y0+-^mu-`4LQ?Sm^d(EBr! z^OusUt-Ts)N;=%H^AR;CuF=nSz?`|| z;!0R>q!gfk0cb-kqeo1`05{Sip&hRj4iYkyXaIfuGmQfjbfi3d+AF zQZ59_Y!H7Ijj56_YaBRi%Lgiur6OOC>yIWJP`kJ1-8iH`sT#=(3RxI$rGQy`QsP;$ zehuT9UC{8Ug->uK1SPzj*UfXy81&R39VN_gVHLS^$ORO;jRs~ShVmSY75>e{s)+U` zaf@$M9Vwx;vLYq!00MTFDe(Oy&f5G;fU_%#X5ZTuCXz`ewFdrJzoK5daH|EYsx@zk z?5Do!LSacroJ^KU{_o1Ycxdfw({^>%kw1vBH{BGQVZwdjj==Bkc5>hK9rz;%JlD~) zIuXqKUC}^4nLFb%)i&(sM&^-(td{b=M7mC*_tbs%^!znT41nN^sGM7w8))^b8;I{J zSJ>`yFN-4YebT)cdxOSRVPjw zh5ato)sqS^mZ|51kPH^{h55{tGfy+!ekCk*26o*@Ou_8q7h*NpN+~G!nnpeyU?lM- z99@2WGXH2blDc}wNR#rk^Phy8qo;i1f*D#G;0~)wV9LjM^R|8S_+^!tIDosYXj?Nf5?FQv z3bLKStA?LK{C{4~Wx}dL*NiWf>h;IqfH5{1?LQpl!CB|R$O$=r8+K!C)1MPlnd!O~ z|MxCG-YXz`INuw5%L0^0!G{zA6RgP}0Ksue+?sv}EKQoSpZJzWebV!~+Ny`I$D6#9N%X`zSb6)%c^pXK6i@vPi5Ak(eABPG+Qx54(Mx@*4;t_qEijM;@Np^LI z-ME(Gpeq>ol?t7WzSbXdXCW#&+c(%yHBGkD5xt-xU7eAC=HA0VjPO$;7sX|plh`IV50lEdR9)|8+Z`4a1e(IiX|u6G3C@Y(vyn#*4BNCG^G#9W z$>bpE;p=_%7RX3{+>n=3iIMt3@|l40&}+Zc(%qP53o58B0pfv6eGvjY+RDD#Zt)(l zklVqI_qP0cUA3Nsm-H^Hn#A$%6T!(vr0k5r!bi0bYp;-kodL6)=}fo z(^!P&)<|6G3-d<5#2}zODL=;9PFXO^eds?AJ>lfBPHJ6_=;DRb&iSR8+WXi3cd&t$ zU2ll3rNl2cVcBeMjMg$hhE~FHvF z1qhGM(?4LWI`zJN;vE}IxY27R4>_-_1G~ZG=giCc|I#N?BBt%88P;6lg`=JRO!ZVB zcXW*1QBje^)p1+lMYR7J)}*KjHV0`Rv*HCEW-5;EP=|K8HdhO6tU50j+M`c--5BEYvN&kIo*Pp2#KjF+E zzss?~xvt7N#UCVm-5NKV>E9gkA^EUq0>EoO-jmb4aclL?;Z@OScD*ce8afJEM;d_#@i74nJ2H+JJjok ziob;A>u6GRc=cOX>V$l$zNAkQi0s1Q@E!X*^Jm3X1s#)55h7epa$L6j~GTp}A zsX}0-5Tvw+(=3})@OnM?urFQ`O4`kagniYP%{4|;vB7$i!f!6L4n5*xY@Hae?n3tAoz z^}hMfg_+nBB&q8;2NFG`|2EV7K36B-#!QmQgyBM-ivWoYSqUhTN4lecVj^w+xrso} z`me-K3_@f?Q6O}v*-j|Lhwx)BN8c|<2ZBN!JP$Z#vkmr-6&5~SHk2IYKQrL3`XrHZHt@tZl04+>SD z#f`C5SURny2uyaLA@f|`joRNj5#TSEq+Jl05bR$6LOda3d9r>~hk9}A6hFL8u0Jlp z5508O1CF&wC2_J1tk{E6uq?OTigk!0ZkiK@)dpx0Q&=o?^zVqc6nVoTDbNJ7bBL0S zOXvqTMY@bXN8OxZo3|N`e@9nr)4Zi#z$>#XJ8j4Kf-L{);?c-8cz5IT2a*ZEHeaT{ zI2lj0!@T@ujm#H;x7CQS;@sjtvsATj@_)rmFPLyp!^i)ItaJr z$`908=`~|O`pprtQ&8op0c7|l>!8_B$Xbk3|DcsdZ$DahnubIh`cQMR<@pxw@=e^H z%(ZXOtuOU4cRB`}V%b;mL=yF+j&QRQaqxjdU*O(4a^f)6joT-eG-ed~c6C|oR3$9i zBhBx~tuzZzIf)}rbU=LZYB zRG3!njE_*T=9R~oGs*gOjFZ%sg3-zrQLx;V-N01Jr)~|Y0it0ZxVQPc_|i4+dV9yX zJ1_i=Oq|T3>~Y)d@p7cxK7kv0mQk6a-~*(j+v>^@rWMkqa%x|BHFq)S5d=A?NucBh zlyesuufn&Jt%`Znf4;uH=5l7;8Wyvf2ykeDe7;Z`4B`bJ>2XlfRrX9nw4pP)vjtw- z0$#tZ|9!W&pX+rWA69QPlip}T*YN@U2G3mQ&r@Pa^(#%g<>(s*hq7#sYqEq9Ef*Ty zJXT17RnDw!G#-FhlR?qpz{r^*7+~NuC-BN17nnM1+r>5wME{)c1!v-W{JcB_jjw?q zXUVy5#j>$epU{~fV0aN0z1p(Oc{{r4@l2o?yNV-auvj9t=sm_Gu4ij7dUNpnxKRpv;qB$^_5KymbhdWXQn@kd+ZjM7 zrKNanN61sq>l2_RP1n2q@cIXOq1e3AV6p@I5<^Q2QAzo*G1y5>JHNm zn&3ez$Rb{$ioSk{`Sg*6d0`qY9~@-*}C?W@2!I z9i8CLY-dBj_VJ3^mgIfEPx^E1k{Txx3eD+$>d$nUcErMWZ;)4S}ONO;^is zJ4^!`wp2nZG3;5z5!iVDD40i90&aUWvUs2}1!}b)MX#V%iBFssIeJ^OiZEX}4YPHZ zA3feolnlE1mrDI3zssi3JTy!WidsNJ4SU3yc8>54B`4xQnq>9nD8Zy2=YWHm)9cGj z!#J)RtS=i^)Hvm+L%eSOUaOf*4{3&Mp)`(p)VGrgRG*O>9TmONn^aeFz_~tU;ah-# z95lW#|8eu!r&g4u_4VcR&IaLy=~(I?(I3<~OJbFR!xUn-Uc3g`9&xrAHqvS$Qf2yA zLr|s23$sg}eKSi@(`N-;6yU#pRXO9z9AXCNc0N+^qJW_UCw7zx%6nydMHilP8&*x{ zzV?c55!MaC+F`R1nk(CX1!{1crz=L{Gx2^)?AsxH(p0MGU2US+;jeGLHn}$aZ009F zG&&`UZ)J|n8tPW#E9S&|f<qTOKAfKo=&FF^J} zxCQkyKD7g}xfU(tT%`GQQajxkCH<7Ys+;2`BGm6ILF2l?_KSt~>`_y^kl!$$JqcS--^1RC)HiTh2R?zT-nOgKxOo25jwV{stRi>Qgt0cd( zC#zm<*Vd61P>1DrHHNmzzb^2lT+I6vhf5^bwRs9&MTyd`nk0VADV2pmJ9qwz#5N0I z(M5}W(9l_RrgyyjS}`3v@s-?i7e?(6nz4Y>QK zY13Zwr?-7_=~wqpA0%*$AQ7YE#dr^8?X1PmMoDE-zG2@@kVTcY!2@}EtZ9*YwgR+@YWN=$xl4zBe;Je$e{8OKFBB#4a8~j@s+Re-76p06KCDqqklmC( zID($2OB7*_A5)6#IsSgM;I`ItF#Ai;yw#M_jnx?e?ovT(0CH}wyMfyKzQt#1O+ty* zQ)}?ZnT?@r>4{ynUu{n3G;+?=>d&H!f7FWndqp#<&LCqpWpkM_PV5+d_*; zTO+|xji5%7z_encv|K=U!Aty`6_~W!#iSu?2l7D91#Ry)!d6$ z%&|wYYb-bwXbLOzihWmN>tx_@W?A?d??Ih?EWjW}_Xx*}>pne*Qxk2=VgNG!Epi-p zzUNdZZ-4&CJ^`mYJdQ|_zjaP6joEg260l{r1t@*G=6gqH9{qTA9<*of;7N{Nck&d# zjuKhzj@R-nn0sPmIOdS@ms>7i9Fu z{LM_t@_A8K?p*jWZ7>}WJ#m> zzu;MgDyb!qBnv^Jx(>2kuhVucIkuqZ#I&uq^Oo(d?U zi<}(RXcx8Uub^0~ig0(vl94aoZ+-y|N$ep3VrwbVytSB>Z^`_ z(ALP~_)`g_Us(p@M?-*$#Jc@VWKt!3lG-g`4;_eJcw6GEc|&2jD1W!UZr$eLcA_nh zrk^JZNnXyqtb(zjN6&s{RQ`KKR|M7m$pN>Yy3~VS%J;F{PVyE54!HebX0^RBy`qDm z9G`Q~vnLZte)D*rg$jJv1bYs70x(9k~I2}-E z!@RV1;ci}Bovt)%Gs4{FLVLmC4wL!UtZvFRq%ZQ?K zq&49jVfB_KOkYM8cd`OET4#_$N0#A}ek}&`6hVS)lbSaQ*Eo^z9+rHMtAh@1uXeG! z^@6rrHHQYJpa4%W!{>k~?jBnqN7>1Wf|~dk8%Hn0Bn^R`-|wA%7@bBI?|1oHoB5Hq zL~^3Cn+HyQh9z4S!)$%C(bOgcu#qWACu<9K&;9%tUBW*)J=N*q`(`@)TS;LT;+}Z4 z-)5XlwBp$c+!u&<8>eQU{#(4*c0ZaSdy>QHAgG<){R+T7>?v@x{E(M_-;~16Kps{2 zF-^|IhHa60#N{i`jeaGGqL{#oa1>AmbMgPM$+zDSt|I|tx(Cv*tnK^9rAqJ1XCl0u zTzJqm-}O19b?#nI%sVvfxqHT^d7x8ZqKzoBbZTqXytuHEKYptnBcFh}3Iji}iRZP@ zPI)E@7{~xuo_Sv@(ZPt3NVS@84fGe>QUqqLS-dx}4pBCNm$m3=C%mtj724mSn08e8O#q1AWBjv@?6>>;&ye;(26olO58j$aUZpN z56gE$s!gFixDie;SXGI%))lq@K|`W+7Rs-rMs!PHpK9nf&CLV&z8#@H8``*andfEA z$!>j3AFxCTNi06J*Ss?Ak@KO|&RFks?>-Maf8N;-dL^=?X?oqhGENHQdK5!!O0fk)S*Kq+=b8@3fG70b1`}KRZ z`_fm7-mibTcIC-;m51L1V_H9;u?IQk<6u>uu&@GTo&(W)uTqAUNzh#C6kB0Ztf-dE75%W^{oHZ9Uc^JU4axhB7fWz3cEaAzSf>SU5bjq))l!s+@c0&i z8{* zspin$b3kb?%lASZ7c@ANfH^)p`mj-9Yl!%l#yH#wB&yR~23})K26wqV{R>a**MZf?%0*_J z%rgxnbQ`>5>SnQWIYJ}_+4U)Ykyl;+9+Db|oWv}TQ8Gm7Y}&s4N-v(@h8`6i1HS}g zLtkkZnF)I3Z!}?#^J2?w&inQzcVq}ka$V7#h>r|T8+eMEf$HstTx`QD6>JSMe)k53 zBm!8kw{0;zlc7O}v1NzA;9Qn+o78o^Hrv}q>5^0E$Afl@ujT483s~agVofp1}b78MRzlp!a zdMVWpI`9zE4G#Nv?0J2s{&=86bppFFO2jo1ZRZ-vcKjKA zLTAj0NxX2pc#sB%l>xd)$Jhj%(Yp>2C3R6rNXqFfepgwNuuXXS8dmTvqs$*<; zmRRR%xNLT;SSW5uXFiH)AHAZJ)}&_c5y2s-u3N)ty{!FIhWu2dEt^hK%hF#H(kHDs zl%?nU_)?v)Ao1k)=J&yT+?gx0sA-R*&Rr_2L|3Qsw_U#5_xyF&r;Lk(0X4cLw3aDt@WZySrX68uklsuwUZubfV-K&P@7=_dSM5%`M zMzfjDvu}Jd@r&&^nP%HbUdT`6uh*@UZ*k5@DDHjh!BB$@Wc&On^_^JpkM4sK@PJR@y~q+~8E@V`8Mv{x-11yB!{9j0w0GbsWy(p`_g z*>QilUdyKk3baEntqkUf|ExE(dOqLTZgTD3vmm0WN=j`wkkM~Gvv`6P*w>=%hLW=V zD{mVR?BNFak}@g^5}b{S01qk)M0U0}0}pl&D3MgP)c6V)3WH6}6G(z+Jpiw;9*s>y zfjDx8oz)B3;+;5vj%55X$?dFRLvQs>L@g&Ev-s*qnBH$_8G2|RM{r7Z> zJjfga8eclT)3et^R2QuLh3}7#2;c2IF|!s1sl@H|ebKEJ?THlpNX(RMOr4~5)F&H~ zi=4nF8vrWe%iOryQ?uuhX#q5sujbv?L&DHEzap7Cs~D?`*7?=|wGfAw$B=PETaH@OL0z#`)~VhVPfz4lP~HFsQc zKvN(W@sPvzz6M)3=uSSg{vZC|YIxlVt64OUUV|ijGW$)&j}DG%@xYtvj;niiS}R&`_)v_Dkl$SR=GHN4XUj zSvnW>{!#7fAw&4Dv99KxDknqdxRb$-eUOT{C%qf{keIii=L0y5P(UhGibV(2m5gJh=Q?$$fpHuBqnimzUN*Ej42uSC^tttBySz)<+wv~ zh<<5p6SfuL-B3;=CQc3h;{G0H;WLLXmcdShl5v65g~}AvGzrddv`J5N#RxG&yro9d z@Y<~l$a=UocUY1|VYSgkoeZ&DciezVY78Y0p$SZLnP@cjxF1mU=*<%Qp}O(r|6w1c z7o&TrmL)?u2&A1M1f{7Tx&IMg=xOvLWE)w7=i=a{m1*YKn{lm)=Ml@_IJue?*xH9! zjwSSOM2uCUOXsC|m|IJoLP7*Kgg~nlzh3rKlFCujrluQ{G?%bk% zlHwwT*6ZB`Q|rv_o&=$3FpewkIWAG_L7cV9Lmj^rH|b?(={>FAzSE3y$1JXLS@j12 zfuXTXls&QIADp-xRmqN%7Qcm!Q}-!(A_R_F;}Z+Y@v9ag^EW6B+tvLmI9~wnPu_rf zvscAd-@BiXtLpB19E|SW-nH9D6UV2J*d}lok1RM>#7T*O@M85~e~Q#DfXj&Wpuzyj zZ%I$B4W)x^2`8bl0~ap6S*9bm+3*kmHbqJZLq9BFrm_$z%nXb+rqC3aa+yiBGlJH- zSnm%c+|JMHX4T%~K?}NP>&9AFJUJemLX{bX;WcY`%td7 zG9GdYmN1K=nctF*yrZSkzx6pMM(s;%BlZMh%qT0=7l$615$1iky`Ex;T50QKk|A9A zl(Ao9X1>(MRk?2lV!d1d?sL^@>6qK^O#i|V_C!6_(v5vnJKYo2@$H~JVPp=%DJLnn zOYxeTcEFp-ik7ArDDPji-ph~eMrL}9LCyl@*s>-{bjit4Dwi)e4Ev;i|A7iso{<$( zcltuaOaE#lE;`D$rM{>0RlB(*h?2I^Ax*=g>kA`E(-Zk~yHWfqP=y!W=KkSuR{Zo(Nirmpcm*ic zK1_>#W77M_R#~z82Y4!b?Ndd2eO>hrQtrCT1+xQ3I1dUgO`#;bp_U5KETo$r!hJlQ z0WHv39jbJx+j9s2>^65D8|`pGXEjIHssvCV^&|*;<>dv(%+RGr+d?jWqIJaS+SOC_ zNm)bgJY}ZT?Q(wgVRJtK9$R#{AQ5>fL{fyE1dGK0F6yuc>S$LyDN@RpYtF{}ejo$G zD!l5?Wr3=j8w>T1bQaYVcnQUuY#7-D#?>KOW=PF;{66p#$|cI%g#J?#K_3ZKp0^)h z_p9DulYkW&x+Bd)->({wb8$ipH>|FQpAqmGs|csoEnWs zaRW+o*%%YQaiCG+=^onPV6x1?jOM#D%)Tj)5nQeik~y9tJa%^Apwg9?$G2Hg>-es$ zVtBnVmI0XTTj`do#TREX&wU%F}9UNQK{M~91I(lFsR5v-)4GbOz_23^-J7N8X-m$Vd)x{`Z!l7T^=ys559pMKv`zbE-jnkd1)P zFdEvMEog=8W8Z`C5uy~9Ki^g3UW}Ui8R~w1_P_Y0i;jX54zz=m5Lk}Jw6b@AicdzW zj-eB9yV$OYgu;hxqg4n963?3@OSFXStmz8ZEDt-C(jMD{iES78Yh=xsIPMBXWz z0zeR~nTpNi<&*vR-(6|)1ACgbFmbueS#czW9V+>&A7PzyiWpin#@{a(Q>fFQx{Tt? z)lL3%NYY3){$$E|Zo?MsuWzTIn4_a?QAog$^=f_>xZe_fBWz8`Y)vtv-5Zn!dLBAE zac^Qz%4{WB#Pk=3GgY0GORI=lvrg2?A_MG5b5WgZX=vd-$$E8)2DYfw(?LKX{3NrH z1g4XURLGazF}xK%Um6b5Y-mJ2iW~ECj!+~|81VUfM{S&^cVBsSHe6O_bB*l!FHD1+ z50`qW9uB0~vq#uf@qitx^Ya&LYN^Q3q;ZB!eWsNHIV);`?njbdA-b1*L6vdOXh3^1 zZ>_!hH$^M%L~#K5bHcvpI6Cs(D5aa}{9jjRE|ur1o7~gC5DW2*8Ph`qNPOM244bJ; z@9mzU+tWbhu^Y7H*`3d%EUJ?LifoP&uvc2xBXvMNC76TBMQa^xM9%O*Y~?CuqsKQj z4SVk=ruzK}=kO=sab-93Y=IM7dKUhSF(=yRCJst%*vj>RN_dK$5gwc zlbNjeS5-JH%>rt80eo8f6=1~^FE}j6@)FPP%jqP3$85k7>WWGh(d3UBI!E~nlVnr& zeF0Ol89eGIXP~S5`mHW%KqHp5+P-(ky^0<^08(7~#Nl|5r4%;q^%;ud1F%`+ELql| zTO6lR3iicLcHlltNNi<${dY+AKii&Z0c5u3E@wp9eLJNxJ(CV|QB!e>e%@~Xvh(Y9 zoAAZG{dqAXr|XRUORy70Jq`E04$|TdbF!<M+^_IiUj4+}PM0YdKeI)m)x zb39p$=9|h;(MO&STo%A#Ylc*0< zLa@sg>@`@pKOK)lrwMDK-1mm# zx|w)CqSqWOz%Nk6rnuNo(vRe=Kh@8trsK{nxrlUMqn(7JdO{?eO92wA%q|WFTOOjf~Zp8 zexDIPvkC|3bL&2b;3m^Ka(9-%`)%Ou}X znO(R1mU0_dMVTN07ZC4>Abq10fz+n`dgX%NY2LH%h?zLl+{Xi8s9e{hcTl$YnkonU zC)yZ3t4G{!>^d=NgNaQ{n~;E(=B=~#uKoDY{D)U57Vq|X>I>ijUrKODEt{ncMMBC~ zqt<;5hU^a=dq*X2%*B^9ZX{=TCdPhQ2`Uz+1P@@gX(ul;r@N57Py<4V*glR#5}J+r zGA72zn<(C^qp$vtnS87cR>Nz)1dDi?p`)Va+SfSw)tQg-%Jz~!XOInx)l~r!YEM&kYnhyG;xHxZu&^%Gj{MS?#e!% zg34)irxDZP0vd&=Th4|a<_zu>=kuBU=uCi~??Pj?guMG?hVQ z-^AGnoJY^rqe~a34BsL3T%y&!dmh>FWOzyA&-!9~Yb({E$sS63vTW48uS-fe<}cu{ zQGR=RO`^9f!}rgx6q#8R&5WI^U%!Kv7PIX@%&mC^TO}*6s4f9)Np4h2Ya9n=%C<~-3V2+TnY03Fv#_q9iUIyf z4{IJqb$$e8`oz;-PO_0THc-3byf`myW8Jr0y-*^$=vMPIv?nDqb#@`FU(&CI*+UuF z2CGA7Bh>Yi#60IL-74671H?S6!zbdBlMLe~`#||P>@V|RQ1Pfjs3|?TJ^@hhrI&Vm z!r4;J>BJ=Q-hH$an>ZIk2z+CT=FeaEs$VPJz$K$sQb8BpiAK%NX%!B06MXqk8TRd` zr!wHbtys!J+D|iv_)Vw*i-IF?EEE&&BQ2QqL;E5Vyn2m)r|>c_6pSYcLdq$b*U^y& z3iDZ8upcC9TH+sNxz~ln7XeqqNEG2a!5U@Ag%Q_wfLB^uNbg@jWQQ40hG`e!Jlq7rddpFVaGjqetG4@J zn>vyHh%Grx85vrRL+zD|V2pa{u!-|9e)&GJF^R$T z|M+x%ngEWAE(u=8lE6L;3wSg)#M>VJoQjNk`$a4;-(pK)cHZCU^iqE!Y!khASTDcm zTK_OVf#*~AW_!uy-np)kik0qW)u_4JP%#ExqHji+E1bX5vA60_j5sxtD)+=R$GsNf zr`-c>!JdnlTDeNXfK}*mH1s2z%d*eLrJ+S|WXW+5+p#u*qnem55sz8__p>al>%hA|nX~ zjqlm^OhgtP`ts2sra45-NW9R0;Opn}4AhQ1WJoUf@)W+ikgCAqul_1gW`blK41$!- zmfy=E)l0HB=BuP<@MHetephAwz0s_mJ3yoTjya@0M=lh5XFa`tQ=DIM@hLGAcH1ZD zZJDp|xa=LMI=G!%^`E=IaQw>{LpBFi7(R37IQU^|b!e2Bm2i5uu9&<&?_h3MJZjao;>;*qvv_56fW1-q z%-dp{VUZfzfhcO!ZQ_ai5W7m1=37?(qlvy&M?+_w7y_tEleoS0$^@%8hl)JKy}$)} zlzROU!JRInB3il!8`B3-afRvc86;FwI5(3&M-$-_YSCJFFLLE-o;Yz+zQ!z|h2FT8 z#IEHAfe)*AeZT00@9`Pj;WVh*0mq($B#t(y$kqsmNnNS?CIbd_+ZCT3vy?j&yN4ze zV3;0Zx8o&yq_QmWYY6lN_9}TB6wJl^z%<*kL`AEXZk9+i+@PbDw|Yg78x!D|-r;BG zP8cIOze z#?n;Qc0!hO^v0($sjP~o`yN+^MnX6)nvFa=-)2!d;v!O;=KI|csj^yD+|T_6v5Iy6 zP5{w9q7^4~zgQgEh~g)gcI0a>)yq%=+fP+|lVNY?KkCNACeRd~8!CRPM@w-G4!$89 zLB+9>gJQs(^?W-@gSB2ZfSZ42M1c=SdwzOigF*Sp>vug+>l{qcv?N7e_G z{b0Kq4Fm8uWJc}#Ar~{;aO~Fa7!DRIp9H* z(jA-F8JzH1MKnYd5*Ooc$KHL*=lcf!{r9UaHTqL|D}12q_pQ7T5x~hYbp5vg$6iI9 zvjQ46Y3~`sgih1WAA5XAJc8WjnBkTdM3b$bZ__xc?qejmy&o7fK8JB8Cv?{}yP}QY z7253UwC)POI-Jp%>1Pv7_+HU-X+br&x$ClE6mtX*JOj3{)%8RjQogRx$5l2|kRG3$ukz8d7syVs2aJKqBD{}P)0*})#mUbKqh#N|!nOneE z*%Ko{T|GNvO8{aTue9u4Nbr-UScO8gA{pWdmH!Rq+q{qbA1{59S@)G%TA9!gBjyU` zm4Zz|G6@Dzg}ZSYBZqe@5gzSq{VJCTy2$#^S;-Z|R;^Ek zP>whn)T9r<_ZZ`ps^&V+u}kEZ$Vb7+)?||Yht;Jy4XSwK0N*HluZZGG{p>?G43UWp zqj@tp5^H$LU&?+ONRK(^8~Vt)bwocJqX{jtKV~ZiwmW(MpFC?-JEG-+gV&hTiyrz6 zuP6w;txkL3e03lI=o-v-f94CoSjZg!gaEt!{@iuovsFbzH5mi3>e}Erq2l%ijE+dC zlAEtC=#!UoZ_+WS?!s6uyi~nx2YZ>UW2CiTfC8Hwp=Q-T@Rz|8wpSCd&nkkNZ zc@G5lg4wzHE%QE86D=VA)^N`%-J0ZYOzfW3_)zpn{(+8;%OX+B+|Ar>)9PogpZy;4Xgn!AxR!h$LMZm_D00^_bx_pY zBAKB-EC0fV?z{mR*+b2nr8zlp-D*cYSyiv${I zu2uU$QCtNyZg|aD>rc`{Kxd1|5;a=eUcP>+Wl#-%M!>gKz#Es+9tXArIwMn6l0ML@LOul#KW@~(EJ}v z6v3p9ACZX9lZZ2eu_r2E*SkJMG1<(M9Q3>n?S*IW-mj9`@CNR?S%LGXNTBU0vDTSx zS#_S(Tv=$|c>}1o%JQyBUFY6>)cPW3*5K4OPj}>%2aj2|u1m4WQd-+P+{3~8Vk=Um zj2jwBlkONT^%7DGJ*cfs?3Twk0^Q?6ciZLrYwVO<9wYIASVA>*`pV@yb~$KyM|>atFe zmWbKP&R{iVvnegBXCAi_{AWx!x{2sVn^jyrLUC#!I%TuqG~l_sq0B|>exb9Kn^P$> zh?15B6QTb>DYF82&VNAK4Ba5%g zJ@oPZah6#g8T=vjo$hqMj?Q_hbWfJc^X9%1g%%{lP9x4mP)8ocvEJQJt=L zmZQ`$*P2ehxRb&dgvd$8@8B`TlxHJh5aOSGP^)sdSXts+=SbEm-ya6FlL@)`7+n6` zo`wFX=+fKzHK$09X_9UG8HvmCm8eoOG4@XucN_IaZ3HkrSK+9f%sPlkGG3ywO$8Ta zyQANnR??U#kjBgY%o7@QIfdu^Rsob+LGfr73PqzVW7<;KtQ$_Mdj~n-W$Aq{ifD4$ zy8Lk>{zjT)hH~YUQyH@Af|?mN<_>y4pQ50+O@1JJk=GK}lcd}^=p_IbFa64z3fW;?XkR!Fo7WwV z#a3zaa4o)5g1irA0CgdgmE@wH#dckt*!}qpQvg)kl*zBqfeH@Lc*DGD;OB$6_o_7B z1%Lfm=l!Qo_DG6GqU|GiGFHNTsgH8D?%Yx8D`AL4P1ijncK?a*9{enw4~nqCzm7Lp zieUC!HPk~LVwJOOT|%^L@DBWqD0TyPu>4sL>9O9lI&cTo_qM+}6=e1t69BoUO2&a{ zEd}7TO20a}zr9G92Ouq7-nR6sfztHF_u&N{D_H)$21}GKw_aXaKcre*3Y^nUMK6G| zJWKkqHhlTzbpju!zwikxu&QGU`<~pE>+pS)LE9Vpc#mgNBSMfqmV6D1Gi-kKn=F;v#PQ=33eF@su#fmP zO)|@;S$ZL zu!X$C(q1csLpe%Zu;UlmEI6HDlwvuXtU8q_i`dsBoX|Q89IyPKkt1YUi*)}lVeY4n z%__SVx^Lt|M=bxp_@UqtU;4?QSBW<_*(*M@x3wm4roF2Ws>-fe2MkycSNl?`jf9xE z;b6G{9uoKW$lcKR(cCK2wkPj^?0}Q>E+!A`fI2VU!G5~jm4|71@?W*ED)i7EO*mRhL%T6;rMMXPRT9a(ae#K-JM z%+7H`3_!CfIZVPD3umm!_Ex0ty>MyK{aNLfKFAR2Z!pyYDI!8#on%9mmjq>gYY-a3<_C`t_fKOTweVo8 zvC~&@oJMV>i`o(I!baO#XqNGPVB^cVoTJ6pWzJsvi)1i!rKa*GEz?(e^d2XpsC1gg zKJ)Noy6<`bq?YhxOxXm9lx4$qf-wx_nO!WE{Y$c6&IMp<(4)Mw%#%4OHsqUrHg4vK z(m>ih{_%RvfpyF@(;O__wpfBGh+J( zQ-sI~_ld<>@kT4j-rIK_4|pAQ5i2s8u@byIsyMhujaccE#>l(wbGpqCEHL<;uTaeCWKl9E*rc8nz755xpf-i& z_`zEYFpXDAFqY%|e%E?A4oh3C_OzeWH||@yXcorA;vg|kHbX$Bq zB1R;*h`>7LM_}8B`B-+^@62Xm<+%g>gR%R;_p_1y7~Mqs_{mCfZ5o8C-Z!I}?{kHi zHu(KiQja3e+ft#SmyFWq@z*8xzR2@Zxv!Bp@XPoLqgO-y#d_PFhE5w$Mm4f7xAlGK z*~Z$>MN?7A?${nTV{;DNhBDsH%H2y%^J(Qj&Q=dEEuH50n`@|Y-?&|QdG#N9fa7kt zQv3{n zSbt^sV0)Kg^X(ttdYNiz@5=7upYYvYzI8@OeEoyxWK>mPze12J7ktdP`1lVEevgJ! zP9{WFLXsk=MHq-wRIQ(Q|dhqttCK;Kf}I3*}%QTw8s;bR_Ru-<7s{mrEvIk%*GN!hw4gvFv5%xwHk{8cx(Q z+Ezlb%!_`WFHP_d$!|LXW!+z)%_I6}Ef}-G-pPHkz+>wcE9g8wxgLJ4!VY#}SwIQ5 zNr^$HCN=|Gk&41<~kMxP7=cMY~E!dNr8r15c&z>0*!oL!b2ev9Y>aepz9-aq7R>%)*eayw{ z@KuI|ckc(f;{0fZTQQ+|Y|J737LM?M18IZa1XeQ2uURY&K`{lkYzDGTN_Pb!90T?i zE1s?9$);khe^z{RfCn;$G+(6}x|j4&O^m)o*B~EQmF@@fH?dcqPnUYqQV@Z=UkE?; z_7|gv61DuWfZlJrh}RFlF41A*(az_&vVMt-+_^}RRW}9a0H4-FnmdRU*>9~hKY+EQ|Rk@M+C`Sz}? z)gcA-t^fFPObwlQg)lcx6SY@$8xJ?<_ByFbT?Pj|jnvvJ>RGnoHHnCW{@y(?!(7Q< zPXklY<_wF9?A?GFN9}t(!|zljRj+1JSLY-D*_$jZ4Cd&~hv)o>igT@_n4|(fALc(r zyOkY}r0)AI0s8_dIdQ%RuwrSn`VIYwJ=>`e@<*!k@6*nroBW?8ty_F?9=xGl=SgBF z$G$z_=O}1uPIFSd(sW?YVX=gpanc0FPlL&w9Ta;xl4HL#_=tYRBs~qeoA4g=Z|}rQ zm0VU5Ig%St$}V6ya|;^_(PM6?w}UUaw|;zf+p2ts1%$TMVB(-;n z3ue2yBkD@+EHbxj4=Va**5j2Vf9182VuNgW@qW9)QY*-Bl8g;$xe0>v$(dkYFFhF~ z>cx3ipzGc4W`Mcy-{0yq5(d2kulJ$B4zZOx#oY@NccJdAGWZ(29AfY7XdCAa72dZY zcbr@xgLU%-7ax4%z8!Dpy`Vo)offUbyUX3bmi!8`Hd~we`lwkxSzk_&! z>_2k3k{~4PQd%WFMMijR(77*KSZCXQuS)Y-06={n#P>4KiXkO7g?zV3C!wZcNbc~` zaZEW$DD%JLG9&O9zWNXLI@=bhp+(NnB$EgJ4X_(lEyBx_T~ZlJa~yuQ2U7eMdo@RX zp^+OF3?1`l6&SG@QrH}d9Q$2+>8GCz<@tow z4{-8caw}Qtp*G_$-EL;(uN*At;6b|Sq5C9%_FzAQ?rtf2ktfsJ%mbT?O)XwEAF0q) zV>fdvLkcO?nu=Yqr`2UjUV>ceu*t}4H40C(M;-OId^vLAyWANp9$VMX_tm^bX2j_@ zJ&dp?*=+0(#|&QeO6s)hhtcZn3OYy+0B8?%*a45fIUItwAoZyC=VUDq!Kr6q0-Ba= z974{PHwM$CkpKD>+n)7W1u;B%PvluZh+NmU^cdQCu?!=#F^!QVZWh9!ZW8U-JJzSZ zJeR}7w+Llj{99~jvlP4cQJbE~Y>ec!027zQ#{_dX4xi1&%)rLDy7y+G^9a=!fXq2x zJl6l5BS>R1eO7GsxwM@6CPLxc)S39o>{~J8!?0KGd?SPUaA4JGe0S!~m+_B0jh}1H zAf$iGO1Cg+EtWT-Gshduj|+*9blbKhQgUKOtTbFY<~$&MjAh>$IW5>_mE$nn7YMWM z=(t^fTpG~}Rxg*aaysXI9H?{vBnsNn3P|8(_k6~~fAv_`H z3>#WBm831h*K7Zh);EBV@eiVKrvO(bmXFV@e>Lfcb1Fc1F|-cA22Aa>9c zWHmVI+dE@b_V@_g$A#qz-Zb9cudPc}V+wZdgPR6A5&Kn1o_(EkzuOWMsX(77WMI7i zJT~w+egy;lc9~Qb&KLuRO^O3Q_6$sGFxRFwYo^@jdYev!W5~Z4|0&_MOxdrGyrI!zyCVX6u+kjiy0Pqhok8@7z{j)$e- zN3Mri#3J&Mj~G>y7_E`^3T<-dqccq~s%OXZ>k(;-O&^9pzRG8B`_!nKg@ycVFUbX8v?)h_b(A*;f4 zm_|4FB~t+O0)lI@!#?9%iVyXnu>G+aN|v*);GEy>x5G{-Q+jcuWClsu5O0Yuefy4! zf2mVRe$dQiI=@zz$T?zGnWpCZgSbX~7lbI_dREQOn3>wn?veJgAHi`O1An@qVqGTd zmv_x??|{^U1fZ7Zx0flSS5oj{z2fg8EeVtPTmj&oZ7>&!?4GC!0Wt7L?PD*1pnc$U zsmm8Y`^=;Bxg8q(MhnX&zgjJ`iu6C!`i|Y?U2TMiV-QZWI}4)VGw(>IiNnVrWYJt# zZlu6=XPKl|pf4A{@?OvsHIY34_WmiYsBtOceb_L*HOlrmOKNE(sn=W*rBoOnY>Ley z0pd58)d1>F_MU%FSb1W-+~;5cFKa}1DMS^EXb{F3R{N{zC3JnDB9r}+927m<#FsE^ zj~y9MkM{M`#h}y*zqcgH1rB0G$qsHTW9}9$JGPMx^`N^Ni3@~II0};{f+uv?&~~Us zBkEe)ZtYjqDQ`=dHm05ipD=8_8i%ziu;D8n#eEPtA|=;tt@!!`ScfVSN$_#|klGE$ zc{Qg<@_*;s{cu$mDuiC=aY1>}(gpsF!CH4CO+-*pSeJI-{8Ft~Fb0mt$Q5+zLNj(i z;arR+m$$D2VPa7}=)0M+^S(f)k~lWn-43|E6PcSFUx3bb>;h}a(Tm4^oD%c^=@Bi> zK5*AVUL%q!2y{l6A&U_QAN#wuF|NjN=;-RY>t{w5!&KlhHmq3fu+~nNIzqx4-`$Wr zNQL%22VRw=7a+$~Oemj5!i7o|;M3*R%20jGv`SKswx$N%;(_w@+Ia}6qLgb5U-o9n zdv7<$Qfw7JFuH0qVxcjDCjSHjMhNy>xyuNf4d2`*>~M)!Zq>x|qeuSUNWcHejHZ#5umB4*tWK96C*%^2-!MqR%uXMb9 z1JnNAFyEo+ZponGv-U{Z<0O*!TcGO~9jlJeK+!9?%S2}L;x{_^bHO1rK-Gu+0ep*w zRhRpKztqWGzUC~tW~5LThU~FH1v*Pj`c8NrAvrd7ZNE_FVYV&LC`g4#T^a5g|14jl zqT*^-fH%!+-KOLi?IlC>M)`-XF19Q@Qn>|<|J>(&Ed-02M8+JBBZCC%Fc-Y3p0Ai9 zRmfY@gjd^rHbVm*N6m}?pP1H@0Fr?9S{E-iA5j-vQ6HMj3hjVj3J4-x(OZ0>SEB)v ze25WN70*KlpjaJ4o2FiWJZG9w&ywfcR+FBoe_LIGB(Up>qVMc}d}?)5Cp9gYkww0B z_u$#>Eo$O&_yRmvxYSbWY=>_<*Mu3HTPj#EPB`kQ`A?qDif{2sms; zH7_c=b7}yVDI$RZlZ?gOKYENzUq(1qkTE1p-|NX)b!gwFL8ij7ki6mj(R$!WKI?s) zdQN>TR?sbwwap4ZEbHMbg+7x@AI+_RyxI>#(6PU#2bwzMbS zoGb7)|lnUX!nvpNhX{pn(~bPQVe&G&PKbGS+TMD>vx|r}4cMMoDDIiT>-Y2(fvpYlwwl zpVrzF3@Kqn1BwuwJU-`Ifk_E;I<_!ASS|)5$PW}3rU;Mz1Ea%P-b#V!FWvqKS*UYl za%m<)^tZ;(SpzVp6I&*2YEL4_f>h42g;~I?#RUK080h`R2YfT*0o}%Vi(Zwh^w$xy z^y?}CVWYTi;xY}Fq#WMR*nQh*%7tfdwin17o(@fe-q^iH8#gem7;zLR;PRWuGd``S zy>FZxC)z8g_GOsDIu{@0BcAWR--q_m;6PN})*I7;U+KUv)r?&*z_$g9vnx#!U%-M^ zjfZdqpQA))(Q$`}jrP8R!moUWUW8Hk_c2oE-s+&xw-FMoWLGuY;Nu5GwE7*c>z!E_ zj6VOJa!9Ue^X2YeP~#C1~)o&Z5G5lN07iy@kpK?djtSlq``{58U9KxTkp`WjxN zfl#dQcg*&|qMQqltHnR6lZ$JngB*!1OjL;8#eA<4B6bQF0#?r;%Es@DJ=7%k0-8TO zt}Gw1sRgD#Np|C4QRAjyQw|8397NSe>&MGPda~b07hv$>+2Q@zQ<^SJR0BRvN0qCX zSLk0B5$6>AJ4ccC5GqOK*fi+f;3XSf_&u|T_tH{7ojV{ND}h5_sUlyr!zOPxi8gS! zD{IrhgXyNi8-`_2)I%8BMh+Qx=uqRaiB-0?tiv$;qH#5yRU{b~i0~4#hn<;1OETNs zplLMGNh|Bt;zbl-RpN|5JPK@i8zbeWR5Z~Pv?s*$*rLSxR=M*D?32!b!-A^{=n@Pk zY{v+;IaHfx0vaAV?e-6cp!Q_CJ~g=TH+iQDI2yl9at=?otJufJa@MWZ1>iBB1D$xs zVoBeHhw&rfTdH#-3>qO8QPcT*t!=m<6K0gr^W{g_cRGsV<8jO3ECJari|B)Uy}Lb{ zLPSK?yQwCBdgVG}d{&q!{0au%uNy6^$oj$7rR=he5L(TkQ;Bfr4F9R|u2uf13{tA$ z3e`QRx5K)N#U0Yyb2ONQdBQDs{4&{v_0XKa6pY)_|2Qezh9L@@N^i_*;XXu(GT!u0 zaRsI2TFoW@&vdF}-V4wr$X!4iBD2n~`e@k3BN$#$L;XuJdL$y|an(He0Y$4ZCD-+F z{aIC!w0ivIxig(pb>!{hS?!>FS)bD$#*bpMePM|$f93VZ>raDgZf(vJp9^Yd>RmA* z0OQ>nnV44hbFbA=`@%0eRu)~CTv|}#hARE{S)HyUb9SD)v;nZV5hbqale^tDWtYz@ zz*Tv5=~K2X>e79bNn{#e%4yQUuAEzlUUOJ2gzwWENwg@*p?YC-C1sB)sPS~|#ZW|_ zmvI{ZMx>{{;g}Lv!+$h=eZLU-$c`-Z{ufne;Z$|hc5MX#1pxu+Zt3op4naY>q`TwL zKRTqlLsGiCK{^iIU5Bni$KmC9=lN#dZ)X1sd*6HRwbr$cb~}H}2_O>8%;oP?G1uX* zdQD3%kxXcn*94G@lrIj>Z`6*%1Ksje=#P6bcNOjx&I`R0KX~z146hg%Q7m!Fx~~`Z zr(=T3daxl{*6%kN97rGFY~U*+4`x=@z+?QxCO&qkaFyqRO`c?0BplfnK8v@MpW_-B z-L&#?3M3mPl{n$@t_##$-YYC80eIY4#=qgv=z`DyKd56vfS@jT4aN&9KJ zNuxT?r;3-Kg=}QJy1e7`>T#}}iMj)UR9{M}_ihwt)cR+HVxot1^isJeQNqM#?FL41 z+(h0*C#uLVLhvt)@(WqKZCeQo?$;#P#Kc60qLHX7y>If(_LGmsUIW`Lf+w$Kq`z{H zMnX68&LeQXMR=XO`&`((ZPx@5pc#?%Z4E}ii%@U~SgYjj5{8|8H?m1?e>)(H&i|Eb z(ppgWmKJjgdOqaki|ZstVNnN36Ha=%XPmx{Ie1I9aE@DMRMYGHX=11Qj}4)H?&n-g zA$GK+pnXn*L8*7G@qN~V2z%JdYzX}9g>eUZV1Tnws$3U5P!l;V*e8A=kBFdX6MM{4?bjbUZH&Sj z!O;>^QSP)qEEX~srCaOTU)K?J$~FU3Hl_crSzq4AOf=VDh#35wUhzF!p=P$XAdJd&$?wBOer$N9&`e&U3x{4qst2MR)yl&F2<&D#{n^D zhG~9i;cryM&j&0UkfY_Cn0nwE$9d~Nel#V-Sdd%+6W6Utj95>Lvu^k!C%YpN*LWig zwALf%8~QJt!g)QzFXqy40jS+w8%UJxIg+cqoj>FC)E90sIO%B3(cc6zEyYraJ zUpUFOeDhmRG^-C*VOZ}(JxF<-YE{XwZZkmx&`^1=$CPEwca~ZR_n*i_CrSYSF7~7Y z8xznrv8p}!fmH5G{&Rij`*Aa_pwYDOk3#49|1!$kno;PjjSvk?^9_A)zdh|reJ+Nk z&EGQInf3CPCOpImPTF%9ljFT#tZZ(8Kk<`#TZGgsUqSs0T`ZNWNr6F{==eTp9{5!x zu{W`^Q$ygyc&|{@c;4-OFAfEkariqiHLJDK`=jL%26$5{W@MCnUxM!RG1qx18=#!r zp93SRHXd>On*M29j}I!ZQ^21VV7LFu^OZr+V8hY<{&fNQMsjL=X#1T+K3rndSba$e?~4zwMZ2Bam%4v54*&fE%MA17@$ILJ zm7zMl-T{?Vg9yp?kHXv@SL`Fo>4dCGsDwj?PBBnb<1z+fr-}P@__%pH{dklSgl01k z-5oG*(Roa^^`c66QwspGZ&}7e-WnY^C-X%PH=tYmv-c>WTjFlE2n(QkO?#0G z9$81gQ_r>mcNd0&PJHJIaeJROPKhQ8QI=*8?J7jBOdNN!C+2(5@#DaE!)fb=ASI8n z&?%L+)BNRUuLz-*T>O8Kg6V5&mrPu@K8V`6GsAf+w#@E2?_eu-L>6(Hw%97PgOpFa z$Q42cxblfe(6iR8_J5qdYxiNvkYVmO_|)|SK7jZvidBX=w4W`)d-kig1xBA#u65Ub zza?KqZgx$+7e1Uk!^c8SBe+*eIhT(LOdI8wN<>y~p#?5Vsx@VqbZ6I_#A>_rm0|E;w^Yt zJBphv-m1E>@Fp%P(?KQ|aJGZ16QRjh%1TEQATbk(qj2j%dSYODv-0A$b?D3LgMS~O zv{psMU38!|LB3)K|2(Aoc*PhV2NL%MQTg2kR5RB96|6+3Uh-RzTRecVQPsg-VFV+I zrRrdtql*F3cpYKWcz(3^9JT}Y1~N(PQPOtH}*G!en)s{vql;AKUh_ck-+wRg4ydh4Uj&uiI!^!=~G&*Y|2 zB-xwA2~dNPWGM%+NmWFa%GQwwJ1iEG`V`>o_)hxvh+Vz;{Fhh zbU_zp;!mVox)Ck}ddM~IzA@saee;rQaP9r9ctTxKk}Hx$gH*%H&6tXYlv#jU`i|%1 z8HEdTq4h#^4rOwtq}d|3v+ta(_htlPY(gddAk~~D%da$^{+}wGH`d^|l}#P}Gg7`P z$x9LfgcVs~Q$nM}_ftbLn#-90e z%eRA_{q23R%czL=DayXouf8j+$gfh1G4~YrByj$NhXWWsZiQxvH?{BOl zUj}DPr)AdnDqF%FWcW#4(_}U5Zn)i*2Xh|HMqr;u2H`-*W5OtPgE7!EgY?4E?=lQk6jV z=Aa~^GWtJ+>=R0vAAa6A%1NC5!cJ?H-{0;1a`(HOtq)<@!7?7A2va9j8BqWM9dN(j zGZud({l}L=MF?f=9(# z?8y#$pUUIFOp;>3FRmTfs`iFhh( z+!@W|k|Ob!d`ElaTQk#ngYmENFFC=bk~trEEg7}n8j%{xYiUs!eRey5E^t&^#Ro_& z=X5Ww*PwI@fyp|=0Zz-YnVXm~ zO1sG1z_Vd|>t_9?`_Z$s1XrVR^XXQ2P27{z`w-qAv)BFStJez^4Y*lUx_0shM%00T zr?bwR?33<|WD(#=4*$yX+#2#?j&d{3z8t8w*eX>c2vIQK4wg(LS$K=I0P4nF82x?| zMD0z{iqj2*@vWS=wZjucd=oCF^*h@*3DA*lcIL4DNUwGB5*A6;kt!Bvmi(nNs@c^I zBQ)7}+Jt`pWpC`ryMv_djJ%i(K$yQgG&{^f02*hin*oDYOylXp%I!DN4S=By;Y53M zFdR*+_b|VAB&Fy%0wDhy;Sy@gUej#>Hk__$`3DL^c zgLX`2{ZvEHKEkrb8$He6gZlp;vkcsPn^go0EE2vXuw6$-`#m8{H?Q?g`o;m0m(L;G@Ff0-i9E}b4$3&nQv|EUOcMO-)q3XV0Lw+Ku= zI63jn=$Om(WVPz_$}k16_gk>nv)RRvt^Db`Y|4K-#v6>KWQI=hU+7 zGocS#_^dxfg*1l+NS%w%Z-0OYKm1K4{tey|C*~1($^-UT8-Vpyd0{)<&LhxMc0ZYe ztC*m6`!E@bPQAuC|+P&HFn$P|6xs&ni z-t(os?e_eB2H?nEFt<4tmjw$s?iV!*)OfytXT_JT9^=eZrxsYzlFHfT=;yfFSB%f8 zGJbshm~WHq*d+7`7rbbhA37rStZ#cRw-Y8f_#Iyg76Q74 zLUxDH%U*=3&K|p@^WA>Atavuy^^YzNMsoZz3e4#m1yS+xyw*8NPq;$rqS(rWQjqTH@pRg%9>~%YiA1%1U`u*cKWI&)SHyvdBVul3`=p9W zj?-AWrQ8J0W5LGw-;Eo0mT3SQmg1sD0*TIV45L$~ zuRjjd!hdY#ata+e{VkK|%KT7Lt8mMQtAWl*Up@SQWL1B?G~ijXoubXKh(VEySY*%x0W&t58ab=^xcmFAijN>(!; z?hAh5cO9bfidkzsG*Kg)kE!kQhl=7|+QT<`U0V>~86vwSj&UI+Aq(`##Orl+mT=UuZrp%AN7lmjx6*jZ|8qZPBKFg^ zXL?8C--wmIVRZ9&Mm5O+Rco3c41tX*KMFbZ$0z}(Dj{U&NUs&LkB1+t%I9B^Wjmej z>i`pvUK{i6ic@9VR;>CEq26Qq4p*g@j^5)i?ao6u%U~_oTT1vMwP<&Ej8M@Ia7N=l zkDl6x^TwvmA&!m%-~6dAoo!#<7Q2xUN|T*=7%Ci&S*L#I z{a3rkty8_?`t^rVVLnl#ZA^14UC#PnXmP2&x+u>d2+8*Kjl3;)8Ctx+@Rtje`s9r_ zJiDacd(ZjWJzfdaklefMVHYeB?IqH}yI?i_!3MolDbP2pCZA8dqy@d3WU_rM{ z^5(g$hBd~OR< zlJ^h)0-AZhTk^g^-fsO^)T4(O^Z_s30qKqEQ3N)ko-s1jwh*B`;^)8L)4%$W;`O~N zdOs;t4^U6DZ1;(+8}%mKhg!g+1n6YI+fMtPhAX~jnH26dm-;zurAC+b(@#Zh!~#74 z*bv|&F`4@191~9;&>Neb{7nxrQk8Wg8$you^GEiiK0nik#(lH2h#bi+Z!yB{QIg2T zOOXId$iCyhIW8OP8?`UrS?b@5?DL{EY3THOxqQ23tEH z(SgCa>&UaG783I_{P~&SY%)`7CHS7e@COq;&6njU*s7VuN! zS&T6mlJ0+u8GjT)*(Ppadi zw4->6;`@t7b`mJP4}E{Q)Uh!@o5K`yw=mS2QUp%wyu_PvWDPuD_s|pwg7Y$XAh=#uu zG0L^bAOoJrcfPR0+Fu7IJIMqr9P7f|^yVAD*t>Xd`Q{)#XK8QVf^WHXK$Gd5Ri#e( z^b%Xm_qRhG4h@IOO}V#+JjEQA7z&}6vqEGN3eAWv?2me!5W9BQAr$gdjhELXUu9T@ zHl;kalT>-`6V^3zI@~9d><5M& zN@aJW3`ZuZ{2IrC0N^xA>ioepjYW!7eC-CRaC8y*MJqtZUQnU<*6<0!cWdAdI`W!4 zpO>?RO)RARQJDp{OUgKw?F*ql{nPi?2i&oXo#&mbOMZXDL}~QCJAT}aRq|Fi1t#Tp z3U1q#6*G97yDvU8dtE!`^^K@A57P=i3tSF?y>x6l4shHzp{I$-Yw%|0_|0zDC4wg4 z_}c8}GMyGj>Yq8-np%8q8S`8w#(XZi(_oB?rH7Osvpzrvm%tA;H5maNvtl(lJrK!P zZdFe1DK8FTp~y}%RP!qr=lX6zNviY7t({0zw!*ju@Rsr1q2k35{rGy)Lytvjc42%`o6q0<1eUlK#@e4T6{G7YG5P)cdMSkyD#uOO%?2$$ z^e+78ITwN3Zl#w(9pRSaTbgoV3Gw)(XC!3Y8t+(4-q+@!u{w~~s2Z=Om|S}EH{KtB z_+F#WLt)Noo711v+qipb6S~E}-RknvbKmsk21&JEPzbEkbKlg?v?ND8A&8+6qo-PM z;sj;Z0SnyD5NLuUo{9VO%B<6+KcQ{RvU<_(&^=JgHFNQW=!M1vVj6{ZG7n6rTGbnn zEz4p4{n6!VQwDch^P@zs!2z22Q}m*3ksRUb57F>{ju&irpLe$V@4Aa!GKZTp&RIHM zMASwm8b-^w%b8=2Gcsu4asEMgPUyx;pYL={!>~gF5#t+8InnQw&$By&m%1yJ9zBN` z0Jm}C;pT@QLfXM{Z$oajnGBo>X35?WuR}KIoh4fv3vko|&lP?1*_OEGqOH;lZQCId z$a7kXWfM{gzI8Dv*RHW$DGGs!mc%6qhY^LC>hV|OAGxtVa5JXu_=lky8EvQcZ0Gz& zX!iL`KURZ@dXhWhxMDNS)G1zAraAWOGeDXo#YyvKWw!cAXt;FUIuQEqZ#LOg;sGbB zP|1+~e8hr!uz5n-VD#!Gc~9Ia!&GeaI2 zHq)Mc>+I)kSL@|&96wC7mnzp5fuZVaVLkQOQ{qzW_`SR5D7$CV`;E@$>|M{!qvcBQ zeQOcp8EoSAIr5vX2<`g$HIVTD)~(Xjzr`7Z9jg_h6tI;QAvoFc~+s=~V7|5^=p6Q_yS7G_P!=X}$e;cI6sOUn4qRq^5ML2p$m*2<$A#T*ZtMw}C zuV4T9w&P8fXIAYFHt{XppIFMhw8K>>lm+jt&Dh{$d3qSeEaGrhnePYr*ZY3 zAKwk+Cq~?UQOfZ%&Th&B@NLFrQriUwI?klR>)paUYFc+b(Mx*|Vnv{T!f>Q=V2(Q* zj~(}j;*fB_*&?752vZ{%8>5!y%6!mr{k72#=E>-tm*EX}==us#24!`ASfDvEq2hgO zVG1GO(0|v*_XmgKMj<86*Pz# z+@-!`*IGE7I%RPYjt{%*LaY^wp+aMv(9ROmz}??@LhjZG?-MY>Miw6Whq@9S zJvQwlO7a8n!>3D3(#6*lc4nNgx@FR}H<##@oraq+_XPUlGn7}sKP&1^6)2BnxRx_U zw;)DgNta+GkYoT#@Q5?=;~!pEW<-J%^cR%!{lW1sxV9(p*vPIMlU#>Ynxl(pGA$ z2Kz?8dmdx&`}1eDBA0rF1M(V85$WMyjvovr%x*xG)y6Hb!^TmAC0mW)O)Eqp6iT4DdfQNgdy=7p@0F-417l1-`;-f`Y9^79tLBoW47s&VU zEI{+hBhUsJDvqZ$@5Z*)2O1;-nV|GA}Wv!Vb%)hr=;dNPtk-^1`ubxEb8$S)!XmHnm7MNUS7}&H#xxc zFy?j_5`sGtPUFj@4M-TXsid(6m^YT3Mhf$T28cRw2*rsy#G2odr>nt&A7|0|H zfNL0Wv~;OmY2r_WPKq{nNo9OZ85I+6J{8;UIWLUNdFGF_w3*s^RXr^sPg(^mv*k6} zSN4eK{~ZHQ%wM*1k2=iwrNgTJ*lL4M=^u$kABlQe58L$yjvb<`z1Fo>Ad;+F5d@XB z;$G^cO{fa*w{5!fYj`*#4$nIQo6nYO^W=tQhZuf+lbv?%?Cdfc-PAG*r%jrpJ201@Xfd50WH#b6>8oNXoKM)v#{?4 za$8Ji%f9@%_banRJOWor{8t>Tiq~(W=imjpetnQecVt3T&-7k*?Do!X!+ZFRiqiHe z*_$cH#CY*v*|pKXC~j9?{{gObnwa1lko~_b_6e$Xv8~*WcOoJAbSa;WW_mkozwk+? z*uBHzKH5cdmj5*xc;K>A3v8i3(T@)!w|aU@+_}mYDKrq;I|~}E7WLNaPG8=t!?t*c zm)G=PY@=bS1j1QOC_}SCZ;Esjgd6^QnF14JSC_|6@1POCIl$wQgB(3LKr9MeIxoYwB{& zj<^d3U)=A#Y=MW6fNlLDkk7LITDsojdJ&^VUh?4DuH8Lz00|hpt-){NHiW4sL+75W zYUAmws3+A4jSx5*Ks9Tm$h8c+d&OD205%&iX7_D%pW>&NR>3L<#Fq`=c0Qf*&hPQ` zfT#th$D)A^weA*Z6GY!J>oI4z4GN z6yLQqa)vueI=w|diyP~{k^WJ-Q%D!{z9?kP2YgiSga!#sGVqo**p`~K5KlK{v?~`6 zD5aRfm*cKpOnRzvK2?4HJ@W^#%NVuLcXV^Zw44`s#Cjdzo$2x#<=4htTyET7KRDcF z%WR?sLrGnvEu=P61Gf>YybiS@L~_ zs%;J`gv{jEUx-nnGB`LDCPzuyUJ+XOLq`pnp1`qcnjDcA`~wwJh1&eZ7}Po*eY@y3 z;Z*o`izL>-bbjUr*kt?7FYf&@mrNH_;eJDauR~59y!qY_YJY&`rbxL}WkXNyF_*u{ zr?M^($@u{jh>652!-GT4Iz$8}lQ#1uQ|3@ieFoQ4}6KmXQTdf&c{-xxM$%F98# z&JdLMu}#g+a_)QQbTitWdGCNDDkr@xB?V*U#)fsVi^~EY{4(`<7{6G!4-P|Dky8*h zqb>e!s`T48e&vS~Z`IP%eT{r$p59=h`721vN&OzUO+EhN^k8j-VfF+E49!CoTC)4u ziJ30|MgDdMF4kXq{f;)+nd|0glb|XFF>SdYVI_1N)aDkksIs87#r9EG%HSSp zaF)n?miu0F(GE2T_>-ZLYcOV*7iD6Gd+PnL2p+QYxl`ZOe?C+itQ2{w5`5TEiQLn3 zxfQy6P;XQASx-$KZU?Q9^JHnrV$btd3B&=>GSO6Px!EUPI(dI}H@LZ*eogps7)jIP}uA-ne+LN?IG?p=(c2T)Z3$=iK0uiE!jnVIH6+uetKd z+#)7HXR4wB?3i_1ynMFbbTAnUWi%uiX?TE@U-DjX*cJNB&kLOOFTDo4?(U~r_2_^i z#f-RtEMW#EeXprqsfqMFIiW*tJUULFsX9a^F1NYZI8AB`hI@=uMgJ&hgxdm7&OjKC zvM9}5Go70TKO;;#x9VH~_ zD;rjWUmliF*6rsy!2S*C|9<_6h9T_{illlAiy(mR5!ZakqAE`jMpS9XM2~8pvRHk7 ztbcyG+@yxTNqXiI5BUSbU@?slXOx+-i`rkcmH#*Wz1d;=?6RHRFp>r+@_wA_{(6LN z5tGxA@g&C-mr(r=8(mGgL$iIf=vPjZcep1(@GDj*k(3Rdbp3SytLuWU#>@g&u&6)I zjvqf;3CbPaFQ>v(B?mC)lFeQ#8u;yi@()5hZG{bxi$T*C*YQSxBl~_A&)mf}=gD6} zZ+XLgj~PniV@}5HoY;Io`dn;EZQ@yQD%WA&k)l(#ZY|R9K(yfO-j&gm8tez;Q{MRo zeHpa0ZnlJk{DGl;Jy`3NRHf6JAQ{y9`!9Wt=Xa@;mpR3}a}OpXrQQDBn~t6H5Cuvv z0|yz9({hyBK{5g$UV-w>zMy{{H#g$JmTKGbbNjfP>+$D5^Q~I|r~Qh18lY$Vl#A+XSHU8p9yB!Pa6i~UTuW$&E0ZV3BR*Zf?czSy(ViKayV4`H zS6g@YUMU2KP|m#pif!tJykdPY+$W@4c+Jn^fqSg8n9l2lv%43%mTN7OFgVyE@IX0s zLHChn(^&t&hTxO_qBHSTJUL~0?TFONlhkl;%MAST}dt>zv&I!k=j4aR~@Uz}WUvncOSeTKt<1giU+%;nR~V~L3PYe#km z(UuRLpENZA2$oXNSyL37ytU&kWgIBXNV^F7vtg=*dnHZ<-h&fYvd=gpJ-_y&H)_ie z2j|d-_!bS$T=7-EjbgL%yhzxwUO1GW*1#h*D3@q#@W2K^{K{_1anF=qYf5 zGT0Dj(Syfo>7Q}-X8ZR2jNBN`ZL?XUQa zX({5o%PBlaHI*G}4bE+3unh+Gmku)w&eN7(;x1he=L}g(nsn3QqxMQ29XiNpPx4oI zR~L>PK1%zf7U+|jv_vaQb>&#eBN7VzuFh*LK`wY8+YU4P>|E^e-^Uzy{l`NZBhVHm ztHX|##|xiLbj&wE3(W%jH5CJF0(0G+184J&I#B;R<`mGZyZ3F~(R7F8ElE-h_Rk+k z6&GmRHS7;ov)qKPO-bts!o9rzj4@qyPgVTkv9N-I3fRV`*zG-gGateiGYmos_KMd3 z5GPRPP#AZCLXU)1OHP1H@FWMstWZ}3l&;_?ks+JOcnwHVeE zd03q=Yb2Z7Iu>0*irw>r7^QgESewt+O+=>;*&n`%M=~e>QCj`PhHo6~>|H*THRah} zFT^k3h3+mZaE`3Q^rYJ9+QeM0-=xs;{Wyv^OFdCn!wf&ky7wHA`BrF5wSDomu7ndh zK@zB=(2|vVLE324YsN%j2iEs(Oxw}(Ot$LL-5-V74Vl69n|nzM_j{^bsuj2#?N1x; z=MCYC-0|~O2s+-x^V!-D;(FCpeUCAFf#bEafL=@-b?j@HK?Pv2=vj??MrwZ6gR?&> z-J*QV+W!vD&Jh8W%@WN1RYBX?S7{07tF}A%(MS6xk=ozI!FumJEyhcF<~z&!b2!?i z*6p9S`=lSDmS_x5vd8_e`jw*V$l9g+`ChO#O z9?!`mKxmD`o0k#bA@8ya{uZ-VCp@+I{Fl}uPo_|zOE7SD30dD(Xt}F2Kr)-wWV&G< zU&yvC&g%RD6Hk75sDw%0?`A&^T*f_~%C`IW)z@uCRCq=YnPz!vq6rWThq{rBH_V)E z{$=DlLaEZ^DUo=W#QX8|s&A~rHWW$HX5z&(lWCj%4-O8B&k$R{v&mI+g7e1@EDx`! zqSnuhiZwvRSYK6ee@!R%NYH(UepV}}#fS*5u`B)7Y5c0E+5TImp=bIoy*=B~A+|UD zY(*lJ%!a%i;VKM)vnzvvK&lwYiN8zAlR_-1L8r+pP2iU%^Y8Z| zj8a%@9*fPBuODUMhK$4vr7~9KzIBgtLmhYZMGnACHEJz@mpdf(sisF#ND2!P`$VJ``w$T$GiRIcvNn2y%koKi^%VKVM}T*FL1)z#>F2<&78$ zOFAg7|H~+n%uQR<8AQ~}tHN_05!#ioDW(END>Ay#Kh)f4yyzTV9Dw0Zm3Dogct@8g zU>)ty5p>U(T^;sXJ{KN=ZNCKZj&=oiqGvH?Y($4RB(R*}2q6X=E6_z-d93cy&| z_NeFn%%I0q*?jia9ai2b;1duWi%e9UB1G?@3(b)g;1_#5VZ59DiE(HDG}fV6lzAi| z=9Hjw-ZC%DjMv+2OqUUD6<66jN&B#B)8ro!7pFU^m85si<8-R| z<}|X%HfhjFv<_L?aPxKbeZud*4-9%Aw^t-~3%_!_9(UFR#s~ZIpqGOxq?C}`mF15o zp2YBP*8bt2HpdT?5oTKpxEW*);5duAY=!YWH1*<{%~@__MSoC-G>jPdKF|NNfwduL-^aN`#k`E-N+xBeTSaSi6r?0OrJdXBmZ?4yY zTX2_eZOUIMCeQU<;7CY^A`KExH>(mq2P4T^&%&GY+y-sBtHUY|EF#cpR$1S$g`wnS zM$!^Ib1FK}$W=73Q}MA;tsHkrw+?_yAMPOxqNZ7_b;j>%iqAj8`?PHXP6x z#CG4?ur{PGF4Xs{CO9ljOGJisa`Z8KM7ZcXhZil-X;uvbY6q)nYw=zSoR0|GS|}Jj zgnYR*mI4pxK7;f&0@%s~6C=-y4F<@D!+p74MAJ#tN^zW;S0v8zUd4e%B-%!SsV-J?1nX4K*6j6JGg7EB#NAzq8 z0(Mmy(qV4^i|SvZBMkL2lyVqMK^#o)1_{^eUIf=dz`|1$zVUkh@@vARJo8TB ziPRi%ecKNrE0ec}TYWZ@jcJ9=Ewi5bsLe^uBseG=_dxf%ym+9He5=r_T!41hKZ@giDnXEB-mv$Oo&xM@3ohazT^W!a5^gs#0dria??0@n{VsE{SXDrz8b> ze}p=P<|%P}F-RJpqsz41op8tnf1b4O_$Gs8!@ZD^)FO1U%jrWzk}u{OGPT0Z#5`GA z{tZ=dK+7pSI1^&4nAh!D<)nPe=HzT-DEK||NeL1zD+O-+t4d%qZ)A>iV66HnkuNCT zYfW~#TR-0PUtx(1(>~UV9n1<1UY!Y;0<+K&$jgz+4r*q<+_{hTUlDN&x0ET+{*D}A z&L#sckL9anF0{aJqcX4(rmXdga&r#dPCAR%qoqr-^a>G(B+xR>3-3gt3+wI3DL}K0Pt={i#^A-`E z?2+#f1;7_mC_dw#M}o2E30rjgXfQ(%G?59ujdQKh`KgSn>c>Rs4ZS7MiMiS>&aIvl z-Cc1~lI&EwJU1duQg7c8YkmaoZ*4jND!0;P@{`fSzKiA#D#tCgB9zFyx7?~edj~~3 z(@H|KQ=;qcBTSdM)8OT&%(bUgX7#AkM6fV)W-pa?w>TX9&L(MGBML=hiZ6h~;5H!o zuFWw7#lM34b&EewhGYPtFb>8)z-M@_wLHk5ZL*6v1TnV8yE9 z$G?-=R7wPVp+c7UM`|?c6?&G}YaZL>j<6{X2|$f*Mg@k{rZSo0{P6kOBm2pF{+Xvt zaVXrbRi9bE{R=5<^szwhf-&k#7vH}ahDdN>Xkqls@~;fKeN6|`>T{|5Ty>+oZ(;MH z{q&gvM>l*X&KIpz%s|AB!0}+@EB)s%!SxZ&%W8WDD zWP*t;m!n-*Zk~gx<_R1B8dkIBUtk-euEwEnuG9URKy3e7i-VCst9&B5J=|&x?gvkF zwJt2y2p*SGLI?aKgsq8p(;S z9{bSll0)n5iWOtsq@VEbA6|HF5Kk`Kfovddh_{#yP<$rVJ1Ne9Rn^e}II5eZER5%+|41 z34|sC8R6&w2rX6aAv(ueU#hG=;hi;5b3E{D3#9NLc1?3>N(=mRW(=ufEiTjKe6$IK z*Yn+#EI}+)J8Hf1I>_)*X6bQO-9{YHP@L2JH8KBtkLa!_Qs@GEr#_CP3#3aFNeDSp z>VUQ=3$rM$fnWgug~OFnbsnD@`^dj-NqKZR%^a6c+ z34V$dxrt=YHBzs({#kR@_N!o(1BW#!{+-dHY`xA$w)?I5_Lj_BPu;r(Uhfx}$X(H; ziU{<@LHe#1`~jydJX~jET-JZxVOiPfvW^6*ULh z1yv|)OH(4=(di0;FkQy^Uw8T7YVv~Vmr1mu7F)1!zEjIc&9jFH4tqHTJ1w2S814m4 zaU>J0?Ni6}1tU>nX(s20b-U@o=n&ASx?hq*X{{o*B}};UrV03PE)64m zTd7^^H|GsE?`0C;=^tDy8}HW5@Gl($a$4*B&f7HeQL{L_&tHuV)`3$T6?Zf>L#+KL z?!222`;UtI@{3JloTVDW0>`MVwZzPPA zw->U~tA9`&AFE|g zSZ>HMA_&g3j??^7-)4MhLJqWg-8UnzeeZ6_F&Qmd>qn;D;(y=_ySnMTjv|y;aqN_k zoA<9=Mnw429ENSja-00+AC3@RyZ7=U+7IbcTB0`lk6rhz!%%_EofP@PN;c*k3G%hL znrHqY25bvDt%gry0&~(<(Yf!+Q$Mv!YoXi?ekE3x#OfHU{}Xh zI}>i7z%2c_X-Q)vNebN>lPBJ|VrG(nNW*dcfXn2wF`@~PW<~F0R2d$@88QA6XyEc| zI4=sXMfK23@i?t3c{~K%!^SxA+8#-N09DVnnzz#&f7>7269l{{(4TM~yzJ1$)`aj{ z1yG>`RcMVA6WS%IuUt!>^dpHp>ovDIYG#qSGZ*}IAUNJ%AO3>t@gS^J zxDASbE7eLAJTK$0ct{5^^ zQqg=?K(i}FvJ?Z;X4s?kVysj%{Rv+Fo<%f41&@Ne!Nb^e631aUH>t#LQeTK8C_4$8 z)hPR*NwyrmhY6Ch90fIYRkyQOd)flN`kIKNo;#Emp3c0Fp!R|ya}(|{et{JwPD#QA zVs@APAKx}uaVW6u@W7orqws`9$FRRE*-ypU6d#U@N2HR1l>9f%xV+ro)l?kL-nfOBWEUH1`$cqLErHFBE2$`4w2=ADGaOi=;jS+ zeAmd342NEmzU;3z7g9PpJas%H0TOF45YQyq$R4NqMo$Ds{!#wtnE*H1HVD=WEGg5! zEJVY)#{X{g@wMe^`8=XOn**+pCOBer{!ej~?cJ2xZRr$3*!Axo6!b2;+Aq<8yT=L? zQjv4dE?fU>=XRQV2 z5!rX-W6#}hrsr%ZDa8vcv)}GL62L2LN4vabGO!mC2QW?cI(J9>y&zYBhUCdafz=DTq&7$#3)*O|(nt7S6`#Ept0ec90hp`O7@Wxq)jEl~? z$3ToA0<}7|dijSVqG_NZDi~U;fOAXUcZO{$BE#?E+48wj@9Kv@c<>?!h6b!nDCYwS5C75y6Z*AckQnmQkW$~US5v6p;22UN(k-79 zO(L{Px8lviAPMwdtjJ;xQyQDtTAE=tMg5S*I)8Pqv6a+*E%ma2~9?;(D9r{!ps z+J*lOOl?GHt<=l@05h)&biI}=OjM@&)tYHw&we6>l z(=*BG`BuWnRWAoXWO~^&KJ1uOxrxU8V-P{q!wQSnkwi6vfy}%oMu!kW;qTOuFObgT zMQkzZ1)45EIh@!z^PgW}+BUS?(bkmYd^y>VY2aMe*UT-uJ16|&N4Da(y0f^q);EH; zbMzFtVz(vXH@2qeV>!e`0Yxea%!!b5RjCg#Q0hjBnpguSuJP7x37gEf;eB?7qK0>K zWyC*bYlivsu(Me$`RQDl&Vjtii@%DsPD;>BSmpo8lu3;7))zS7dPIS%Iq=W&CT^G@ zZZ$B%_-69s%9GRUPli>XRyQP|2$Xj%&kAQP#N=foR+1U$0@oppWipI%{hZ=T<&vfPl6(z zx%eBJ!pARPs%*UWj~|Zp2{*s6WsncmJ*O7HodOd|()5c0L2NG03c(jzHBUNnCFC!v z$Ev3IefL%v=K}~0K91iKJx}_jtXk&?j(iDTT`oFP3w8%iq}TT~4p4|M7wU&>m|K6` zo2(CXD-d+1>$ApUv5Y0~Wkd6J7iNptXpP@H@SuinrIQxqWSgs1>J!4sNfS1F;T-vM zY7T&oDK!P36s`5bVIBFKM~zU6{G#3KnPor6$;r}IPi1NUF-Jv`{%+fo7w6z=sRIsY2w4mR>QQ}`eY;|XtOHkc>^y4e%8RStJ8scEst;5=aG%G?fSX# zt@-(s!TCg%u!pZ+ z3yW(q@KGSss8rsH)!H1|0zd>_-aj4Pn5wED!H^gk-Dx|Kuf(#tsXj3%i4l0`umMaN zyE&f^q&1uTN`$X zkkj}y8Bdw!gNlbJXtLL_tIs{yrTyM|odv@rbjQ$*(Bb!w@nVzW;k68^hs1H@>J+!u z?Z>{yXATi)&%Eq2q+A!m^#})C01CsduVo!n``nQmzeOj|{H_W1_qe~7zJNJv(_X>C zS7s&KvmLJ`J3{BL8&%fa`;Qm^aK&+goi=deZ=ZgH7$4<~3GahO9msLpm?8Qu5AEGc z<^v6JffC^jCa<{@3F={r3zv~9OEi{HU2_s(5=L%w%%@ml55f!6O}t10ZcK~5*;oh< zJgraMqq_zn7*JK~?`@|18H1Tb9%%PHghU*$fyfSiNgVSk;TUd4()6|lZTnF!a&7j} z!hzC>a|>cUh!6FDP0b;6D|?I%O@ z0>%ogq6&Kn+JmfgcfN7$^^rZc9I`{Vx=3<*5C1m*ze*9VT^1d-I?=$MS?nI~wWEsZRuEyYP0e)t znL$@U5`6~3E3IzeNnGW~##bp&@w{J6qG%GcP?cJ>tH5%i71o?MRBI?F-kw2w>0sB< zUO1z9bI;}6mGotzAid=3>RDHg_fu@j+SJp*H{PGt6C^<7HP<1)yOem(s@ujC<#UGe zI2#46hdlL=#VYumU2@Oei@XM1<+FN@?VX41v!n75;xL;t|CiNC`Dn!y#E$OsFE$K{UgOWCstEAZRJ#;%4< z9A=W7sxbakR%HAq!8o8dzFC6ZGvxJe^QiuP4Nwy%;-wT;Xl$!}j@YMZSzplK)K~d^ z^vN{vMjH9Hnyhwz5&zuSilua%r_E(~Xn7lCUrcqeG9+9w?oU%PT6M4)7k#hzp22!j zr+)B3Lu-H-H!D4!2pxUnJ74^No<~Tcj|1JRtE{xsPq$~{#+G6G+njRyoFH!W5 zhEDW;yelwyhAHEi59a@~>PTf#X34-BA0Xbn*^GLStQRZURq0SE*d890DB7<4B{E#1 zNW8J~QQ#*L{;bww6#ht%kAtFZ9XG>JNfY_#?Kz>Ph}6&H;*LI5easF~TPMeLsjQs8Ovff=lroP9o}zvjv-JpoG&Mj8 z7FB(2(qFUEVL@>sF=s8Gm}zz$=t}zy%%4Z$OyB+=Z5(mV}1ho}I_SboTU$iIbfxS;IhfgY3w*nof6}2_yQH-{)U1W(B9wVB&Cv;>uq`6fj zRLwAvu6!s-n(MEwfLOcLHnI+Jmf_>t`IAdiyCP?Rg!+q#^u}aCC|51egj7F!b^1Er z;Uu?z{wcdvY>NeKg}p$TV#5Bz!QyrHuRQhc(!zk_o8FS_wMzz3*tK4Nc8#?5bpCcT(iKS({hm9arZm6^iXwdadMd z!mp2caNW$R&9JPM99RyZxW~xLPt?eEqpmS?1Uq1a#u>Blu1@mQ%*IWm-mn)Otce+1 z5(e#KGKIuji7u$)^ZdqWb))_nfyyu$jaMqQXIFss?kj)GaQ7zsLeF7*jNDdaWIoh9 zS-=P=^`V;0alW&?$W#$yHd`m_p#|Ihgbm(gl97yxS;czF;rj?w8*+uF$8!{wQjA>N zN19pE>5bkL^KlFNaO8ANkkKR2kBsiBNEp1o5#js!{x*iTw%?GlmHD+SbvxcSNyny# zehPPvZh`(x%%J5<(=>yOEM;57B>%-1o&lmICH^CrJ1L1@NX*!o2`5&Q9>>nwnfp3V ziC3}@L}R5tUZ>rFM&4|0f9gCwk*-&$EFT|9IUjMNRnZ2qtUi%;%~Uz7ofnhHadHBo zHtZ^)nSfn{96^ovvpD0AhEe)G&r~eG?J;*_T8}%rL`P6pSXQ!Z4AWaaM<)RRXYFs9 z{iK4p#0;sGzJ<=F&!V|t$tnVChKw5g*PN*0@$=wZo9GZ=Bt1bz=*KPC?s6~F>FI0l zp4(Gh{GQ!hoKJB1#o!*a6}<8_x3^F#mr|L-%rg4%Q_KP`x?lm`vGEi!I{OTtIIizk z$Khqg!3AP+{kT!_Tq!umDg2zV@pM31zEplN_l)OPo{H)YL8it^gSe9Vt(mGrqj34T*RL?-R7ukeJ( zb)g6tZ@9??gZt*`cv2kE!i4U?+cu+vv=(uLW#-njg|0a!i=bWbF-MaL7u%Vx;C^J9*V+`H|j-|K=34w)65?xa#m|*uI>tF_DTDB z+us-VsnSV)7^WqrscXenMw*LhdE8+?gB=~^YPfo>%VI}(c|1eUvHo#vq$}~zwV0JL zA^>bk(CA9be1S@k-6`wWfu165Dt+_sgqzEs1kjna2)nj%0Et!WtKCyvdix=y3Zeg8_{uvUDx@%(c^6oSEf!ni zQGXwL@p;Xq&Y5-M*F~U|e!$Z4U$NLqxwh&|zphW6c=SHSHLmt#V`M@yuj|59WNv&` zaz?aGRa`9Jnl_+JpEu9pov6dzYSrY#lrak5$w6Rf@A^D<$=9s1ovb=F2yh*>eqz7y ziWm@chUIEGwA#^k*|Q+*5I1aGon4e;*wQ9g;>16NddYc-HLn?9_8*bZ(ZTr&l`c(F zY-ht2F}|p6Pwi+r-N&GhAz1xmTw0y{bnz1lZ*l86yNLK4^!Kk7ES%u1*jw?+^Uflv zdfRPj)~XszmXutVzM}jgCUDnXq$}XFEmNoIG=1-$)E-UT!oNH&=||EkrDYOAx?FWp zFZ*p=(5?g0Mh5ZWD~HsbLo;kZrX}K1oqch9Lwk&blr6Q7o)=y7C)UUNJ&!Ug&9~u0 zEUia&<1Q;W?;6WpBUNG_o$2p&TpL#`48!KW4l&5iYQC_URBd_l0`d3&m%fRX<#>o# zRMEQZr;041xT%3oxR5_Pk~euREY8!_SW6AIw@q=INEzxIYQ!C3a1V{uVi2EW*VZ!4 z+9NyL-ij%KGMRZbTY+Bx-nR}l*vDd#J2XxC4H}#qqK+#W<0!kb0zX&%M=KKWp~PT+ z`_k{-v?$w$Cy_NM5Xor!yhavn`wV{NUT4-6_selIMxPLzomRmrW(^7 z>n+M0UHkZ@e(#HUi2W*vRY?eiGB0sr9;SbG3~mX3KhhrZ=BCUf5W7w zvzgfiYmU_bT4BV)^koO%!*c~{Afp5PxivK^Sn?3*{mwl{DR1j8fw0`gbV6}cr96hO z0PQ3(5*n>qdj%DH)r;kh9d%0i(DYG#(^w242W}}6(t822)~LYw8D$y;`*OO#XY340 z-kw0av4@Y@m+{qf*IuJi4gH+f$#&=s5$Iwfm6z!NY1p5su4xdU$oZ!Veai~76szZt z3q*YZWglFB|j(U9TVC^`c z4IvA+mYCNSBD*RAfqw**YZFOa+fkW1-2ehM!p=~T*G5&;P=(hG&n^$yqp#QA3YNnN z8qZM4nNNyW-$=U}Y4hvsRnGFc1z2xpF~=gNTma{fu|N@#X%vjk6UGYtY508v(eXiv zWX{uryj0;vy52jod722H;U4^L8pa-e%1GtyhxbWXj0B)ujmcp=7Lny68}lqBJ0Rc= zuFz7f{E6T!<$cTIyR0JoYdL}OjzjsT=lbW};Ypgu2U8Jh$^_y-EgI)@6oI|QwrApa|;=T3`#Xc> z!*bJOig&z+$Wt8RnZ7uAza8m`mOGP8(x9;haHhix9y4j8v?xRF<5z|uQ0ps||53O9 zBXBF^6Gzrx$Dy*~6RWCrILw>0hI*80@n zP)kMn~7R9%=qevUz zj5J{XLo2HwM>MS`Y6w7#JLTnNzgpg6(`K5O`RPqMnA~u3;}`wLXy1-A=h;{MJ;6fD zIk$;~*f#a^i#5UCY`1`9%2mS$i_Ro(+-BS5zlLAg3>R}Kj!9(}%09fN{zUmsw}E79 z6R)I^fd1z6{xCJ7!Pa%4`L4{;MGy}yp~5ma4pgZnNgs=zggFV6VSbRZxXFohqTBvH zP}g$$v%i}fH|%JU!SO7x;91^52dN(c2q4yJ7deldzEbcxkz9W4cpldET)wY8(4A3^ zA^#9%U8mkqnme1XpbObJzN>dVTj9&L`6(l2`6xDI9CN87ay6vdtJugr?f-o;Nkj`3 zcK2@)$u-5Y_1O|IW!R3fqB~!bhQd6OaT2WmEB?XdVSK0{>Sti`DPbL{XcIZ@i?g7P zH+dvE#mLk562-%4IqLKnI+^X4gyD>GJz zFGYfwl0D`@Oo6ksW%p(+=-hIP{!UL3@iq;3aSciRP#>t;D0JL=5jg1tUd?{l;pso_0w!Q?fxic||Jgm1dD~Xn5 zu_oM2bc8!HV*DHu)O8i*$5Yx zqiRRcHoK&q)ZS8gk6|#k$o$}14+Zt|xuA_Qr76~jQsY^eJM%7BvXP?iP6S+9W$Y5% zmSLg@CJ_|(jMT1=aRXv_N|cU`4aFLz!nF&qRg0QJqh;r$ z1shh7O7DV;hCrH$THB^DOf`5*e0mWO#Z&zmn+>Ij5nmUKY9m7?k8 z5A${l9Jy?=su)Dh^&I&+?vmflclg05k&^$uE+|AEqb6loW{&W6j$Et03DR_LHc7WI z7@xJ%@q5$#CP$Z4ZHvmXoV7`twBJevl+yD7>?rMdq7y|?yK&>zpsu%Sdq}sWkGAkb zQPl80iy+N-%dU(2ZkR5u?jwROrLNOB<8Ha;xz&pfSy^1t%XIhRzoAz&Z``6zs+{G$ z=y^gnGot0Ml8V(VMtc_0{2bL4 zZl$cyhE-$G@jwZtS4}MyW{^C(G7#LZ?;HSfhcfOAKUI4jT;A6Y+QckH$EI0t_$tfS z5Z8x@+Q3K}0cfTe0=DH!P?*=VAG#=`neIA;-y1iRpLjW5WQJxTA4LCd0O8^al&E0% zmN1@NnEOuMV=9(-q3y9*kJN49oa<#vjcO{<={)_}rPe=a-&+&ApR6*TBPzBb?I&B` zdTNf1ZG?uHOEgh*5(%tq^Pd&cdRrcMT|OXKHhyDQ?d|%z z+qm-xJFlIm0v^8*UG`z`b;@3PE?0{wE^x^QJ+ zeQTTZKYZKxxj3Qd0fvz7Mao2&7;#=2gy{PyAZqYhnfZm_b?^gOqx`w(VTjV?C4+jS zeaeh#Q!}K5MO>I8Lt%qIcoOXd@KvzL#Y$+Yxz3;qOf6xdQ#n0@NV$d|I92}G+ip7o zS1G)}^%sdN3XCl`kl9jcYcC{)z_6W+Q z8QR4nwb@~p;NFCrFHt)SUmlJitpyo5Gz7nTk9)pAbO&v8yp+m@9CSUSkbqz=ij^K2=O*?Xb7uH0eX520 zbL``9`r3_HEKn29B?Q%Z<^*OESduYxyVawHGc(qfZ>67Ve@``gmIvoL`t8W^3e*TM zs>b6e)+(Vce71dg5~0UvyPNg2R41+hHZ|Mdj8;PJj%+;7N|^o=aT~djK8UFv=g&T# z+h#tV^8hq}XaRaK520Apwc2>yTG6fzWxMZVBQg4C4eD5TuzCwOdOCb<;se+ahhC}1 zESXy4!r$Y30li3KcBOR;#j+8~zU~6o>%%R;q3(H*sl6I-t?OnvF9G=s!f5T0e_ud0 z2o`jm6Z~eL!Pu7-04BoN;^!X7J5zxWg6blHGTcY_PbAhlDIB}zt+5XhGZ6yhIl{cO z{L5-${OIh}B=1CMxu9axyftX3%jMrCe(LAs#<4LatNQ6rZl_zqd6voYY zzt2>CNs^UWmQ)kM@!c)+^^^+09N%&Aa_MYiF$+N^ku7JZhrwKVDV@L-c zV5T{-YyZfk_c|Mo+8SItUrT&u z_s(P_Ya|}y_yjM<36n(KyuL@DS2a~0mEhRZw`N0h|2I ztdNY;V-h!-M$aoKiE$Vd>#>%kej_RHH0vfgqMBk+p~~=W<{wb!2s&!P(J@~M98_EX z`}+1CF!IduJSU=?j#IYGdq~XS9fm=uJau{5(Hao;lng)+(LZf)M!g{ z$2e21_P{U*@`O;%bf#vf9F1sQO5 zB+OWMZi77B6Zcs%EIVvlHw%Cn<0xVKdhd?=at>o#!o_z)%^Iw~Iu*fMb0~c>8CT8X zvx@Kn2Z-a5kacz@tBJ>yH%r|4>rU{_D%lg)sP6JhnJIqvOeHdp1pBg)&<0)X{oUBAjcgk?7b%s#$9|qJ(HynbpT^ zJ6dx{lJu3i$;*lU@OzxXo|@#p2))uCd&CBmGFHTw`AgDVlJbu*DGWl#ID^x>cMLq6 z#>-h)gP|MtD~=GufPSFaDLy)G-U~}IW7bl*|G-v8PB5fmga9;|c%xE#xY;4Wxip3{ zY7l2^mRmWtz##3(ww9hFJoxb-D1JeOot8htt<4~5XV^cN+Z*s`g+Zmp4QnufYlqn{Rv0 zSU-=bD@DYjVfTM8k-QWX$N4#*7N@E6K&4QR@{cdNhr}L*ojAUj%1nu`b%IB`ONLyO-#yi#|`oV{8_;R`OJltE)fGS*~%CD)b z>swVvgslLWH^O;0%F3K6ztE_KTFq-3`qVtEDQ(Q z#7qt7%{o0I9H;o9aywGrvMU8SphMUu|7e8|yqP@?pBwq$TZ{Oe$v=0Q-8?_NH!EHG z`Qa~{v44eyz7L*s`D!VOB)pn~(s+_v4)ayyiAWZaouQSZex!yr81gdT0lB`KU<0qy z9NlBQ(AlCul*M|cdO3%^k2lv+XK;oIPfSVO^lV$~VDM}$$6r;boQQoQXrzv!PuUHD z;O476w8g1lJ;R%iwdv!i*TrY=%plbWap)D1ttsdqIJWSPcmGLC%JeDcG9C7b7-39H z%R>@!gY%*(D4Uh+!rM4w5qcUwewdejfhgNP){rJx(wx`DRf-4GlVx)->xNdb>|?Mx zec7;m%_fWaFD`VOgo#)0?42}Zlm~F6_Y2|N*%xIdVb8ALr2|yCuBh+C%A{GQKJU;8 z#nB(-$klUwuikqJzgdF3$^nndyDJ>yCqc()9fw3aH`WdB-MEk#`?I{$!@ez=^VURu zB%{g;H7@j-w7UGZ=5J$#I*Zdqk zforquOiNd!Z_6hH+mr+?;=kQ9(m6qfAeT>FG=U2B#%hT%k$N}2hz^k7Z|yCf1b*T| zdmQ_Zp$@}8c*$-y0IK$~D)Y~0L258sZ5Wdhs3pwWe^fo2KOQ(e~Rrs%>1 z-i>5b`3F+o;R>g)Q{?sIyO z8T;*M6her7^3~|rkzvSRHM?M#sK0#awPEPib%^0hmqA05lfbX-u37AuiDSO; zYpY)!-=+l2+HQ>{r04m}DI}9|AEy(Os`1>u2TwleU#Ik?#G;{Bh5H5y^73n#z5$uZ z`}=~f+rJ!WbMRnSwI(wv3HtKpQL^w7gj0o1YWhNmHOc%%Oxcr!y4Nqw6Y|MVKlCef z;%^~>WzqP3uMrf@qyTM3_QCnjXLN5bf8X2}k}0i-&CT-+fxy z+;`@c!9?ffF`oq9Vw9>g3N+@|`V$zFG(8LD6X70)Of#YjGrWOsXFkxEQ>zX0shyyY zl6K_q^w_Xulx8a_1M4omDFYCzRits zaIXe3ZAW~RP7?sqw(tK%U=o+E?RoUPs*b3L2bxdIq_OcfulTIHT}jX_zR^iBa=Vz{ zj(hc|Y#?rjs5-NXQoz<>)KFh8YPY2mKK0=Kj%!K$n2mw@#Vsg>R&h3~?2g9h$CPp& z9iAMqnhuu8x>}5M+sR^nsUHTaKyf}(g>#s3&!VWX4Q@#A)k~eq_#*mrC2je{Oc}9)pl~!0r$c{#OYV( zH+E^UXkJXipga9-xfKNpB93RuBAM`;zl&KI;)BHZ>m~Kx3cJPHe$pcUJO01P!buw8 z#nr%wdMHAjjlTMmRCgUQR(hAX^(kY^#**^7{W;0Kx0Bj6$MXh1`kK#D|CclW+4{Az zpz$$}>eH!?cmEAM_o(PPzrYer_07~54 z1v?dwmUFli)qh9|4EI{pcue8AmwybJ+dP4q0ll^NJ*tirO8>NJ2E`hwoKI^q^q~$r1l!ekc@L#L-`BVC zK77HH&`oxwAVWo(CsZZHZ&%^{L;yVXR&FDN9J*B&>(D$let(zMx1kX=zx%}VvioYmhy&a}6=Rsa z3XHe<7EBJGqg*$?Jb`VW6Ad+=&N^=4-l8{1addfe3%-y}#>7&t%^7hSd~UDG!@XGM z8cwd_?W`R1xWkGXYVa9YUqPKeoNgFufMj;C;q?uNeC|`QH#gAU@~07r$Z@yVF^E=5 z2g;*E-6W;wzKNcl8~)xMPq~L7q-Y9ZnM*F41mYMG zZO(lxd;ek}6^Cxvg%Vr2@};VPqW14XSi9V4@n$P3p_(o65_8UCHy^!9kz-GJAQlli zd)3rt-J^R5p-}P6)S+4TU+^LFa`l+0OaJU+q8*e170%Y>A2P7m^pt+*I-%1#ZRQqI zy|N;rK_wQSD(Ysay(_#NdgMAoZDa2^yGAv~vU+9N-h z$Dg~xb5L5AQ^R}`NPMgpyY;%&v&L)W9dJo#Mf4YDs%vi5q=_np0;Xsp;Q~3B)y$<} zjNx1JiJ7-Di!RXixiATP0$b|2N)l)ha@;#h3ic*!rZBVB$sDgr{;kyB8Pj3_#aJt0 zLI%HDY39F39S)@5b;=0SCh>r$2Hx^bTleG6lXRgA6+;6V59txag0Xn(P)hfUwfSMy z)|-(XW7oFdHtm-tMc016bkn=SZk`Y#>o~mPUfUYvH315quLHuPi7#W}l5)VWi!NDq z<`8n>ZqIca+Xp~u-fJ4IA+cSTN+9@;5PIX%h`HiPh3PY6eZKc=V;sCsP z3fsSAlK>Og48zOXs z-(lOMP!YH~A5P5p@FlyaTR))eMf~~KuPDPIDcf%eacYoy_J{6|jJX=cRq){qz~A5{ zLk3Nfx)|D0+T&qsvorH}$d_ZZ+asFdqxrlqMV803P?@sm!^k}PW0C>E$K|86Wwavf2w}@czzCP@GUr{<@*LKR~h>hZ2iLR}5B&1}0|Js@qS3LHtVkIx3omw~_-W ziHt(Pw~l&7q6_T`9aVvolT$a|B*ra1D9#QFw#hn*ceJ%BcjQ{f)!1tgWMv#04|%F+ zm07!TehoIHczSew`Q`}STG1^rjw76oM1L;lkd?(cSSM-76s~`%F&)2x0Ka0}O?9*I zi(A14J(=?@C7+lE;XAo-H@r)#E(q0-=30hc1>lt?V{cpn2O~{Ag>iBuk0oMF}$eharkNW@%c;)^xB+!>(;Xf2qw=^f}I4BYS!N z4=d_EAOg9px8p>yPM&f&tMra_g5@%jN2PYJ?og6U`p4sB%GBMlJ>Uab_&p(meR0}A z(e3*{mE+K*fo;Zo;qJ^pFs6#W9veb$8F;?4L!2IfNuNg9m6@`|B+%ow z(XsgXG>4j`YFc)%-B%~oOuz9aV<&6hJ(IF_aI|6qAx@qyU~keSGjjI5WIA;{80ver zgPl(wSKtw5T@>#Z`a@_b+-$DP_ldJV+o>^#gy~o2C;6J{1%-bhu2F(JpVNz8%Fxgm z#9+)NdMsv)tFl@FCZ7rir8pY&EmiT2c+AXKxgM{4zED$XfPO8>A->OT86#L$zH zjUzXoz9?Nkx{{wz8_6PC!EvazFuZY3fcd+sQ5d0=a8gM|sKmF3PcffK${vrXz zPFDoH)|h2uqJpZu7Gmr6M)e}+oU2pE+}$bDO(>Ff02CcYIy3i4E? z5n<$|0bW8rboaAVhkxQB3cD*I_TS?LiA6?Q;uuo+5{YnH+xZ5tA@7w_5A%s$ek2W( z@XFD(|GWc`i#U6w_aR$5``tzNJ&Ns?Lr3wKjyjfJq`z_;`4iLEG#Pe#vyE?PxQD)S z7DAK`cXatGXm&s5Y%AGX@cQLV*#^8WP6dWfM>Y#-pY}GxRDOBY_=`d*axG{S z0_{^@8v>$*zTXjnh5PiXyHr3+(v$L74*g zje?J(rn`Q4t+MhNy!0EJxO}Y_kXckIpcS*nzhLf~-#ClfR%VIMyn_CarE1i}7rRXY zEIom8;dOXK^SGlFHDv3xC3ICW4sG_j8j@VDxd*jtw(mgK>2&X6Sz}skJ2Y(s?F}W@ z%YoOZzrhyQao4XqzaV14BP^}TS3r8pTTpxIE!+F1Vp!Wxy2$US1jxbYK2MN1G2bebWUD^(ARpm(H5&Pb*K-^Z(gq!qsb95$H9lJVQnwnlLgj%P+4rZcqK~ z=Wn$;1PpE7`UDOM^c8x34j&xacU#voZpG2+9(2}uM1~t(g%d7v80`nLDV=wJiNM|F z>Do?0us2NZ=jzdBf18yN#sG4CD+9&&^j^b$G&U&8_{))~V8(PIXuDC z#^3J|eRNCt=_{j{*Xu4xHUgH;^ar9u*RpgeQ_!c6C!VaiXuGixiGWTVizpPk$HBhk z^3=N?I#(r@KeZ*>MnU?dD4y%557{3Uezw{sXH8R%lBzFvFme`Ah&jo8dJ)=i!Fx?5 z)`hO{K-Ar@)7BPrcS5&3U7S(tPhAq93l%bLFxzE{cLmOP*EU11Zl?ElD!lL4;~QQ( zF=9*Y)_PcuB1dKU)q&vpINQG>%WZRb3Da-+aZJSfpO>ng7sH2g2r(XrCFN~3o$3mm z(4_}0iuspuDWDX%WIrmf238J9-qO!$SNVn4uN~gpV<=4J$J{!f-o{lkKALbR44s9N z0ap~bJ-Cv;?`bs-dL7;!g4@m8kALM3@cA-6C5iXJc!nlcMFGBHJ9-mlZr}3l*zWnc zggbz380gyqFybK87rgF~(s!3o$y6Pf@_hZL0#a?b{BU;e?di!wWwswX@G_SXOZ4Y} z@Gv-U@(+gTkx5FJM3U#3Sq6Xd`dym{xybdlNGn!5cZTlk08ivK8ME*w6b&&ARm`hb zE~J@i^4io;JU}yVH1H-+e&z3jjrY7x6pMG-Z*^XjM%b`jn{8Qww&1=?r`nGu#5?9$ zc4Qq|51fp2w#9(sXhGSb*93RdSYKR39n}(k!SAz}pXWGWTv^Sy8=WojyavM=&$bLk zgaXOFN@8-pe;>RSh3Sdp*qNCxR-X`?qVH;N90QFhl7NRW>!BHZY;zVipHr=&ABHR9 z;oM$w`APrAGj2Qa{#_IQmO(o`%ruU40@FHHd2HoR(I4ZMD-}$5AvF4O5Zou-OktVH zh1pJ-zm;W{IpNt&B5S7B#F5m8ld+WKsgKf#x#{$tZ^rmVUrgL}l;0pg7PI zV$vLW8JjAj&U|5r?7gxjtLf0G%>9xv1JS=Z}15Y27^4ji%rH1F@ z|Iv!gLqCEhc0XRwIjqa~bS*8Lqp#DSex`T}Gb{RZ+L^*wwOI(FbIeFT{es&OAeqh) zmxLM9kv-zP?YvG{6?(l*L^}H15m|~cL3Wlk6=?iOH$JE`Ry)ozUH8D});dQtmv8BE z-~sTKing@%-yN7-SV$WY>D4V6y=&4wri`nU{di5?hjo|^Q}EW@LsVieUFdVwy@Jh> zukYG!Rn0;f=vO0?G(O_=)aTGM6z8yelwkGhDtrt37|ZGvu+n1xF7stHI1Zf3#NQk( z7$oI-Up3&hTDty;Fuw$6{ zlDy(RV(z|K#u`7(v$~e!r~3p+8sta<4;7AkOJ}rS%=FuPQ4C}c`PIdQRt8$yP*QX2 zS+S5YOMax5O(@sIR40DvvsWlfytg&JpW5O0i+d7++$-z;FzdOjU1+fEHX8S&)+MSHjRxNEbyi_D zhEk#*U^y-6+;^%MUqCjyn?a+9N(X#$^Yxir;7q)F_xujVGg9&!O>2RNa`|BXqyNh1 zLT>aqB8}gbw8RTAs?HZ$DaJtybHz9*G3HDzE}{*Qf~_77*X!LFmICj47rJ1*{_99{ z_}ua4F_54127{}@!G+q92JHjqP7;U;vaYQ_+4BIO)8or_#%fUTaaji~P(7=I;kbr* zD2vWiouJLA0>-vx=)FQj7HK?`2GQ72Z<=G zc6(t>ypy#2Y}pA`xwPmf7r25*r1@r43PnME)OL^oGxunZZO5Lfw9XW>Ah zY2gR^jV0rlHb?Vk0;3ZdWTPSW<&k}|)aivvJ^}j-Q8#9vp^-EZ-W&YW~iByS9}f1jV_O;Y3y`aP&oz3%gfGqbRxE(&XIs~J_?FP4uB@eXX6Nu{cA zwC6P@q32C;HK|TzeB4TJ4eeG6LVgW7{!T2#$drzW|9JBNt)t9%gFX|xozVsj^FiN6J z(!-*$*yc^2NoWYjvnHX=ZmV}^|2@`gKn7tO)#<-p3E071nl{Ffeu*VeQ0Ma;-^}!5 zz2Lcg4o*7Nk$T@)6imdJjf^L%)p2KL9~;jEfR+-J6>mG($=?o>M(A~xO{u|7MPiY+ zo+`+0`Kvc&Q#8=%x;bA!L74-OkJ-ams3L#8bgd$4rXVCo?f`LU8{xe~+!j{m@}rOy zE-aKnce6HV+6)$WOJyQ{PvpU{T?AGyhx#LWV>M#Ig1n-?Zv=%UvDOyLue#M}@ilJ& z-YXx$ar>l6GOFN_6g+P~!K;+0ybR5LH-3*f_xIk2=U;;nfhU56c8Wal)%S(L6`M6H zoO)7t(IlUE?MyR`JKp3bbX6E2zSZ*Tci{ z_0m*?>Md7;9)dFIU4kWZKyHSAitq@Dlg%%eFdo>eSuP@%?qYkn-Z!I41y`bjbr&%hy&_ zk^W}-jo;+)h&_9~l{y5eo-Zlt`gnh*f0n+7nA2hpuHgyq7YNhb+|&9cd&x zw_RC-fu5{7VH!iYj+bh6eYaL<;_5$3EPbhC23ivBo;u%HJs{Xy5nl{wPMnnnfD7B22i?_+CkOsKVl=vo3Ft<(@~yn}45fj!R$C zUrsYkc-?HXBlv&1cl}c>c{ZPuqu+#}de}G&@p<6#O+8P(TA!%N1s3BoANMjBQFi`Q zeb~A@s+Urt#vs_-&5dp8=i`X;ub34{@W6$EI|44_*`pU9*1qqQZEs|i^AJu-!XOZ^k>!!S>8fjh>J}nE3!u+pl$hGE=k>5b^fHtBv zwsO*Bt`aa+_a%YA%w`DvuC!-UjL0qk&WujjOz#23e#mU0IU{xMeA$7*?C6d0C7V)( zyu}xfW=Cix%8p3Bdc>a!?$#hj_VjlX2f@2!6Ku>Vr^T-{b&nxQf@YU?Q^{nN|Xts;Oc$En_bRr@3g5IVZMU4R?=y#D<9{RA6c~Xe=E?j z4r5RAza+_3{3^l!DKS?@4jx-03%_A)Q+5N?#wfC%7PrYozxrQO_zUk>NfkL@<+S ztMgzpXgb2-dhp|LUQSpZ7G*8}De&Q4{zX5SYw8>4ZN++vj+|z+6%OX5u)~*93Ir24 zE*j9Nfx6Y4v^XCttk%0lS8x>6o2l++dj0b7v$*76K8F!CR@y-5W^D#4=SAIh6caesx%RHSgFJ>Z|g8`#ETB3%+2^!Q?+C zc;?q5BT;A`y5bJJdoH_=Y~~WEE|`emjE zb@A7~@cDaFw_gb``Pp%W>v2UX5OB3QGVA-3#EHi-m&h)%i}G$xemC;!6Kt9Dvr4{% zZ&>0P5I5voT0?^`WmGnZCE>pQn1C)c5mk+0{vkjm$?6T}D2q@1{fNF1i-pp$BEw8i zeX{6Rxej-Y3(R~XtZ=dwMe5tym4m(dcXnwr*B2`Pd}x;et)0KRZR8wyxJy6Trz77! zSnkr?j|qNiN3zwrRxNDjllpXYCv0WdS4hf7R$UFrh8`~nq9zAFho6g?Jk-WqH|^Iz z-gVV+dJ=mvCfhyA%h&IQjGT+k69cJN-VZ4Yw0UT$K4rB5-5BWkE}(rWmGhb5ILf9&!;RCcXkb z-(;}vkgNElmx)}WC@F{dQjC!D&Mf7Rb;dA_B+1Ctcj~o9r=WSsFtL~A9$slz7^a)5 zudgEKNVjdyUDHq^q|zgj;~Op4z6HkE_sE#2EB|u9Gvbi`6$+RuLY=a*r&jdLOqL*1 zno4ySS^d^ikE=`WG87X3$`gdgNcV{_no_4rXJb3fL|A^l>TTg_^#Sg8sQK?5u9@91 z8VaXs6UX7%aH~`(m1=R%$m_jID5Pk{_pvLwFb;O>yG(bZU7zO!6E=pl+n=MTrGMi& zbfy3cqnU3O=?*Df`b@B^@BiVLl}M6IP#o0|%W$65X!EDeCnfT2o${TkwEsrFE5J@k zz=+i}<5tQcJGyC_u<;XVa+Dvj_T5^VhHAezN$TeFpLy3D>@09PmP!h9mwnF6l?4a# zFIEml-z!Xz!nL2DxEZWT(KjwGs2C2Ww)_EN8z71%_*Rs#BEXVGhYnLJ;C0ik+?%Z0 zAN$eh@9eNJaEc<1WDM@qwT(@K_loO{k8#vf7NhFC)AMypeMCqrgc(lr-Apc?^)B~z z2HRUI>Jk$1=I@i;e^FbMi6AB}wUvh$!kHv z$i2u>GaX*tT{=uSaaw=ApIEo;44F%|JQSKFD56!n2bh-PyOlkN)Rt1B3YE)7N4pX_ zw*K|#QLvf_mrEQC;1=Rd0|M*$Prrs)h=8w z%;PZ7aO=kL7wj48SahxeG&#+!w4_h&s5|$>kG2-PC#lQ~>d=>z87eLxpvExYF_rg@ z#+ixBB!3J4A-Rml`;za*iph}~o{&Glk=b;>uUPf;tR-S~D zH9L7ZGDr7vTq_huvMy(b6f$BVF17$hYxCnxEN}aF`7)bm1^x>lgzs(%tIW!5fg6hz zyn``FCZ;c78^#jx8U$;vg)CDMt%g=ZV(pjV+BicR*$FFrMe=U-Q_5rSp?WcTt8$oJ z4js*<0_1Jxew4Q&+tMZzZ^(tzmFJ;-=;Lc>|8BJC6CrqZOq_k}iiA5A@RLDNEUAT= z+v#$=gp%h`8KXcW&s(X#CDrQ8HpCrajNTVc^ccyxD=^;>PB%sBy`TF#NY)jaG@P7o z%)A6EbA47G>vOXce|vWPAK>1}udcAV!txB ziyQmdfQu3i0r45vR(x3x$33J{hhw-}tYYz5nY5D*a= zi0FmKI^cp$w3>zN!KCG+#^5aO0gv<4u{q2lWosTzA}8D;B}2H^zw%HQCLXrZcVG$( z&(0m?)LZMF2V!34J#V?H(6QS63!G-(XF(a1Q8korW3YhVL9hpud<>wKOu2Uo3h`6# z5JExQ-Dh0mMkuFRkZfJD#ubJQDk)j<{&KWYYB}i0Itv3w2-nPmt+Jrd=yM3Q3%ZJb-FrdM`k~GFNq=#~t0H zvkSXPXf%7d_wIh>2KZK{8GlI~WjurUh93p;W?(YUYgzpzt5vmrYII(`+)A4*iRoRt zrkO0aoGg`c0gR?6r7*^4E*g+-I1O09g|a7K$M>e7@foc*WlNB}E2_^k@o|(g_Lu0& z?DWLe-BU33MRu7^W`y6Vymk^n*EtP~XZ+6Y+)e8o3b(PGPdqse)k3^!a&Q zEi^l=e2UTDwZo*FMxG}KW9u?D5qb#)fl%gFJW9sZxHWM64a(mo`I?D&T*_w0XtcpO zB1xw49G!@vsi31JL#KHf4Bvx>Lx; zOO&B0^Wu=!V<(4wj98K5??`Af$ue`-(m%u;Ka7+q6eJV~=>hvi@5dWQ9M2(R@zk-L zieW$D%OW33lxZikdAJ37f0w54PHI(gpXuG|uaJ(2SSVI;~_DwDV0z8Q{Qc z4A?7Xu6$)usesgZcvR>xYNUKZjfD3pd2!^7RhQza;9xW2%+S~xI{}rhyw@77`3K_7 z8c-#McNrStQD1&3mNGZO#uLsAd`lTFPWzf?o)q37B}UBfW4TtEA(IYxb&b#cU(jz0 zvf-_Z{c#+ghrOtQ2wK}%UfXv1>Nxf#T_3QaEA5pQ0+8lzo8kW1%qr=& zs!rP#nt|2hU=*~+NU#Lg`bUfJcTrxf%M3n!aYq!iIJcopE{`eHT;#ozrRPi(RmV6f zp-ZBrC~EL?s<0GHjR-GwzlY}>N%@z_B11|q0B@{Jk{Q6Y%HEif_Lah!XWp(Y##sKL z*ZJ!o<$YQLej%VD$xh077VMASUivLzB5(L+_(F__Cp}(yvIxc`t08O7_w?q40UtBC zBW&W&`!m^Re}|7 z*Z284!9NF!{iIM<#sa!&aaz7tRQqyPPCgULeL6$u&_CG{E0&Vgw2KWh3xEBaNe=g3 zdRv^f`6EvQCnA`A{U2cEivZy4Q2FkcS9|{>0h!!cDbKDG=j6^Vw`hzS`sSBiWa=*- z&81y@`pOE++duMzA`mtEKQz;Yr~EF#9%g2p zge9+f>+b4T2p0(qBgB9<5<6OShz9=j8?&E*OQa_d(u79hA@L@HwAGLmRJPO<{sGpQR6ePwE zXDLj&403v%ImTwY)(T^$--9$Rv5TBSkss!GLm(>nWX*P=oe+OOI) zRk? z4ou)E;Fs5{@^|fJ(kAVz^D8UDZR80%G?KS@ns#Y%9cXWqlvHSYoVbOwOgwoX*-e$= zFfdw|4^u)!k+!%RW=W^(BSRP=RFa+$^yT;c!ZON4tblQu%G6dNomPA!g{JzXD9oH; z|6SIIcwJQ=_39=Zfwp1?H@F8()H+C*YZY-8Tf#=18zLR)Bd#`y1TR6IgmHjg?V@IR z>TfA#%^9rA-I4mJgZNasV5p6Frg!#7^~LI3g$J;jW9Hv+}gl zGpNsk&ki`9lQ1t|Nnu9T35;214IOu+kAg`TZPtGV&d!{+=92xZ>*@2FRaNxInliB% zKvs^`a=YsJ^rUI=?ks%=jjLZG(U6N-Sd*MIfsIw?ykewwJdej!+~uY})u}Pho&s82 zse#Zwq|eNbqJze|U921qIdZwTdc->8{hR=ldNWJ{r~X3E!dRq0ChxS8o9v|F>W*N9 zUhDB?V1vv`v4h!roIbs8qxEW>jXZNFm+TbzR5xarC6l@!f787~$#F2m%XNtq`2ZFO zFe$44Kz;8%4Xa3>-NP;iz1j5O*WOhqf@;Xn9hbQ2dyPVlo48_yPsCBV&R4Pro6P)X@U*C6cG<2q8 zH@2Bqlj)thtrjZmuz9ANXQ?IvlUOxL{}cHZy(iPKh>rjCdYg{aYwSW{?idp8|;gN5Gl&Xj^iq zW$>dHOi0wv|E?P{>$%Z-1_mN4&YE>@-p>k{oeCu1EYlZ2UUbYLs3(wB$AeFfg&V}v zkk1d4x2;pfO0YFnaYb67Z4%XaS7xLVVwP!W9uvb<+d!So{ zWV~}9eVPTwS$i&Wax>*2K7Mc48{DL~u79>eu zTAKo9;|ElDTu!jx<-C%*zU->sv1v3KiKGvj$gU$b>LzEn^r`kZhs!Lh0^yD8yH)y0 zR0Go8fqswXuOxTEg~a+b6~fCQ+OngGlvK?Vy${lUItrSwW$*i?Nd!2yyN*-O;aljS z3=A3rtU7G}g$K?BpL1M?Zd#vS$bqZHwwyV@Fc{gpCbyvOvzav3e7uY14z#s8LiuEg zAEIsYKHC$Z=rsp+2_PaM*(cPynbjOt!6v7!dH4Vho7o{ zqa^r_p7Ce6PZG5WUnl&huy$?j+7w7E)2tEXjPOw_er8q7)ERUhTEP2e?Q6?Ty}+0L z#2RI3GN(FG@v%4b$cCQlS0V%W(@&wt|1UjQVoF`0$*JQg>l4mOp)6L2=vc2)(% zq;Imj&ATEmzLl5Bjgh5|oyn(Q-@#DAjqJkj{vGOLOq3G@{WMO~WaNL=Qqvgur5uch zP1jX-nE5MOLUdg8cE6*j1T-8ByA!}Q=hvk@MGbDw#Nx%MXA+X{P!=0DpJ+_YJ8BYb z-~=SPHqG+nV+3&6Gny>kzIc)a+#v`#7`7XTP(m@?%UBFOrB;#6X6drUh@i<>2nr0K zMB0dC%PVY{9Zj>bZvA%oT3!N;FYleANyZn7f~f-?xfH?~Tiy&y0RyRiub>9SfaSMV z2g3_@%gWLUp;l|X1-x4tQaf^=&@wmXhyj=CEfqNJYP$vrmnNRgG9G>iUY5-8HwA>93v;J#$s+r(GnR`Bk3>u(U(H!ilk>%h1K z%IgI-Cr*brcPlAc)-}6l*XJvheb~zmOpwua<@#|7&BAz|Vy0|pUcp&TJNtq|m?A?opv}-L9z`iQX7!n*txCttcm?Zc8$NgGs*rRn_N=3`wn9%)1qtyhs@ADSR!nL}xyy@8m@K`6xH328H!^el1ubrx*tsg=sEQzmlAnTHz2pB} z2|TmIvEuR$9VT4RKQ(a*L>+9f852D$EbQQ~Al_WH0nO!+x^{;LABT3MS6u!sz`*r} z!rqlX4c+jrLOKYLcI<40rJlFJjykU4szUxl>GNXgPm(|Z*g-GA_ zhm|Q3fV6_9^hS*lwa6RE1ar(hz{E{UofW!H#Y{{-{KP~oMPGyRN}hDW*TsHc6NJ&A zdh&4OW)|Y#`tBv)YS&mLS~7e+f4llBMzvw02qE<++Ox30m)H*LX}7sRB!xxvS}sCb zn(brWY&+zqsc>mBP36*0ZfG^?m8m{6uionegHdf4z}&Hk+AB-$N9d(TUwfjlE#_%) z?Z8Se`C~p;tKQ0EA6L!P=&W+$<(RF{B+-__bCDqZD_2N#HA9n@NYrv16~?dk-uO17 zEePJt{%5-9Ho~8F-(I1| zF&2#SlX>pMgI@glPR^fm$5(z%=Y@C*iD?OSTW_PH1Y=?Ch#Ay`cMY*%bCca@;`te! zI`}+V0mtoI^WEyqTa#JZn$DQ{6#;8NW1zAk@=;UV-U;4>*nB14CyY7hqU*|Y^(6~w zagRUpfV!89xC5V3hE9kMEag6JmvJ4u#o-cQub;-|K1M%}9)LOdx5^ynX(Z5qyb*U} z-1?rSnC*Vy`_;S}v}LCYYq8(4uVi{Yd>oy3kFdld~6HIFVI8lJzdG<5! zIFGgG6+&(^*0ATYkbW~Oy9DtgO88mM*Voz|ZnTfx{NXA4ZM*G71LwRWV482JW#Nn< z>;(_qOlGoXPqJN`9{rU5=p_sw)2es3^8SU(u`Q{z3)jxygLr+=`l-V{K}J4Rp>a6w zXG34cMxiJy5G04L^V#%zA}ZlWRTkKmeNCyraR-r{SI)s}3<-vP?E{+EIPP$2mw@P` z3~MHT4IM5;OyrXQFLV=>%uVHzKO_Q)7rp%6=Tv*QA6g)4*~-_SpZI}3Z1l#w zXoA8z0}7#lmBpok-Wgzp2Kh($m+vTM1_O57BRiU4+KIpS{01sTcLdNrD#2!+Z~~s( zVq2#2;jGi$p6j079`vt2ymnAmq;_8P{Y`kNZd?ra8w%wVn0K9U(dV5?zIK$&Qber48 z$%Vr0$NXHimKZRd?0OZs7kiY}jzvr8&E(89ngMn%)_J`_bL?=+zvIeiP}H-Z#Ty*= zQWdzo_1xNbc(xp1t{H!IMjuqiWBfv6hRM)FW`d*V;rfdabZt+X`VD5D~?`HZw4%!HPq#>L)S$YyS=yJ&Aag6-C5 zR-KP#DFf}>`}qY>@V|rx6{Gow>eGeL5dfuKH>I3v`<1V#OQ!UV?0?3GlaZ?7qNdt4 z!Fb@nzQ*C{k*Cd*-`TFY*e0LEVJS4P)79NIk^|BQr8w#2W87hd!i!!v4aso=^#jlikyRm1`w0_dOhw?od&bfVuW-`va$i zjc5B#ExHRfjk@jl_t#JE!aLva%XZ}`T&V#aW@>$3)^Jdg4SgI(hdoA+KgkuMsWnb% z5TZUu;{H)+w$pWHL5ra52*X~e-P_PYd&AThH}(&C&4kwa;qUiS65NNYYVf*5-5o_{ zOwCQ+*xa~(T#?IU&Y$2jW#i~LrqtHg;CQ?c+oZ_HEUB|jo5Ai}swg;wvCXM9=JfDnZhwcD*!EgMs=6>mDyrHoBN{1?z7N`UH0VdHnAKX=Q zfhg@52)lQ*E((C-3&W}ODKnuvOqY`Z}K&ia0byCO`>w7GpbnZPs8nUp!<(Uv?@ZxL~ z3YP{1c4D+yn5f}YdcycrIgkEifc%Swu*5U}jzc*3ZQTrW*w(fk?yt|LP2<&p+H%r~ z6T`laf*0 zJ1h|Ouc_s<3)@tt3uw(gl67p%#NJW7gnAhuM8C3o?omxWFghF#RkqfckHEFi;%blj z7L3B@R}FoVmECzKiG7-xz|R~j{{$M%t_9yrKZeitR`y=PrTSBTa7mKp$=Jq!cMH#I zU4QQ>eEANVWgD%F1*~(A_60f)(^=c`oNe3vRpGaa+{8QBOf)rT9`_9iUlnsE9RDn9KP|uCZa8L0I4@K%I6Qut;Ow<}PNpC+oO( zYi3CGtI*01?%}=dPd5}t*Q=wxfGSBRgL}e09nH4f>zAorm(IOriCmnOZ+f8;aC&RF#scN^N?%pWM^Yx5 zHrvLNHC(*`oT7H$v_$l#y%g>Z9+ted7{09!jlICZX9G^p09qO|&?l#-eIikVkm>=+Pr`Rm#gGdl)GHW)+iD;Z}3K{WZQ0_YrV7K#gCQ z`F~x_xFS~tU*uL9fZJDDptU1{esPP56!Met$-tSsz&6X*y|d*`Jd(j{L3@a20T=9t z3{**%gO;JI_q(sWvztaTTFC}{#%I?m>6*AAv03V{Q@G)4V*1=TsNB8dT%lktmkZb* z>o~)vGWo2zbocAfW8*>d_smy{w$9W+qnAe{vjBGUZRTF55-qT~tPwwaBc3MK7%8N! zNDC9FTAkOlbkDbU%vVj&L(1X@y_>LaaAW5A87C-Pce@&p?&W-JD)}W=zY%*nqqiuF zYW-Jgv;+%|+A3WN<=a=J775w~Tnc^X=xTXEgc-DTQ_2 zF`cF76pnF)z3;&MiMz9eNO~tLMefnhad5Q8=uOtdhKHN>TDn0qNsG3D`A7>!+8AT6 ztWk|jC5;#Sqn@wpHhRO8jxiqefwzUq)FEfG5f*Z7t^y1*aZ7mt=}@{dIn<2Su$ZBB~}~xbzQH% z!G%rsG?wG;92kv3Nq3fkkRy>vahN^=wnu7fjJQ!obg?Ay< z4yq<580_!y^rJoHCpJi}l0WB%VjZD3X3L$v^6yY~wARQ?sTUE}>Z4nuhh>ER7(r!F z(^5x+6`u0_iQnHvbn{JCHgThNvmEaHE#rMO#`?+IW!L5~00KQLNL+00Eh+aDW3J?F zU@7Q_3*>+L6iE!F91MK7s}l$A^bpSKhq`t(no!*NJ8}$EGhneN+)jaPk(n<#pxba&on!1K?Rf5)@6=7yi*GJE;*mUKAboQOVuuI6me zngBDm%QNnv6}#NOv`sQ65XnPhNBM4~?hfx1>YEPCQNGc}rpf;pHs5eRKsCRR6Gozi z>{&$H3%=~|$chpxRHo3fvNFgofB%?;gh#uj^<9SdK>d#RqJ#IizPbc&nZuCV7|_X& z&l!B-mhbo-YcBAcLf7mEOB(%PDsT0``yaI+VI+2f%`_ z@KNBU%i1oz&{ciW_+e=W+T!dS=Nnc_@~2(eHo!tt{Yq3vx%32D7f<48{KTt>GaJAD zb=_FX3-gVY)T1ZiP?t3_!P5psqG;lmdONv(pYhmpwB?&8Rl&1`NLn!t*mpW3xG=@dN+x|ncqjd7P*J;h_l7oq3bwlV{B!N zc|zeX0@#z0TTRJJ6ZDI6RlWS47x)2A^o!7G9sG#M4BQ79QMT z$=Zv>$;UR-vhhI>$mV(58`V7^u{hDG+H82xZdKC+ADQuj7H+^XxPk1@BF%kxG%=ZLn6&~dgCn4QazXGLE5Ggo{ zRB5!cDNPx`t)RT4-u!4nWTG*2LZQo_&fa_W+-5|+%UY7t z9n>%;?n2=2mKBK=v~DqDU+{qqMfk4GIW}87Gpho^ANTUJaR)zbOU-{{3NiepvUm%S ztZx14;VdFPfXlD@jBYrZdq^mQi2%)O9`26vVztOOPgvD|rnn+kY{^?Iboo&f!UwGk zleq0!b~>~ItQ+W{76LB=EEpa2t3KMDhP9uj-R(jq^9-D}_p$I1N~Bs;JYt1PJh zb@+?9nzca(qw1&81d&1gQ+D^w%~+k6(br5ndXD|xA#LedVXvwTZPG9&_5B#rvIW>z z1V8`6_+k}<;%C4f1(?Js3o<4O^lC6z)K{zyyM&4Xmxt0zzvFWz>f@Y7s~T(f!g5-5 zmFlw5#=H64(lbbKsZgdNSO}=q8x|z6X-&&YG7DLZyAPUg_-%5B|A4}SX9WW{%BP#j zBNNKJa#bn4UC!F!T!g83#n_vUV@HFtNfI2Jf5#>cy2~iTG(q8b)(6V=Sey++Xjo2suDFjEc@?b4@1}58rEi42(+uvB<42RjYN@TdbCTcF1=I4H5DwY9koeY&A z8R{A3ucXmg19hLX@YpTL*xwk=-wK-IRW-yfxypo@zAY`@)3HJkCR$_l*PE+kES7uwwtrG)PGb{9WMT#3bMV5bT2@12ggUYRY zRos3?G=uZwKUw`k{nMn4k3<&$)t#y$MtNFKMA*gMOs4XroM}$7l=|P0-a+@A_vo3Ey zn?Wdi(;Vd>%HSe-{0?(ur?VXzcvop z{+h@`-d#K+IH}x!W@_aX2p!>T&Mt5r#CI9cQ%GB^O8(*{b1f|&8DXAua>XI(Aonj~ zb5iL!$@QtrnaB4@c8v?NT9N#WwtR)`e%WpQuMwmm_yqi2aryKHwO``ADh3)Nlbq}t z#xvw4Fs6dpocnD!^l+epZi07iFHuQ1c`E-esZAj1x_Rw$*cDI`)nrz8_X7{lyX^z+ zL<-ZSZvv|dFUffa3;JjtbeWq!VQx@^xUZq0rCw_(iIF#}Z0l`l$ea(Q(1)Gg`&nw4 zplfg>RV?tg-=v+f?>|gWnq`+XrkY!06d_GNg`S6UwMC&HDb3f~PJ!4u1VZ&90>p(G@&j%_c&i9sZ=wBz_ zHpTpw*{Pm>*wYckCgbA2DJu3{)^!*R*vsF1y9kim)t&R?F|kq9Iu8iwx9HvY#Wh)| z6_r&+hvParrby+_mb|BwTCDkyx6ZkD+crKP>w?E>C=Iu3{crs&0|))yT}Z5FgdlxW zV4w!^Ez;Ms)a<40fr)4a6HsL}nV!CGl3mY;T9H4NO|8z7yyu+c3D{^dG%0Mg#vYEn z<`FQxpDv^&(O3`n&G2_Lap;of73k-iwo z7jKJWu~x~7Np!cqQSoPNAeni!@BIjE2!OB6_K|x2muUae;TUal7*PFqK4~( z$zJR{UwrY}T59WG*={Na%HfnSZ6MwHSm_7X9X@+8)|wdmIoKYdpgO>3+{T8l-uxuU zSn%UFOB*8jo-6Rxv24s>(Z`WRo6FYS)dVFP#4txqymuF>wq(!OlBI&@FVhhlE}=tD zuVpG_pN$y(74AU6&#$Hzde`N1VfWrJJ=%}UFIyuZuf>RhWs9hh*Y)z)8c=P@Ho@v5 zle+wH^7yuKBY$U@q~JK-^VscM+25osIP<;fbV#U{XJqA}KuNx=8ed?2}6I(DGuHB|LsBM3~l z+!z4q-;S>0n5mO*L^r!d1r|QgvN`vhpe%xXtk9QWoN?iAp=Z(Y7ix}q$R-tS&_eW?STr#{20C3S)EO@xPDH7}~(;t6fQ*6@qE0NvH-M{&9!wv+F|JxoSph zb&l+!aiaMIxPN}!`u^Xvw;Pql3^76MNIR19lVZn5A1HjC>#rjq*m~BRBpl5qYEjnM zq(PvrwOsHaV-F2m?^9e^>xy3O8yZghMq7u7w_cE&;ME-|-5Si3EokTpBW07n*g(fC zy|4)xGV7iRIGwoSbnc>eqdKa{`Z&CyC#?Q@@93w1RPf=7mli%<^RL03sCf$wj~Ql3 zT{Z!J?bo4UMtNSqGtj<4_k%8nB1T){s@3rlGiRi;#m%BQd23(!%6x4HgG$tvdLEf? z_?fB<`>c176oSiwQJ2-CEXEBLUMpn92f^9pi}mPaj}U>6T!UjJT3n!q^hDO@iL+xh z^51nNPN^L@ayGNM8lTvB+J3jsUymiysp`b_rJ}^KQD%aHw(&C?-^R4#Dz)v^4r&R; zGb6&4y~bQy!2Ve;3hVaen&ic_%hVq-I2BM;`tcO?^d{q`)z+5iBmd>XHBXchyc75o z-^P|h39k-hm7Ac4ze&QtKHqV+xRE`ULMx;ylWyZ}dRm&*&~Yut?LRhWui z#o4B2A)V$=pZ!ccRdqJQ{otbcJy4nMwwQ6r450tOGRgs!DN6uP$aB_LJI{8WNES4$ zS?iijFflX0@D=?jIs&m74;( z_{{!nBBO&L;5qK3&+6NAqQA3#OSgQ3-SJ{&UxvP5yQzGDMHeQXjydG@ZJ})1|fSS`EWHlgRIv2oZYMnx&miqlcyq zuXEaG;gno`a7(7ov13~M=uoSxK!v?ZM)cJdt?|Yi&Izho8vUAq`OQWiQ&u=CBM zd5YbSk@ev-h+ftIl-EKDD*rIFdR|; zBVFc2e$}m@<@%V}In@o&ea^QVK zH1Jt=n0g1oiCCm9+Eb7%n*uE7+C!k0PURAnXWuE#xnLpr40ufCv-Uyf6GPJXsFQ8T z(zIZoZ&caIGPVzW)u4r&x9BOexb549I#kvqI8v^)uGaD?45BZ|@J6sWI%X5sZ0d3m zT7q%gvy5Ru6ZlU>&czkFC2l#lGr}Zkmt&eB_n@>)6nED_rkZ{&I>7wMKmOFhFCrmT zi<&E=hb{Ue36+ds5-MJu<$12yOSZ)XPjU&vN#_3&+E#N+XPM}kmi;<8R_1SH+kcuZ zKP^Wh&Hu42aZPdzJ!l~?hxGFgS&HZcXxujyIXL$I2U&f?hZ{?_1E3M;nd#i)Y@~7ajt#` z=Q06}GoUim^oIbT)^VPafPi0KrMq0YYpYT=5LXBRv%g3zsm(3<>aiiM zv(^3Asl&_2Tk3KDKN>1&HX2L`-SY7^Ixd`1L&ZFb#xjX9;-SV^BzNb&DPm1Rn;R&y zkQlJI7E8|_b@22?ktM-N#wI`9FfPEN)zuIvB)7>LM9aB`hFVf-b)qz*i3zWk$I$Pb zJ=YAMZrf;ift z@kI|$bY_3*dO)#M&}y-aMlnrJ=t!>ZTve?U1~OYI4aps^sFhgm6yKQ@o_)bPp-cIz zT{iuP^8I=9aTO14^{rktiI{=ES?+HFTSARuo$dl~Mun3527M)VD|4d5lv)+onQV14 zZ1UY*^|iG=y+ zYX;i3fan|gz{Or4d?{!zlC!>L9yYbU-UZ-Iw-P=R>&!H$@S*o3yKB>0!8Alsn*G}$ zn|mHc3aq*6ay`bf^ogm1Caq@^wgtWAoD1${8(nurZQ4&5bQ5`q} zlVk>FP4?V6qgWbuW12sdpPL)f49%Ja@~u%c-)Xv+O^W^?w3A40>xr7AknefXqc^)E zqrNQKGL#a@;dk~#r9(Xz!aM=~xckK4kK-t4g+bm)W)^L6cxmgMUJbfO%fse~f$%WH z9A26$iGit4mi@;;(uIiHCteE!kOeBVkgsfY^SJknW312&dAPgiK_XGczn6z7l_xCw z+ob=__6dqn7CIx%rgFY2+{u?s+4He!P;zUL_{wN_&F|^RyDtVm*^V8w9oTx*Hl8S1 z_g~34^%j&B#EmTzc+L>+Ml)Z@UxtoPx-@IwP$XG^jbxp?znQ0B^Kr;>bpyf*#fY@; zxmA0^qG$B6^t&m`=#aGIt_gz-xy37uak!9+=kO0nGl_gY?+w@ayyqB8C#>A)C0wL# z4j}&5bd+%s#y7{di|W*|VZoce$-q>OFB{*ddYR$O-*^i0SE(ku+rB(ndO9#{*y72V zoZq5YLx>vZd!_&Q;fP)~Yz*8a4RKGlrPEk^i_y7M(-znCa5cZhxVFyDJb+YdSUTds zA!wOomzXp>eQkQ`Dm&aMx^cW3gYnpTEy*-~IOjc2o%{S>=e6_P9wL72KUjJ@b;?a~ z!pTiZ?b%|NV*kA7Su2JY$bWF3e!&T4Ce8Y9QyyCC#&CQOcQ!w-*a`>?L!I)Q@|jpQ zB>aTA^-zF)qYaW5$-b#=X6z_-vrL*W@WyxO`bRskmL069Z10}&sm`Gs@m`dS(J1FX zSY^;GX<*K)JugMr|Bn81&{KVr4eAYi_eo$F^w{bZ#*Ub8m(2(sI}sl&FfC5%%6Qx^ z@#u~dY3s6$42ex?)d4z4IBnhS^U9j4O|7!I>{ z94~*^1Iqt%y7Oy$4ir?Md76QFd9j{jJSB!1zkO^x_yk$%XBxk}zIO8raSR`Ru3rAa zt)uB7?wxRZOqJ^MgNd5l zD>DUNE315<4>OVMBpWt@U6rh~?;n;}-fxEZ68asZg4)kbwtMPL6s!)JXiIl+Hrw8H zg>t};NaWIV0tL~UI;b24>v?2Krt6lc5KeZJsvx_ieRLn_Jy7A4>A_{EHNr_ zh;38BC5}{IE(nMZSheBT`(sW!Z3mimzhMVCk`ysh#|RwxIjw`;^%iv}dMZr&`gN~b z1E!D4TCr>>wEps}y@Vi*d$}*wYrh56w@v`0{vWUUk7iGVA2YlU!U%9oqGH#A1~M^D z8vj}OIa$7(lD?2e_dL4PoITb??Dt3-cJM!mwdBLnvMoN|?WV0Ew>7xQ+PKTK5YeS; zP3^fH*$uk=7QCS6G)CY(rb@w^$Q1lQ=;>^GrsXxD-&m|JpvfK?HUE8WgZ4IEM;Z-S zN=YC8+kp{{CZIjG04LO;PJoRCG1n5FCEZ+`yD9aCt)fI6uWJMC@EffAe;W$#c^Ys? zy>Ygw(r^ZuB6YN3ev~^pXtOa@STA28AH(Otl^@E8i&?ead><8D*a4ICDZ737 zR+d$QxhL;s^k(+;NL5q|3P<@UoxEQvaC6<9t0+2DXe8-UtC4*?3mMO?Iyfeq>@F~| zeoj%$Q!^&7K&&H7L;t&eNfIE`NOCL6+rgWKiJupWL}m#h;&)ayve$?d8##acH4dX#o)vfF0*{lseG_@+T`611*XPpm3xk}W;q3n5?TSH zrfb&F1s+{(l9F31X=l-=0e|X@&VbUklpK#TF9#la`F}8=g`}-kegMkc@QfI3C*BlO zJxREmpRh3}zB`Eprxp@GU~Kiyo<$bN!D%s0Y!cE&;vjtl@fExxNpl<3B@lANu$2(2t9!r6|y>JufMqOxU4A$3WH_rG$x9Egc@Fr08DH%Jh5tlhHCB^oR^-dI#Ycoe?M-s8OvF}Ug8KRAO= z;&_uhLOfCy08|!#u1(LPeQVp8&7A_5!i4jDqccs!S`Szrsw)w;00z$Xi}($^?=_R^ zd)t&BHg+c|nYM{uwm#u&p8s4uu9jc+#acE~8Hv~3X2)Td67h!hoW2V_pAPqPT6KYn zY1?0|YO9J1$+##?DV~b|)T(iENKKoG5ETkyf)2Ngf#FPj2)X_P#BIgd&H~5m)?>W% z#{7sbo7`1-ML4^ExYppU6W<)~n`yZ0X16%vN@ckm`qLFU&N{p+j|6|DX1_@FPQxM2MT+oW_!3d3z`!U24mqtVMnD4-$uUG3qK``LEC!%O`&f`PEkIeQVLL*Z1$_+iyy1V_?jx~Wm^?*fJy%!Yn*`wg!&yaPU z-qpxbDDfO0(uzse{R=jWl(zdbVYWIoB^s-ltK!8R@&_0mbbPhS?zNhYc z)^*+e0Q|a7#)f%q2X}fx+r%vca^@R26H z&Jt5tVTIBmw_Gb-=z2Yupn+nsP;rbs@~i(K8Tn-JX8E?toPQ2#(Xg?;<5W+|fJq(( z^8rGq-Bpj2I0Xemf2|CFJK$P5)SsOErZTBDN|jf~l{dpeqg>$`pQBc-i7v}*Vfz@m_gwf7s z*Iz#bgVy3yxbT_(YjE=7r3ruYkTHz*gj5_wKigTey9^u08n)twAmwlEEt7K|aY;1s zBeg{;@wzM=*@DDyE3Ag|y>y=1pKv~$4VWS_O9EA;NN2AVaBjc)@3LA$2vVx{I~WVN z$-lOBTKH5pg0O39%CcQ@m>JZ}_1%6-J<<>L_~>FFJpmTdV<`#LG2@b6QS*n*#if7a zZXt&LtUZ#kQ=qY78!Fv~kyqtfiD>;N zt6XF?r3dhUds^70*Yl8`@|tkrukYfAKUCypDffciI-LW9%q`E?Fl!V|d~B4UKVGUL>aF*_clDtta0^@kb5ls?!?Z-dak((-<2mm(y#HZu zlEG=V#mIfx`tXo{S6G!V@dvd(a!!d(Z|G8fa0?tPEu}d27Z*zXvJO?CP43~{DVd!! zG)|YYMNEjpauQ`coSv(*_0|cn3xm)P*0J<#XAF}ggK}5a8(p`|pTv$ly4YVKHxbzL zt^yLJx2Q<+s*a|IeT&~CE}wscG~w^4mQD@*Ev2+2g5iGKR~qTuoZq+qc3RYL8m~{l z;Ai(*W>m80`QooW&R!S*Rtou&gW*~a=rvfaeT6lf=^4QNzl=0|+86xlj_OUxZLvDt zp0;&eup9prXixVcrIvAX*AtEs_=1b4GhA(+!gNlns-OqKW<-IY;W2BX1Tx@o3|VPe z84Lfcx(s_d5yK+Tq`~*HP5pM6!Ib40x3b-ACtbv@?aekX_#ILM;!9VALGu`jZEcd^5f(aeVna8PA<@8DMKTdZF%Lf`6_5 z)Zn4zwYeMavT&}rFb!X^0GieT@i3K~)74J&8$%j~_SkVpEVI8~6o2V8*{OJqkmlxn zqnKMOxxabYyhr|IA>TLEpu5)JJ$I!2Va0-iEx0ZBI~GBC->AFmIcEpbEo3cbdQF^p zR!HRNXF^$YwT}HDe9+3U_?ndue>btCHqAdch?e%;s<(@^>}g$rfzp?hXQK|7pWq$x zA%e@esoxR(3FKPYszVr~{?J;5*gcok(UHgl2dwckYF~_OOPSb{PZ=G-Ka7*v2Ac+{KKw7aq)ZrDmJFN_GQsrv}_VBbj&N;JbOpOpf= z=czwHR{97T%8Dc$R=z`$bBw0<(?^f%hQza0pW#krif><80Gb&c=nE%dDlX7FpPLY?eahm*Ln zNFBdkB)vk}T2I^bPU3rS=rE-7taSE7GpGfP-&1g$wJEPU5_d{umCq|;62JQRyPZE) z%?W!z#TL<30yo6#Wzz+r_O>LmLdLHYqElF1MHs9E7f#p8|5lkqLt*TkG9YcX);1SY zc!vVS7k$rd#m-ZsOtgR4xP_wRs~cNqKq0-xwtG}Itl#mNO2sT#V=ey+LaV3v+-&mo z;#PrHqhU+psJFH3R~gd2%rq)5-74n@XK0EN*xhxwXvKjPfQF(|kMO@45? zO>BE(igFdiO25U^;O5aRWf7ele1mSj1!ME=TDl0CWz1AyfY9|iklJiP9L8O^$O~Ml zSa`Vh)h;C!Qi=I}n{Apz26mw75IwulTiFrlD5;{4=54xlSb4YpbX66~(P6&K)cg?D zvG`0L0LkWuM0>0wNw%I^yzA*@kaGEWL(cCGx(#08{pt*L5({U<(PvU);dN5*385VK*-E zp@!O_S72-{u8|Kt=s%>6f?SNX6+vb(SiI2g*)W`LAqb*U*SDP}^YRZR#|6m}b`C3Z z_>bTYed65r&eZWYH{XoT$5S8v>|2`>yNj7R$zeK&YQ`gNr4%i)#F;|wSUosGc~e0) z;L+>vnMI(iqCW2`c>j_{FGPUn+mAZ7paeY;g{!QAgSfb{`!sm1W4!E%DQIjf8$;6Q zaPlqrL{*#R8NQa%0?0}O%sf(rN_Xl#p&avSI&PVm0)FCMW}C+=EL?S}sJ##*C&tcH zXC+1t<$4R+UMm-!9LBqok~`<_zpht)08X+HC-y|jnKRGQlEeu%?$mHd+K9myW~5Vp$E5JcrsM`e7mDRF*RrJ;rvb*>3QX*=B=lov`{RQDart(;9e*=q#zU(Go)xtE7g(d&B z0y%`j+Rhi}a6LvIPHtqNJgMCch=%mvzO~eP>HUK3Qq2^Y zK^#UjkR(|cOwX_X(^AI|Ot+Mx=6rh(AC3rFNVz0GRq2UeAT-`fHQR;>GYlBq6^pV! z$mqAzOOmUM5oyx(gPRa)+FBsrzlg)?RcE*zPk_AV%!*{=^>55W!=p%%6*0~pFo}3~ zDm9v=NTkb-J`2Z&@V3Uhw9-qC)P`9xDyf4(uEa)~UYt7wjY6RSr*4ofRJ(>w{!wW{ z{1^^M*oznp;^r~Yi0k^!(vk#(7{s_B<5ZT=BI)a{8EC@V zW)|^8)8G2PYahEzEb7dzaBH(aKUQCVW(&W=ycBva5be6q3-RH1x`p})! zh`49cOD|^JJLd@WK~E(kvMe{1D!t+4m+au_p|uk!LcUslsAr zqrRM8NHiJ(0$Fvw>#L=5&`sRm(Cnr&C)1-?eR&xN(>u44*}2hYD6$MJzyTC!Vz8a& z8OQF}@u9&F>F&Xt08v#tnN6XSK-NDDZt+@T0NI$CdbT{p+o9{b{ZM*ltuc7pd%Khw ziLbv6_WRc3c;g?XnToPhgO6jb7GY7#NvA_T0J|QCPvzL{t&KZNz3M$DpOaHU4bxiC z%30uBI#r)?*aY3P#*|e6=WMJv8Do6_(F!h9nt5|M?~2x##R#7Q#LuSqoT>s%{tPS* zz?>TbLh#Pc70#!mr;Otd(RC}gpcjEiyNVo7gZ-tPXsBcx;!`9uTLKP#Qw0e*>j5|C zqaQQxM-b=~^mp`7+P*a>t++6CNP)nNdX@T&s27Ke?&Ru-ZgHG73dDSNxEgDCyD3&SKYAbe z*kC*XZN>^b8FKsVP_#7>JX?aVmy__4Ouw}IMoi*O3r(Y~Ls*&K{AU(W?ZeuSf2cxO z)NSrn6U%>PEy&U$u{rcYms@z+-u-p_w?J@R11{c=f3iDRw-&2O)A%oS{>8jJfDq-3 z-n~>4heQee?sfBMT8K%g)h*MTz*rOwiG9Iy!{-D84;UF`R zpPMqhY0pi@A4Y>e{z&Rc9wlJ~e!|;P!Rumf-}^FxP?7oCU+wVV59VKx;qt-c$J`fR z*HK52pl=po@3!WsZ*~zlaCWK(1;<_HJbM;#gAb8V2~9-Ol7ll(h)g!~2zevN3<{F) z7fziTq%ts;3j7rEi^@rk*5Nr)^etsz;3@Y^>~`U2dJJEps4963^a(-0|GVUMW~v5p z;(d^Rxk8H=Hn6&}QHbvv=M?t?)Q7rVOWneggU=I!*WgAKbk$maOt&=)gsw!d1ug*Hpy+`$Df z_2ZVZ-xiBh03&pzkPaJqn!EtCpsdmnKl^2x`8Is!8&h6BfwhJ5SY~Y*og-EkS=a`Z z{@cO8V7S}yF|MIGrXsmAHT@6Vt$$M!;uyNi%WvCm_j9lo^v|lB;S!=wSx5eT4(XBD zR8G+tM?i(>@NXgKVN#zReKsw3baCz`!#my;$sb@Hpum3ZK3%TF+BhAvdn&LaFA03xeP56kzpqe{6z|L@QUH;?R6yD zjDF`9M37_f=vDRI=f60MS<)@i74*8dcH2u4v4N14r^^0?<`JN_@!*d#Myvm{%zN^& z!}#j`1f*O%=H#`X_Q}5)Kyy@xX52%_Ue(Q(iH3Fb3sskGIqov+@>o zJMtI$og`Z8*j(F5wH#fY5Gf}HyFvVQbjm-c4T-BH>P2E8OVL~gJsH|WhGV+jSNQ6- z?Z&Z-KnOp_!RNi>nm&x@we*|wsl+SF#HF%Fp@yk4s| zjylJR?R88Mut+R@(#dLWvY+Tfxy*82mO@a!7!Fs0)*tPV=N*cxpFCA;3|yXeDfS>T zdO&nun$Jp02WFzUfh7yIOeFY)MElxw+Wd=qt$yX{f<>9LHT6Q3affLhAtEx|)H&C7 zaBqu>YPh~JEam!1nTnMTMLO&gB;SC;7P4~T&&8;f0e6yrjoLVv;oesbE>Ep%EjcaRFip9C|&9%~Mu_I1ZO9XtHS5{T?)D(UvyP zelm$RD?|IPnD9iknXFWk{oGv{UM31^BK5XUE1XD^ zl}?RX*&WuNsIonN9QuSB|0Qan%f>swwQLA4fpPRsC@8QpS;b*AX~0ISIL=CR-sm88 z%0_G6pvX3+XPAc5#56yaFU0ZVbOZJ}DsxD_HEly4)_5gVA1pVYM-?`1t%tGBB1PVC zQC7)QXPe^VA=x)OUaL+MW=+>7t2hXfNsk0DY?6pMbZ`#hu7|17FCr3PH4fhk-Xb4C z@mNgG98bd$RJ+{ctmg*0z40BmjO0d@f$}9Kqvz-G@NN>&%n6iOaS1mWS)G#nF&_?#arRASBc{sBCcYj8sIMS?X|` zhh%~vi=$6K=XE8dhhF*R0Cpy3fF3cYT)&j}C*23BOY!hIW@auqxIcUG$L&^-VB`$N zmm-k~c6T$1He}`VaGBUK1i;Sg_=}?*#!N#d0`Pbg=s3Bi*qI?eQg)n_rPb7vxwrSv zS@5Mvx99jTZrlD|6}wqpL-iz>Bj&(rJ>J`F8V@Y z>epwnux*}hWMYqgrvjLNZ#V5dAo*L!=KrgI7qTo3pjLkAX26WB)`~2J@-ZjZV#386 zotK(;H}dBCou17hu$>li=)SBT{fIqO85Whvl+PQPcc7cgct>@YQVjI?uz@+ zk1(F5bNu+zpfFO*i-uQ7x;<75)S7xrdAIsiHo zq7JnOkoggfQFM}u&np`d#rz9{zDME~X+D2FYk2v~Lv?=Iwk9_asb64@kb|4H=*}u4 z7nR5{M7vxBi!=T(LNd@miTa8*Bv+5WRuIP(dI za`#sw*7sq}MD+YByTye-`E}23Ofae}c`dNu7c;Eh;oHMS>=$46xe`^nu;&by>HOJ% z&ZozTY3hv*J`xBY6`oLrvypkmcRg-xAezI~p@ZbKmL{NSZE4=OMbxEY3y@e8Wm!%b zVqZCZi<_C@l`t{OHsl(MrV))4#o(8#OfJw-azzTDGR^o1!kG4+$;Q4fc1Klo7*$m} zWeJ6%g)qbXYNFe>ma1bjyj-osH+&5fBPM{e1ewxbbnBuc_p_#Xd*nC;dYmu-kb+HD z|D`Psog8&%D{L1M80+FVYoev{mdR@o@UzMq&b9{><(hp~U$KXZA8sXD8XR$g%r)bn zxKen4x&4YuvNK5_#X>s!wUa%SkCVJzsUID61|Oy@YbA%P$8Lgw*?FzTqNOD-PGX?y-ZgnGoK(-@t*01R@ zK*<&JwT5Yu(7NWzY^L4w^r3;WsRPwhrC7x=SFSSUb1^Kn9%3hiAiAUH_rlVjQzWax zW8W@I)daVpgWk{TWulxL9vGGJ1G}r-ts6fO%V|vcY6==1&N98=d%}$1vcvnXSWvEI z(>NVZbt%8??Iwtq*l%C}fI=-22bfW}i>f7udv*BRAtUXH@Ji`yR_G`FOJUEH@zJ^M zZA{sb_*swZOAP6bZ&*5Np}wUDu-@#aYT z%`!d{HHWEefKyvGj=H7-PlEEJpBe@Az@QD|oZ~GC`uEHtiii;4@QLm;oNRtFI5r!Q zwB6vcvE%p9R-op;X}imGcz%2eHQnu&%ivz&vR>UBw8DASIzsBh9mkWnOvm7K)8cWu zPRXfn;|@{BJG71bmeY9P8a81U(>;zCYA^HPb*JU@O+SA8M8>hPZKFFAEgRS58@CCJ z{M1U&Tp6QvfU7ugMrIp}d&J4<09{p5`KgY+IvH;gM#Jy;<(PIUd&8(Yy+@b49y>&o z;Y`e;EXI5AQ_EP#UUKIA_+RILL)xG^Y@&EXnMdJ?AEfl zRGze32u$!KJ8YBV=#bG4v#y7zBu5Oz)Uk1#f32C9sz5Lv77Z2nrcmzUF6Fo!Qk}Y8 zZNjyyr#f}d)w|G>SuzZ@c2Rb!*Cv8_rw6 zm+xw27`lSX6Z|!IZ)a&M?t3i}3Bt-40F&5n&2t{>1j%T+=MstQkKd$4Oq1#-gfW3h zLXs=2+|~AqgONWNgyu{UWSrMsA2*%0DPfc*6(tiz^N^S9eXc_Ik2Iam=tlmOt8gU#2D}Dr9tLZk% z857CC!E*##%j7z{#dYE)C34aUdoz%>CKwsk!S{itonN7&6mq@9&r{c7Vzw($;H7mj zTp|&7lMph;9n7D)2hN!=U_I9W1FScX{UK@}(qJn!&1p*ZktqTkG@lP2MznYb59s>R zTR)^%K~$A7YyiR&2jTp^Ji0#GB*-{F}Z^iSdkP&I%b3%;$yq^6iiazi`Ku zJgh<;XID7e=|_Dby=oDayNvD^ZN|CB0AKWkBeCz&^0|K|2X)|dlCteruCTYOjU{A? zRtP@L3w#z~M)Q@dmW1&@`IW60CdDIlMVVv#-VFB@X`EPK*LKX-=G-yma=C9!TdC;>zWM&>N?L8UO1Blo(IV?`jGW;hA~w~Bc6RYIxk4#miYP8e|1JNR|F4X zyFt#mY<_~eL_YkGA$0$tDiJjK5A_%isJKzN;fEB;;vTrrgVTDUDlr^Mlm8=}qr%Nk z(FNV!Q*82)5ZkRr-{9mX;H-PpWTnZEHRTMox)T@d@16pS_b;TeA7yJFuL_Aco1Pu7 z+ikSz%}WhW?UiGb;_+i$66+wRYnCHWn%(5lnj#c}IYopmij;0{yB#yAD#y`pZU#>@ z-G%%oxryi&uK8w79wjD^2HZ+t9rW&62Mbs4c39^Y$5eVXGz)Fh)xiqs;d_<$BL9bj zXs}oI$rF%Kc^bs$b>}^HOD6=*7Pk+Qx;?Y-QO#L?WnTj6dazi>%!*JJ&pA5o-U_tg z?jC#>Xmh~MS$fs8bi&^x@$8RZx%1`nUHz`d{Tq3^Nk>zE?N#?&@o)4`2D@snAoip2 zVdX&aXMh83)ASHZy-im_>3nV|hNoS~R2Hl7jUC98@Gugx+5!Z242Rmf={lsUk=^K; zq7Xf0imb_&v~P3Ja)sr;-+8a;`_gR20wDw;n z!>A>em0Ja+U!|+gnB!WCq&j;yXG)JH@~*ds9`c%~L$1I7~urAUr(8gkAOg_aX9(FYnLKmYbUE0}xDZ+8@% z(mjQJLlTnvnGk!7MV?l;RkJMI7b}%LY-|}u#7NO)MQ6VwvSGIfszx3Z)F6)wX5=k64j<{CPz23k zf|?TR=Y_#JezoPnQ#05>P#B7CXW04V9llZU&0pWlwSPPsn`bfE@Jifu+_W0RT#XxP zCveWd4TmzOv^UUw_Ae3ez8kYLgs4V;c7AT48c%N%k$DKnA+mVwNxaxXC43I}w+DTS zeqt(w|85>cZfvxJ)c=EqXOUeRRzI+1y`akV;KR#y1xndI6ie7Il^pLTuSsae! z3_3RNvSNGJdZcq1;)BFhioE&+<4|kS=l>qxrltdA$I38)i(Q@FuWzZd`?bu7%ic6P zC8pMj=0gE1UX>pXYrQw&_5ud_^QD z#l6Ailc+o>>-7Q(IMzN<6bnD*7F9Qb;GZD~zF(@!i%*+}5}&db8h|8s!ur7khx0KY zWH@Mq7m`8X!%0-6E?%9*AN#5(Yn~symRm>r?@NSBOyQ{*jTv0tY;^^Cjmr*%{0`j) zbqr#9gdo1kp)wcx+l2CHH>iQPznxDb`DWR4Fv(m1LD@Kmo?qg(?#TQPj7iC(Ci@iF zlPo@&Kgz`zzSxej&AM`y<9@L^=OBoaEb78JwB-h%BLI9bv%Mw&PI<*X-M?*x{g83X zTF!VjE5hUXnY1suP}l6R+lQHsD4IOPd_;4YfnUU!zM)xIPjSf;TUhO+*%%U45UnJS z5mP`tlysOR@5S7F@X87!&{wrY)>qa$jtq|zt69A1Iva@u_q}2%>X?V~vky9ApuoAI z1l?y9Xb&Yh{&$AM zW{bx;4LW*Cmk~rR(#Y|rj_=4_V`}t)B{>VAv!QdGqGvpFci&sf{nV>Z-NA2W%IBmg z^t|j}_iO9X0nW!|_C%?4vzW0HA3Xo2CF`=5u{icEy6ej}_ht`X+A0)mZC}t}j{q5n z4Nx3^6HnWdZ?Qxm$8Sr}&K%yP^GVq%x}8ACB5809NS-mZS~tOpzUkK6MMOm1%Nbz4 zWpd_m|7tmpKsXc2waa%V&1}evFM&M~Nry9vqs77=>+#E^;tL?(Hi{H%>a#M#)w85w zi_k~wp`UaX@Q0Bv()r4?am3;y)u!L6slLFVM?`@BATWeI$sY2=lG~8d3$yl{lzZ}w z_=7y`5AR2u8^ziv3( zF&Hkx96D^K?t8W_Ei|77$6BM}A`yT8&m|k1c#I6MJNE9W!!%b@{5wB4|q7Yq9YNR?5XKF;mxbNr& z5+1IJrQ73Lk)Ra#-jKFC5|DP|lVUlFBK#(?DWuK2rJIrwH)F))zQ%Z%k792P`HY&U z=`7b?4K_f>Sosq^pQGeh0?Wqjk4-ia!61I?F=DmZ^^Os)RFvOY?Udaf0 zj)o&Yc03){OiI}~CvfrH26TfV)<^<-_7DX1efPn>x-@{UMT{uJ(Xs=VS{Y^w%Rjna zMDO}X0?LMjGn!FYLp|l&aLO49#*PmGk29@dTe?c7k@O-GX>-oLfIR6!caqIyx>!2D+(^TQR}oV-8x~vf|2EVriZFEWd1otjR5r zh`$Tgt6{d&!{NSDGLhyQ8WZz3bViQ&Hhf3K}xf_Dj5nfG?CvRG&j^qmZH@j0}z{U41I0nL6h&|QX=LgydBX5 zdIze!a0#kfP zmfbM%QwP@ww~#utXP|z~mW;;F{N37bsFiLzRE?UV)`PBuYxA2WnoCO;y$Z94 zE625+%I$NylbMx@y(MRrNzz!}k713He>q<{6F3?Z&Z=>XbYp7rm+RqZ2p-oOAS!RN zwp?cIM#*T^M&>6Bae_K8d2nX_0Sz-AyAV$EKF9xp^&Tcm>{7u%!;fZs=N)S4@}HYcb5@2uHk$7NVE%cdu8bV6a!>rMOkH+6R+ zD*F&r;@!N?K>yJA12*}YBA(FBg|2Uox`{!uk+t%uSQOb;^Ty@gq!76~9MH{Xf`B;Byw|B%10}{%S)DJ$3+r zi{{*x;)_?2H+?>1nqaqeefclA-FVdF-E^z1idj)E$UQrpc>if&9)y!1{$cWzYxx(L zJplG$;HEdIG+%n$^*t-MGfYA1Y0KFrm^Vb&9sLJ^HI#`An^6=W3pR?6ObK#d`Iks; zOEK{oyy2w)2o4g?F(B9Od(OCaM)RjecR5JGU+47=2G>zd!$!8FlnE`o; zpN;<4QXiOuaj)wVC=PY_o6VJm`<;73lYG}U)O!>L$P^8kwkB=QXK}UfKt#xRb)GMg zy@P_9PEY(p%HLg8M>2k8klt8W;ORSjkrc1hwOuFt5%SNuv<5#gArg7@?97@+(YBe0 zUbX!YI?SR+?SDW2CIjy0HePf80lS&qt~V1k>hrP7aXT)wsy%VeGib3gdiM|E%|%=D z&&1jQ;|z4yCUKi7(dB>7R{C9=PpdB<8vysnevn?cLrN#6CapV*oth$H$We|I=c^2hJnt!eLjWr$z8zQ?{4a@KB$-?e47!8}9b^c6#MI z47|NGnbCthujZsm5*bRC@eD@Kfs_5#fI%N%z2TCr@hlpN&qD{=o|gXG<6F;NO&O>> z_SkElQ<^66hYGHZIQ^k*P8SDNx8`*vzfmK&`yV3#!p)1oC2w7AYav#NA(vFI4EZi? zvVL|GM~MhjFeen%&9B#Uo3MoL1tk;~9>6D_dA9Jk7>4$=ZzvI~{a>RFB6gxIo`hpS zJ^mis29z!)tR!zAq;W^>o^cvS?r}q+>n6P4mP1oOuXlHzL28mQ@p{vslMLIAPnFQq zMML)v8!g4X^9PQ*9*u2LuPRJa&V5TGLn$N*G^f*Q%2S{JhX1w)h(7ld_aCONEM|$3 z>(>(!Wta?C|((ftp$6TeJdL0t#44+#%)YC$umEtR*=c9ys z>E;r}zuTe@eVA1-JX7uwr9Dl|44()cCD92^m?M&v_M`cP11|u3cYfHR`bLBu`b#Xq z5G=LCI&oWm7~>IMqE2kf!xCCGf8 z3bd5W_ysy|vmWFgP-VYE%OWTnNE8^m|8LJW_Sgpb9s^72)F%fPcgpGRZ#wD+j6g4# zNYNT|M=mJF%WddjDDCJ{lxx^2F=-CJUVC)=7u%0piTHXh^EUhz3a7mBlOX)`FxVoc zSK++@hzwHWEkh~peYC|2vSQ$TNzRdEnch^6ZLJ1uFF&+=qU24?%n-i1#TcThf{r~B zG@q39n`a2RehHEx#U{n5pGC{rY-4^l+CfTVQ$eWr_eoREx!#V`0MzqF%Ooof zLv}3HbY|cI6h$wZTwMi5E8=^gvIYb<-3dBb%>^C! zg4(~(a?9a4{VsrUH%e*zX@tB^>cS3c4s~h+o0eI@Onzv@m?RWk1@sfndN!q zYx@8xqzby=-;aJaPI0%*pzNqWG>TBdY06%`dt;D#Il;S4yATNq>v^6#e%Iyb9hY9n5vLkuPuy{_1vy~2 z=vlVo*KY)stkL#L-icJ5I$P6jpV!#!W+lZgbELEH-Q@Ye9c%TEzxQa8i`mZtl%%B@ z+qnpp=0!f|q11zO8sirx@%eG8lMb^N1(qlZ3MRpG??;rpzvN}U*_XNE*cE)bU<`Xh zmt4EO&DdRCOYlERDuIH*iLfT?vs2G4PfQhF6}OhUA@T^X4aKD+w=$H2$ZOLXrzYrp zQ^wL0bE)++X$kc)4>~aSY{Hloqf8sIP=TStz-wxeb3F8I;yI59J*QHQnd@x$iZE}1 zoA9jZ7Y1@^PiOm>1KgLb+`j~GlWZC0T)AP!1q&-xwv-6o$y;U%sU%pGPL_{G!DOQb zhBm8c1AFJVt*d`(;gE^;|9)Y7*|vGvvB6$(sST0^P!9D2*pE&MkNf}7+6HD0~nZD#vRF2S4Y-mgfGGLzCr=gtGZ(z=8 zTfhr;YV)2UIW*<~l*v1=^f6Vz$J_V&}C+dWj}xLF>1Y9;_|D znt+@qo~ZLawf6cyp@RA~Z|3|JcF>k(wBvfd4)(?-0gC1wV-yt0wz%f8KNYafe?@XbTF#_?N8C{th=n(+wDga6 zT&Lt+zb-r$YC72*xbQNmmQmn7UT$J4Da^GkM$k9hiA79(-x;OzFn9&3#kXa!`Snt} z6NmvN(mqR++tYMdhyr=+=iKFiZ0H_H&c$pFu}{<$k<9$)ssq{16F2}DO5BBLgkfmk z^7XsQ5GyIa&;R1gKTXn1Iv$ACRRx;IZ@#(dNK3VBHjz$PT9w+L-*<~7*=07xHF
    J}>)1vSm4Z$MIP>I)rw7&Sn(=Fo><^MZP#(cQC_058V+p!YTij+-p^1 zrv{sOg!+@1N{q?0H|dQcvD^a8LPSTygeRaaf>X77UdayCk&Wh=Tm-N?D)ES#eG=I_ zZ42hPKdJW|NAT1s;*VI%aAaZy

    4+Foq0G>Zl~daO#w~ztu%X+6-F_7x>bUsAIov0-e5M0-S4bkq*iIn z5r&mbfM%Q7^4gsUpl}G}QHa&}Mh_GBu{ykbEK2%5Dx^)9YIKBvs))ywd$(#g@d1*OFbNA{I@N}U4VN+g-I%g+LoDqzN6SC|&QX1==Sw=dNA za_7nv@RkeCJxnsZlyx9?AalxPj*ZTstI2l{Qowl7V{cd+ZTm3=4(kV+^x+QMHpjlERy{ywMI0)hgQ@ySIi$6&3PsO|*3Mf=!u;fIg z^c6gRo;Yr0J|6P(Kz6G>&81d=&oF)*Pd$ zUi50-+<{+wZU$S=pRt~=PrX9>4Ln&F*8jz_BnoqWSwf%y<0#EgEiqLxZ81H3lx#!P?nys{i$8LfoyHSr0S zl+|%x25djtVh_x)@U4r;-8aHizUr;v*n)qaRCiaM!DL8Na z$4`n#)`eZ=cW*IEwL;TWGZ#9)d}ET*l`Q*R1At&3yDidK?g>Few@AWi&PBVw{d7li zg4S5~6(`ob+p@lwxXmYc*j^2H>g>+o8rgi+)a=i)98}cG~EXS)rwO2I1M`F@(Sqvo zr0;fdys`NNU-49Wk2IR@j@NX#XIp;sN;D|)$AV^8hKlDG?OI}@*@&OjlS3ZRi(k~3 z+jAxkRG2YMsV*h?miYFt;8((HaD-(M@G&oR_elBNFfQWpMA_<@*8*_o+XA zYD%||L&`MY@yEzTToWblE|npw=F)RtaA->UvodHx8qZHpi)u$8%Ja|U*L=}y0065- z?w9}0+Km1pczOL}^0Wz5LAYc>mar>P(ffHbI?{1mwA0kfR;LkRt{)Ef>5w;+^J4Xi zAdNp%>2np=$2b+f?N4U_*-jQ)?OmRfXBomxo?8~rxY^G6>6q)Y6o0q1BF7eOuA=?i z`LSQb$KG@$YHvfRV|%8(`KX>C&`4jm=)53pL!p`3&419q-6i0&CgXAb*tZ!}myXd# zzPY+M3h9;nxQ(JF1Nsu$7^w%JD}d*Ijq%38<^~FW8wXg)QW}Q(#LTGG0jod3|<4xR4fJ;Z}a>I#IgSWK8mnzdU&ZaIlT%ymQ=_ z>5&T!|BJ@`J?nf?BU9xm=18+gwo&LXByKcCm`u1bZE(K)3vH|Yox%_`AjKlo zeCH+pyg4Tvk0tydb$%uXki{t3JZCiC*F&4GouN#O;7R9ve&D2a&*QvVm1UMU=p@0* z-ZN#sn(#w&v4gK*4^@HZ1nj6-_z(^De$BNXuw`r7$;hkqc4N->SdqG{I^Etl0Q$*`~ux^CwIky8HFz z1@XP80PYiws-u_2o~IF2uYQv8beJ*hME2>ZO<79uE5pCYfmwa-qPXCE*>EZ|PSNFn zY3uScPsP-qcS^z5YAXTA;L?uENN+S@plMcsF{9zLd%$6vhbB5PP1KegYrNs zNqrC>0~rH$twaznA;AoquW?eX-5Ulo)h*vm!9dH|V7MHl_CM=`)u1x8<8> ztYcfCAU)?nVdd1NLBvpFHf#FSrz8669~E8_@>|?$d5UD|(OAU=!zF@glHYn zD+&ISmvQ)~UF25{fpLhfa0wBpV^_0=#?-q9zWg^teJxd%%Lz)>Mps+Hb6NVJe` z$doVZ!pBjp_HEl~X{)Ha-}dvx0O`2RA?8KNv{*BNZ!(Hv1y#%PbPiZhUR9V@b)Tj5 zqpggcpsJ7Rz!OhP?e_}0S?U~7bV2pAcKEioU(#?rEfyK0Q#KAGKXB&mzB~O9MGFN? zt_&hO#SRcDewmOPbKTEnxD<#no%#!7oGWRX89Ppa$(_M0?f4jkqX=~=@A!&R5RNwq z$8L2lQz3R22ULy7TJp0cyTr~Mvwd0{-L+Nxfz7QEl5DXZl4$=Lt)YxA+8G;v?8r=i zZhXg_7ai3aGx}9E&-(4^-i0^LY(p9_94qJNA6KXjVrN{{l2GVPTBY%3X;mz68IzM- z$HeY#eJey1;I2Q)Xm76gC+_`Cjk{sO%{)DlT5?>rX0w{*huz$0u}NIxg+p*end& znHL!Mm~0zWj*239cQ2z{d?7FxMt94T4VcT?4?l~2a|*n729h*lh%kuC#!33p|#ft(a7u@nA5 zuh&87+nQ)H0xnw#fbjIvoMK;3hQ){yF*5c$XDPLSEN7_j@+uwzDLd`H6yaHGg|Xlm z?m`Be{QC6;J%lmS3AVjAp;;Ox^=n>Wmv3i!c`Zb9BJ9U%~u8M z!XBoZg6ndDJQ6nbrm@SN@v&$H5z7ITX7$mJ)HBHm8Oa~*FS zrD4;qbKbrOzX2;6MA-wSZKt(KcF-pE%|f{-yw{=ArJYC(hZC94H6_W7n>}$aVRT`Ov)?BK@&!Ed&bMo$R&>)C5R4HxU*yQ=UkqMtEBrf^p3; zMom8=a#zIIlNh%o4jrI@4L3f&g0$ZYJKn{ehYgX<@dlA4VgdTMb?0M2!pq)(IBC;) z*vM1C=3LGN*!Ds{@L>BxogK8T&K}zCHgKWTCVdQgGV&NmdD!YHI5}uL;k)y0u-zbT zHEmQ-EK{^6&Y)41%uCM}xCtUn$X4&f#qaK>)eeg#9$Op#_>KxsRx!Hd{f1_mHs=wf zGTmg#-tYj<4hZut;i;bFt}3zlm%e7n93&v;Wg%wIgyE57T$7h5fMMy&Y zKTriI{X=>ppLfW3hjaVrrV#oAFDid&E+l5%GR$q9 zvYl72y;G`H;@?Fs?@Ns#a_i?ez&69n_Db}_$qL!iLo3!}t-$PPt0{rnH?dzhQ{m*5 z-8>g`Ll3t&WEZE*Pg$+l_5;`3f4t48@C`)k&8Ex{fdQW)?$VY6JYP)+gZ({lUp63t zdu z%Caxr+^41;!yXoZfES)(8Ha+=2}&D{=BRO6KE&8~60*Ax=+6`(jT8k=#F^iw4e!PG zQ96{4Eb=(=b6P46(nVe1%Q1g?`d#*qZxbfqhe-6Mv2K!yX?a~aX-7I}mpggeeT42P z<_Cx}Kh};55(1r6?tBe=Rx8J!fFV=v11_fm{dHUu{Br3Mk-jC_keWzLl0VH8C(|SY zyenFWn7bf#`+}e5upM9;`!1+a<5}_Pa5U%f^}P#S_~)eHgE((qfML+XTR_QiYzol) zDm{$IgBQ{m+{wE=HXc$ws}~M&;vqJ6pm0M;34VGI^h(D;QpSvI|-r@2-jSL z4i<9PoTscj>DS2CC=4=Ma&P)!o`Xy*`vpVrlIWXKJ_B#Vj%XDGRXF;Hd1n4{E4`v=6k$+4N}Bw*-<|NVhHP`|$*`9hZbgB! zVPo*w#D~{q8Mo^Z1CBiQlTjnL+7YTi^R@V8kT0S*SiAmxyme2>ic3BN?=jq*ZFa7x>ePvZrvhf0XL+d zhS|H)opD)Uz8^T*nW9_$Pfn{!9X51f=euU6qnPWf!ozf*|C{uTVGEsEwf_-)b#=5f zL=5b%pAR?`pFVSK%dyb5u8Pd>V{i5fq{!UvJa!T}L}TZ~z*7yAOs zz9Ah8@IKoO;14Ymn$8JIi@W_D8F;&qL>41e=B<-ae=Y-ZyNuVVzm|^*u(gaH{cD$4 z^$Po}qB)7(NpF_YbJ&+SpLqQOi6+hI774u5E4r8+Vy}1C&_I-ASqrI2i%ozm()sm1 z%Av?o3Qe8iEF52bYEhpUk(W{7W|`?YETsT)BAEqS+#h@<6fyWdf{UKg7wdtClqK*- z7t0BVvRCat!J&#Fj@ADQ(SJqhgS2L28l6oJod%CRab=TNjQjQ~OH==xDLySJZf&^* z`yVW+p>%cG9`8@Kt_gXQz5ytZ9DLaZu{^3C$EhK3phoH{zswPeOx^E8^+WDDqn=b#BixJV=}ol3nU^ z&PbDV<`W;n53ORvx@J2m=$w+b#uQkJ!yZG#FPnh>dhV}VCsT1WkTmYnXS+;)orJyf z)J7*J0Pf^&wpfj<&=+q|ncQ=IJ}!Pe;D4i#5^pA|r1AG9JJfjROTYnSN$(o-vY*R< zvz<*?l^d0JO?ZQWy}}g1kLJ%IwXcu(xN)XFzDdeuDQW?^iYoKv0ywEm=D2)nCkk`(+`=C^^Mwj`VEYT8*q=*YES<|_r7 zVBEY@IV~02a}5|vl|Jt6RTK)Zc&~QeK581j-xf9nm0;hp3PEyWMYil{m?IqldCwZb z&G-KXMWxwkNL@R9GItvbeVdNAZ)s}%sB9jX!|hmYQS2hlp*a2=MjDfoM^tkECaqrT z@eAhTF}+P=YXn)XeVX>)F1Tp0;PT1Jr!+*yF+w_ZF96s+Zr^i25mpm-1%)og=++}0 zljG&ja8y^86OPis#(VBo2nZHV+a^Nw(oxcrV9LAgpYg=k!ek=@-$^W3 z4@eBQ8ajcLIJ2Q_qhPJ?{+PW2@_9DP0z2QQWx_zI@{;&9Nn7srD->8Rv|(W;WCfH%K$iBTFQ^o#;Fnk?H{^7cv(B95OnwHu3T z!`tf%dwIYwWe6bVR3pN)B9<7s>vuLPR1%q4E62P`;0)fq`u8zr+bLg(Y&4XG-H%iS z-2@o(%T&Su8Fyu63r2yHzTGuC#afm`^b?No^4$_`Fm0}j8%4~jQ z^H`s!+1k5`13&NX`RX|?7>uh>Wlm3m7s3{{4#W7jU>`;g=<@X4@2SrpM|S4^EeU$w zZtqQJ{JQVnZgbT;jy`&mX*h|RLJ{p~+VnPWAgSk(-~IdF?Y538ndmcbyP|}nD?qz? zQVG7++DBDV4g<64%S;r{j25T!inGW$9758Mmg7IoLO6f_*D;%+iWGb@aW;fz&{Vpi z%iKg<{z^{+3!0y{n!7D&ch+NV=Y8e|&(Ss*ddrTAP>*{*Wu*lP z!q>hODChkic!wQHFJXU6LgK0ZAV3m~ji7CrD$1i0Ow~%%lH*J={(Bc?CgEtewrj51 zwUXITWTxDKRJba$S;JyOLCZU9N5MjLv@#{zCohPoMKt5@jr(t4nr2ZW^>hd9sB06# zRN9TkmeNNhldyySIFWd$p(q(WT|lbgJ?PpZlpp)bHl5^EE*p4&REj|k=_MmlJ|s1V znd6?k9gx>#|2u$D!>|!B!V3DDSwj5Ls76`Ty`z&J>>nh5&RgRj?7?m;`84-)gO;NlPYj*98=7!wyivW=QCpdd;nZ4MF~FCNxZZk>;R7Mak6%(G0YzrZE`4| zE24Ycqpk|Zy?#(a72!9&?5}|1{p!AA{*2JX{iKCn7TyQUzn)M{QDN|Qu$hy)Qu~4D z)30s`%C#ppAtE!;#*{)(|mCea)t1dUgV*jY#OJ(`?+A+uH68I9btuV_M!1xg0qTY(y=e0 zMcqdC@6V*`_t07uN*OC(FNf5Fje!u+f_Sr(Fmk*r1k#zz)a$rpMjLxzMW2DMraioi zwE;^di!{_tqfoK%u5dJ{9Dh1<}k6 zP%j~@C-m2J_C+ZcMG!kIRPn)^ncw)A^Ssaxr-*QL?u(yQ9sjPNQqk5*9O!d1&%jmx z;dXy2sGj@6Md0X+sIcQR6L+?A!>V^1=Cjm;?PLfdQ>70apM>Z2 zwWP3q{TaaDrT#kh&fRbjupZR&2M~Lk+v)SX`7&n?3<^WtGnTI2;zJ4an`p znS`;tcgWV4Q1^>{kUmVjwy;4qZ_myE89VJrf%Zke;I}9`QlO zeDqx_J1~&=Ud9XAJGxTRDza2wbyn;wV!DdSvmO-6LM%u*N}RsH8%XAYa+V;)4Up}v zWY%9NL-jM`&<@PE&b=*jh{|k1}d>-~O8!k4Nz&Bw#UYDm~OZFtu3w~wx z>}_vu7H0C;txq&{S){~=>CAw0{}Qr;{#ULA(r{;kmYwaKdomoZ_O$q5s`{GEIQ6yE zIe%UFI(N{y*L89fa8NIFRzt0q_@tY-5Fl4cF@BCKF;|dB;~U zOEy24gLA!~uO#F5T{xX|LnqQNK>C}jUg*2o3CCn6i$5tw;zQmM>_&~JSpK>H1I6=? z9A3uMdEl8%k9qU_1}xjZPUzm7S*P=y*J9jY^lWRtlU1FF8=(5^vLsZy56f;6zLmRgS=#vi|<-W z(LSGRDl~3|)}~)g+75dVP)BUEU2OqXnJrCa0>J5NAUvt`uhE9wm}X{>z@U@2RVaE~ z2&t!r?<0+`B!pq*GQTY8>5P!NNKWdZ%hhZrzdeNN|H<_Z!I%s@qpn z=RH$5#J~;N-x!?nUgEjW_1Mv{y=XHG7!AY|BD4O%E-Ds838)>6Az-ik7@GzLx`dacld+Ad|-^5nV zn*7$X{-b6&Ol3F_bn*&!Ma$_-^p|=8j+I^>{jr`8Ml^iCvug$0ypIW5wDI|ZKsl*z z7QUvDR(HipjQZp?OMFB*`VXop9IH@>#`iDu5B3K|{X@%CiT6+2Gu0Gkl8i*%)>glg1%pJ|(_xzq$Oa&M@3j5TP%{Fip2sNe_4KI))pltZfT|q8 z-|Khu6nl~9rFl$aJCSu|v7VPc{ygA4nJ6m4$+z~id`-vjw1U=NBxsRNlQYCgbinSy zmDcDLGCi5Rj>THrW;x++$DXQLbC1ny4?_$aLDMK#D`^$M&x_1stHtmR@_{v@l1BKx z0_Tozxx#m^8(=WWcb9&(D@kDBlsm&3+kYfwf$;@b6rC7{f13Atxz(2OICu?|TmA?Wh`;+cEyd8e?t+dXeVqQ}nU& z2s1+7YvS3VbL%g#ZwSIa-kHgTU_xKm#AQC*4o_v!?-8SA;bbi9i_cU7LT?*5#E^sH zV0*s0yg%d0ZE-Zn)Wnm~JOAqBjtBZcTe(?5>JfzJnB?90%nZMc5nrAa(nBr-WCB+1%y2(CN z6VZE8>J-=yN=X^a#F}N?%>yOHO$Qb{dE3f7yWvd!L@V zN5Q%wF{8r_dGCP7E@HPaYl7bxB#GF=_`(W;EHY~Ul-8P+WVCyRi>U40b@Ofvjh>Xa zA(Cj(XTL{NT)lkY^qXcTsBb<_67ASKy%%;lLiIS`eU+;2_ia^JlO@qWNaLIi3LaYI zTz~V263J?DIyk*wX>N3R9Y8Eh%CFQozF%pW=M7PJ^SlJgmzzDd<&}CCRa9=yDCg27 zbsqWY?8h)@Wxmi!yx?Bo*+q_7rt_4A!RQO}Cy8z@3rB#0hlVzv4KxxiW!G#>Pa3$P z!32Y%Ys~-Avv|M~d>|uFFaef@aMsGJ^vor-lh&TujSNHgo0!Ks^gRhGYS{x(a{(kgRaKVBhv7~VS(h#Mhk#b%=GX! z`=a*ta3ulP?9L~c#c)_GV3E9J%Z3~e=!eH7*V%jHC0M)ozCuk|XG13J1%~=IAsc9| zRhV?L!lz!&I{3^?;x>1M6Vmq$ap+d(fn?$w!9VXDE^~T&g2PBDy`i6@FnS&n01JiK z6Ya*Wjch>suHfW8sO6@d7eCAFso`zK>Pu_;r?}&~)}vRgEdth{V;;zF<$UQF5kejF zn}jsalICnWKzyR{qun@4#$iF1VR4Ywq5DPeo!4vidh$4s=|W>3yQyO5NQPWUC0CH* zr>OBf=d0U?<~fJJH)$u=EP{d#oJMpf!vwqVE&jxKC@cF#i`NPMC~3Yr5~lA<0L~|c z>2FqECcGnERHhLLh~UYkyy&PlT7m+w2c$jSs(rVJ+P(|rW#=%S1|W&QEYwJ6z}D+$ zC@gLgaIv4FfODyA1}%!12WpBYn`$k@*wA*mvDNl#bNC+}0Y5rkqI}PRTaKocM*pSa z^d4k)vuq_^H$mC9OzIX$mwAbnxtbxASI+rv z|FQ`T+-uG+_*#x$(ctuw?T&LW88y+B9i0;9XDnjGLziRc)mRnMo9A}a$d|v)k6Ec= zefM+42XHK1bF%=2WpA#n5T)m|=VrCNRWwvTzRSkBmwD~$f^Qo;>t*t}2NPov+cV+# zU)<)7oiK*Tz;I&%NzMUFQl9G1BjtWCJF)C^=6TRo zZZFwg(mg@~^v`_)=W{r1DUxZoEO)}Y#MMFO3&@zf_u4*5WqOCU&}CkS^p~1jfSxZn z=RFwl$$PUx_>eAG-+SRg&b3$3brhG?d>Vmdw=-CJern{*M^PwL$^p|p&X7h?ym8kB z;2u3NdjM*l^gtz^$gwWVCnu#(y4p#LM?~oPbT0Lu*9l=uDXOh$r}JYL&)VmQOf>d! z+KP+xN_yVkOG{Xid`z>rWR|0|B3kdO##NKbbmRM2e0A)EY8*K;T5~Qf1%|Ls2;D;h zYZjy`zxbfvr-iVUa~g7PiGdV6GTzK=#5eXv)&PUfaKpTqRJYm}^-=e#e$;HJw7v=0 zQK@uNU)7Jtt~RVYRB;6rzsGswq+%I1<8r^E6f8DAw^lmd>!q^TLECq>9(xHMFMCBF zyT^0RyYI&%oo)+{KigsVCLO6LwJq*jzNYM_#?AA!1xB2maFpEapj~bQN0TJrA?Gyc zI1uSn4>C=2n*Cwq)Y0o|Jx819+}IsQr66rv7zdFw3>^%0#x?4m5Z{opaN6?A+5d&;j^=({xp*wSxdw&Y zBv3!jcwv2@#5Z^rS}E@NgzYMwm&XUlOSZJI^ExqOpR9Tkc*SP2N>Zcu?DN#csYb6k z5xM=_cKP<>%Ki2APQqWq3_rPi)Oc7Qzv1Nnp!2QVvrd1TO}Vg#Po7?U|B(jr4Z+~< zQ@EF3@%y6cWIq;eomqb*(&*bwH;0w6U}*(|@047%D!(k*_59!VDCP9LCAqH#7%6=Y znV%!nAp`v|p?%+vr=8XBm#J`9t-+gDGo2MjmSquFe}CjCrXp@x$-}EC(C|6?6Q$-_ zKGur3uM6M>Br4}^<>JLU@^sUGt*<3XBh9_;*t2O##3tHL@8IO)9uylgH{dPN`FLj>U6XDtjQjfaltnKYuSJ`-*^+9*BI zyzC{BrM+wX#Ou#x+KhhmoB5Q|SIo#iz*V_^)V^dBw~g$B56;Wejdtq;KR2sS2(kKW zLieWaDFn3_Y{NuuZsG1fQJbY3!GeKeFw`UITk}yz$fo-3s;+J=vXzV>K*RVx&esLd zcSS|qUUPeTWO;=`KexU%sA{fRULJ)9F5C&eA~90r_{}LxrclPBX}pUlZeHKYqJ##B9_Y4&cV{TiJ{<^eCJl`1^_ z+8SFRM+2_x2Mx`xocK%U04PZe30Aqx^NX>zogB7mjTFAAAs_OAheYQiS88~OW336k zN)VJvu0+DQWLh)P?0Eez#j13x{EQMFoP5#$f9C9tj7UJky=I(6eWRkrqL{pS)VgNm^e z5>krUJm~Kw%dT4ZT{VvlYrqlyR@WdYl$5f>Xz%)=}T$R`;f- z`C!FAKX_L1saBW9ad>E1B$ZE6Q`8D9(Qi)kotcZP&;BuUIwQYz^Kp&`H^Vj}o!gvv z|2kw?h_A^uM_qO_R}^h4mJ4*UO$C*sUnt}Q{Y@eb_yd4~<|@1*n^0A~lnJFEsPdCE zV?BfI$0~#3yhvBr~xIuB$!Y}SUiIXtyFt70Pk zJ7M-Srm2d0L>L#baDw-a>Fnn3?h@wSuroP8Rr$t)-xadaH?Xms;yel~ARL2r#PG<$ z8ru_=Plv!P7dNwEgam8U`G}V4fMl)9Zc~xWh^>SQGO7&pcH#rlPCgVgnQg3!njL97 z_IjVrN@r9LZdYm&;U5Xi$q~_GX&NtAzCtT3)sQs)SF{jj+7a=vQzeq5yS zLUeeLd&&%YP_HDi{Od@{GjSCt^|{ROLp}6ds08qOx4SFC>)$}?*2w?lac{q~_l1&Q zq4Udox^$w@x<7b@9SnJu$igWRh&w7XDlEqCEoHAEuE*zL8W!*BUWDX5qb|0#n#Ydb`Qc*BTq-Y)erNlS?VQPYV_aJ#cjFf7MDOW7;DkoX@3)uB__!0JAsdNR{u9E|M1|ea9qtv{ z5*AbuJE(1b7W;_UK(EOfi94G{g*AGc zWp=r(JZ;Qtbn-DKFNzSkn`JuLkTDKOxs=x%`p+AVmU+b~qZVKQ_w}bdN9>GHq>}ysy!e&kAvO;Lf$!A*2{Xnx8jmc9 zPNkL~my5?M6!W-3{QS42B-2MTqAC(^LX&*?acIe&Uk zuq6IlpW#(5LdpGNeB{NbC97EEvWp%?{hv0@$-ZS`6vIo8(&ObfB?WKbj34M#4PR+w zlJ&M7Uh%nIliK``X{AALPjNK^Q%jN%!-Sb+w_D*(zQa^gVIm<$pp(n2qyCBVvP^|BkYR`*jP5f9`d0tv^vq`logFkPCMv~;_84gpWr(rMQ$OFh@;L~|v8!D~WB zoShgw4^ufqOoG$xbV8RRBS$dPSE*R4lOFtQq=f{2%H)&n6iXnHiPTa-z5?S7A7Z03 zroc!nS|q8BPg<{y7Z<0l(lS#U0N*1b8r%W*WvwCDQuk zXikejVf{i1&q}&xF54_?ts9=T4v(_4ls~C}uls+y9hMdN?4*@DYsNG?23{O4ORW!k z=t(MNA><~*ADz$#&d}#3Gj!w5J7^{gKx7IXvwk&CI+RA=@Xl$Lk7JgL)vilUt6_ZX z_wNON0DY4`ovR0C9Nw3Om;Q7gph{oCFJ4SuWq6LYQBq8?tf5H8<0k5br*oRICS({H zX2Z2@x4Rf#l71nHDP(@kGoO=f<$}iVgU22p=eC`Zb&a4RV%&Hbc`d?8@@~&Jp@v+= z(yIN926dHD8*+Ap<*gtCm;y+;?QnE>ptOS$C?BG@ZETFy)>qmiq1lt9=&|EdNR1zt5syYcD~!O zZoF6oy`s8Wu8lEO>NHo>&axK5OGfv z7glJe+j77@Bngd=+O-_y<8`-kBrRs83J`H~Blz?1j#Nh#NSRpAt95Ynde&XJ{rbH} zNOkZA<7>YB8gU2{9050mqSH3DOrs3&l(`}3{dQ9y8*m(`59Zx}iO>1xJ0op3Pun6t0vw?PxfST{V@C>9J3%nL56q%`tL0;~JmvT+&?2W+vlw zRlzo{AVF4%A0d_Ko=f>2C7`H>-tk&Zse=D+^4k=Yp4~)TA-8kjs5s?wb>dXYe4ruZ zrbCotZ1s?`K|DDm#Hxdi#&YXGBG-2oO6hGS-~7#u=}TvT=-?K-1YV}zO1)oTEo+4s znJeIi-?w7g|L+1&b3SO$&4W=+@2lfaTdGgMH(|Cb9|_o**ld4A>a{`3Wew%hU&{z% z%+FvG9zmHT$l)YK7>0;9b7V_Tj*(@p$z=9VcZS-!7+M zR1Ah5lZ-Hv%g>vccM27pI`-^v)w~G^JL7(Xd$CH`rhy4SPX5{OC5y*O)k4Lme9wc% z#jR!p3Q}puZVATHKY7bjHU(eKW^>9+2Jx@d;>}iuvxUirq`ddFx6rXtQIC_A`_XFx zM$<`viswkke@9Cs6pIi{)l4LNS|i&ZZ|h6I622^NKG^X!>~P{@BlR>5{Zq}9YKB{* zL(K4J`R)TC?6h>@r){aYDtsUK(zdulnP}~H|2ea;PF@%_%5b=WC&#YCNR6pGbg|A+ zQsM|o>+s>@pXjz(0@?tU3P3M&U zO}Ig~-cj9eOylkN&DcNQmnFW8qCA^Q667>Lye^KC`rcB`Z0*LATk3}%){Yu1oU(#i zv{tWt$Yxj93tond7am4#9xnuP>X%KI=*g{PBx_5V-iGqM@xTI^5J6wBl8Mo$!;13e z3v=MP7$Y5^DynPuVDl=^&2<9VL`7!)i=iyM$7)9+?5VI($GqImDZu-Vf8$`iJNvDA3X&q{#yxGMwkT5DX$S426YNOR- z-Ot?^AM{+EMMzv&(^#=iRI*8~!&-Cn=zyh^@gHcu!?Za6929K|Oh-IRT8%&aj(ui_ z3@65nBD)@V{9J3U&GmwO4E*5vj-=BJ+(}8v_nwRPOdxxUk3f-X$7PBfr$uyS&1UWH zXN4|zE>fM!V@Frf6xrHav#2S}o;g1!$-MB>U~#++s^S`zw{^6+q#Tyk{*<_o7BG-K z5>!Ay#6sV*Kfhg#z1F^cxm)$!guuP9@oisK=r)C1 z;gOsS52~5EXqCvl-;5cOEUjCx_K8sKQjCttZLr<7EH4rF)RpdwP?SniBb$lwfqC9? zIxU5_Ku+?fUydfgL^w-n*LakI6fVFXW$7D`(SJZ)#(zY(y_7&tR_MfGv|!bhFtnfb zl7WLqge;954UH4smKkL3`^RU2T4^pDaseg!Onv8@5k4a#{M=eVEk$z6oXfCErAB(P zn`1v5SJ}|21Hp#!h;W3jEQk;Wy|1^oL*EUn~Oqu=D4CG4)qbaWf1XF5H%44+V-l6faJ3 zr#MB6yA^kLNpbfAMFv{5xVsGQ?moD~;5smy_h0|s>pM&ivT~A@2JYUAA|z z;s*T_3C%*lC{o}wxSsR2m|5SArbr%O6QOO)Zjo{j^mhd#?~9JRAU%u0-#X&hXT3GD z*+9#r(4^Olgeu{?F3GLww9py!v~&$C{nQ`e`^g4moso%lEdAw?HWS-v!8QCKSoWR@ z99Py^m_o4rIQ{Z)VJV`@lQTzZ(QxrQdL3*nmySXY&m$0-CmZwxd5cj{5BM{(U0h)! z^>os9*?BjVT_Ha<#wl8G4&m+V*ma(N-k5p`S27XlN5WMmJ8SxE$Vwsr(bG@vm9~6W z%-snv4B&cfPWL;l_QZY2wrK4g#wfqD8U1z8Yt}{4>#7%L7=Xc{J|b==$ht^#qk0KM ze`=s^E?n?=JlI3&up(#)ZhAKOoipYdUb%?3By`v|ndhtQhV1-gTU@9-k{&?kEYGy| z5;ChL(3a8>?MQ5;(%GJUXa*0+ZO*peWwiosCc-K;CPqK%_5G~-2me1bHanA|&w|3u zCoVBZ9f%oZyV9gBBTw?%7Fr;A?Np~T8%HCGyZY8j(5~;wc`RC)#V%pzj7E;i4M1q;wWup_4*}pS*Qde@3#tAUIZI zt3<=7nFH{%>oELE=`{|;CoSLkiek0mo0Lq({{fsM8|whJ|)X$hII zx*go>SxrlES%($V(Pg-2nPF}MLgQv@^? znVdxry~{{0!<-!F*^bGXUAYa zNyJHW$gKgS5paGFk9*aO1P(;~&OLWnAU7pvoD$PQ?CfJ7K znWse>r?c)@i0`Bq({I32{H}f-$A27UdGp8K7^ZmOp952%lBeON*t|zx9;FR@R6BJ@ zQmEd&&Z}s`735NU`9*jX1P@~4lnU0E1m&4E~qLA6wB(cEDtebQkDp5)D=iH&RvZb6g}M~m#V3z z@R#9?Z2WRPl@Y7e@%hH;)QDb@N}e(V3*#16k&QLT{BIuq?01$ ztVF>HA!Sx_VWx#WrU%1eH_)FWQi^BfO16ICtBBRtM4prAyA5wk!{0T_=3Ww)3jT-z zEA3GN%I(v<_zIK*N;@-WS@!op$oOxw?`Afrw)TUQTj^)lc)5<$nM30JDM5F7DJ);3 zP1>MH9T+V#6G$YhA^V}yGD;h+ZhLw|a^1C)vQ|VEbM9+~ce@W_amlz9U#6}5(Sj{P zP&>q}h@Q(iH^FxFiD52U|^(E6evIT=0J0D9sQRfFJu z=x4xccK*_?kH$3@j#y)J-+Amx0nJ-7#p^zTp{5lT%Ud~({Px2)PG}j_9Z=&1~tpOE;SmLOW0dH7p5et9$(6M_va8>9oLAHk zVj61yb&jrH9578~B7UU^T_?CZ1b5I<+lamKO5Fr%k79|L3v0S6c6ASIP+?R0JfG9N_0$g>C-D!>74Bx>!F^9q_0<`gk zA%NO{68zz-Qq8FJ#FNz6!E$y9p0a z5P?lnFVH?o(tj6b7?NNJ;Gy(JBSU@q^L;Hq^!a+`asdo;q*!rqHH2s0!GRXP*9OAe zKLopwF#_99hGiSN2PJ+cR}+~$?uHW&Pp|7?myc|ZH7}7bdF5S(Xv-ciB`*l{yG=Dh zM%Aq;XI?bRP%p$e%RWFNQba2vT zxQLvpx6$^vnMy0T*M+dA6N}uO=$i;u`%Q;JQeuxeJe*l0>Yz&}ANc$EFSWiye9Z?8 z`V9u^Zgh-d?AC)f*WZz?O9sEibvq{-WD>$$B;h7mYApXG*>#R?)4g@OiO6gGqiTbS z5)ibtE9=Hnw!HhPI_kM*G8ky`u0#wu$N#IFYPLu%wiVX3FWIEzZ;ZQct^z$a_a3`1 z(f*;V-b#1Uj?`4AoCYv>7oVRK7;wg&`qE1aib%qJJRt6`q>)PDu(*j|sbU>^Qr#2t zD?4}lRc~!x*#X^Q=!CT~mK((Vv+pbkkDYjW7?vQhib~$8p66IuoqxUnMyom{ecS%k zfKzswY0S)-Qesla`I#G)K!t%edtINehzFh`|3TwUj|xxSF(9+a{9Z&Ly9+G@Kr|wz z8Vc5#o48TT#vmU0ER`4`d&BE=#(JHroXJI6Rz1`;d>EwEG@PD1zeH)??KM}|)mb|P z$Fh#BW@)vT`%QC#CE0^n*!`!DE=uU`#jwMtcpExD(J~t%lG6-)lOKPVhL^(Y0<2ynVD!g5amg1Vs0QDP=6Ft#(OSlIYnySpHFim4cJPT z$-iHoQAOMC+ekp93J;GT~pu5W!&*m@heM{_D%wtt5eje%%6*w)m zucpCi6rjJt7nzyYRFD_7?1WtAzLFp4t9|=hZd&kQc2M)p8qY`N!T-PF zPde<{#th1o4HO(L^Hd z&BIYitZp#)P3k^yDE6pdKG>9gXjrCd`g`vT)I5T4C<`r@CZO%^Bw)Rtv21kpp?}C}d=iN{lef(=XP!|65oi!F zFD#vgzIrY&H3Un%krCL4WINCv!75~yOK!qyebrG6y?2?6rpUlXFe%4_#9Ch644m61 zVB=Jfeu{T~CTJ{mM}=8q;qXZd08TS~p*57|H?=>a1sh{q;q#vuj!oTzJqD|`HMjl z^|>#eA6)Qd!37fbZOX7xNi_r^!S}w?+MP)-Ub%i$jt%uu5bKSa870IVOAq zlD0h@58JrAeR=K_1YWK{Dm^_&$utt%$Tjqp{&V|-ncyqv?Zh!&-NGY+WnY60Q94D7 zmp&=iwCKx^Wuwz-z7<>s1xk{?kx%cM5)9?^l&(Bkxw<@L=Lt^@)B{@<`yJ4ph@@np zxgFmQ%}WoYW4xKp^jJ>wLsJaJjQrLZ9BDUy3J3m*+bGZ10Oo2Kq%MUF<~M5zt7WX& z7uw&tdIos43o}MMSA>*`nKK@&P=^YThp0)%x%7%PgpT7gApeN1#xj^KL~ST~79cf< zgEnUcb~bv?8Yy$OIboZ=%_5Yl^0g!+498C%y9ikIctZ+B8x$tQI@J7}8i zMi`cKCD;JVba00cGjl7wrMCm)-JJdtBoexGSyNlqgb4UkQ>n=fG;i0tG2}jzd@ijM z{#cG?>2%F`P&ea5K@Hb0SK8sv5J6HCsMMt`b<%7a1-iZeVxPZnD}T1uy}x?)ff_|$ zD)y`Awl2q%;J$LdF2s4M1s2GlPDerHd|OkthT%#-1byS&I!=A!k31JH2k*D|Idr`D{o{=zmfd-QTgGv(}lHV+bttwgyCVq8t0ikoU{fDWz(XTG4Xh(jV_zRob5GwN^5lS z!vpV>Rjo&9Nmo^C$h5P?3C6W@eud|}{Dx7S0Ric4=kxnR)cc~T9N2LiA{`0wM>T5+ ziJz`jp4!i5)x6vW8huL6-DC3p+pM&zoddRdOJuI7e)!H=IT#4Ya0vc1FL6~&O-I~+ z!=$Q-gWok!OZH81F<)nX; z21GqBwp}2n@Wi%*hO}XceK?<+dL5HCMY?!^1b9Q=RsfK&t8?CNl;KeB@sHsgp+|WB zjmpqZNHG0W8w&$BVS@Ib|G77DNU`kGaEa5&Z-L93WA`E5^7 zos?M&QEUiE%gVYWeA7ATD%4Pb=4mGj{N%%a+r4j(aQhpr3%z%_eF2sOrCt#W99O7gFs-_E)3 zT~9ShnYTsKNeCL=7`#U@^n=h#AJ9d6wmXNM32DFV-M$XmJe5Pc(} znFmt3SY38K!rTKdar|WOiiN=@gXJqhFhY(O;B2&=9up$%tNBJSMw+c!EPk1mo@OpQ z$SITeei^%GyA?*V2GIcAzHCk#z$9Uykt+P;7)0;%lVxMo^F1?TLSx5ZdhQKsg>IWm zx$E`=ZqvUX!W+~N3wD&$X5kpLLM+a$rOehz zZcEs4r1KwvH^#@2J6E4=qrf`&mQjl8h-4PKpON6LeC1L*9<8x!`~F=<5WYZopM_Ao z=exYR&+E5$u|{z2OQ_y@y)&%g0CL#BNTk~IC_MOe2ysf^iLr+oAHiNFjm8pouMF36 zc{*(z(d)R%j1S#pTEAa-g0upgM%Umw_&ikBY8nUthPUKaaL#E)Gv%G?YiAig|4l^aYYq2e~rhx`RhM^|eoNG{wjx=`>wottH-{y7y{hZEKMB%S4 z5gFpG0LNVnB~KCUWyK5_MNjtezDV2*J|OIKq($5K>EcijB!=P$0L$}# zwzDyaR4#Fe#!W%n!F4@+@;teI(OD80Tg-E^7pHN8iZw2{qqJ$cNvy6RQTRC?B9zfo z6L&WNqei0?qh3s-w2Wm=6s17y@Nr%?a?g``}vfNiduB(G)}UXq@5)&)iz% zgt?I?l%AOlzUeF-b?L0lyyvfXS*8p)82^^^v)+U{OB`qXF%DfCG%ju#I9^X$k&q7R zki3eZ5t><%7e+8ZW=)&Q#zC)3Lm-OlVUi&z;D-~le$}>q?DZP2Mm1{(;x6JJ6Xzce zE=^rF4@CHZn;;?hp5SgW0R`lIdu}WTjc;Mj^^i?MozwUOvN1L02n(V!hWnCHy-D{b zzpl}UB8ICOEuTQke2xTCaGtv)G+HLGl3J1Lpe4Sq1nnc>u)O{OVJJCHN~Z!x-Db0O zTnG#Re%n=whG;9r$zs5;P|vv~W58qw^R?fT&*n1c<@GN8?#ZFx?j8>BNMCF)KM4E` z<}+Q(y4`?D$=&aybONr%o=4f+tB1EMdyB?sf}iWZ#ns=5$pRm<#JG@jOmB*5lBK?< zp?eT#m2k}Ky32c1UVqiW{##n_RzH^|YS2?<-#lAj3aWRdIk|1OXq)C@z$--*hacv;#&V9ntT!S+!mz~SCgw%0R9>t8 ze_LRBsnM;iq|F2T{+qWzXuQqa#nS(F&hNdmg_th z;i?150B1&hY8Nl=>Hgt|u!4CiA%0K$)TU*#$CE-UJAZciGRoViJox! z53AYR=wElajM(*o$3xfjn)#g8FUZ@@ z1#=JFMt5`%b9XR#hDfGR&PZ}9vQt)pFNHrw?i$<&2akLFRhCN9=@*6EdIxCL?S4TC zE4}?Sr%m7&t@b89(rVjsZ}wSGCINJufi;M~1hO zzmjtxPr>AP8SOu-cZHJ0Gkm{q@H_$yAUBET^JCVYG3UZ*%#$^bf^;bI%Tz9+Ck&|)5O^qtt3$vbWPy@3!l zUXh@w54L}HIt9;PPSJ-Jw{-PRXyw>WQQl&@`E(2W2QDj z^@hhh0o@L>8I}7?L(uuf1{xsGR+5S&oK4*iZyAKPh4Ujcuzy=y zBKbKfeO@uq%*2pKvh7Gid)+pp554Fb9YS$4X^!?aZ=Bd1wan_FJRV@sEBF%=sF>XM zU)m=ej}rPH?sdhza>yT|OBj3Ta5oxwNyZDhGr^N&5m&#I8omO*yacS6ESr9uF#R`o zsK$Bfj}NP52|C@zfld~9s5&Mv2X z*S%1kwgjpUJV7USU%zUDg{^qRg@=i)&huMVD`{~1i zxo;}@&7m@&Tw@AaLx~2}qeuZyU787$eI7NsjDk1>!zVf*-YMYD|NY;}7FV}CG)*UTqIxb^AO!Lz35RF`54*o0hTW@RA)sPOMV`7I~SEB_S zT6ze;Cn!Zb(b6gmlQNZ7b?XvpB$+O8Ohc<lLQntvmO*zzR}L)_ zBDW{>vYxuYEgNE_Z;INu_!f#++Vzv)j%7UKx1Zd({ibDVz<8D0o`Qc|{qLAt#|2Fj z%ulDT`Kb<{>YPs!yT0+9G?kth@p+OZP8Mkvz4n98QkfQ5hmuL?`NoMnpOsx?h5fHc zVFx7dRw>asKvamlj@RC>Y5LuHwZ@UFiMH?L0*}eSEN!TuT}@^_Zt=GNbkx!9#_Epj z1$Vjo66qg63b(a@^yhO(EZrh*Vle13FrVN=z+@$yN7#5V)Scgs@7s!ox5E#WoUb`a zihUjk6?w`E16AOdBLZy#!dCU2ckA-VFS(sBgSXoncB_AFXl@GTJsT)-KxXuXdHP-$ zI@eP`MdPo@q8qn!hZ}SrgN2)#ago0iga0}OED6JX2RN^n!TTd9}7~U*Xz~55`(!q5QN6k zmas%#KJ4+{!N0N3%*xLqByYJKfXwm6?zgvBv*!G1Q7o(O#KqV;&^`Me(^zIP7Jwec z;a;B0P%4uv!0hquy3=*F1;l-Rxv(#d|vMDixw z@AvPziiR8@BmIdvd)w>h{8Mqn0F;c}vlR<|HMs1jR}16c(c8>cE3FF)0x?_~eG}Rn zC~u6*q=WRtRjaB^i+3yYY>&>&rBp*UhwvQ!QU^~AYhoMsG+{@Nb_ssrBFO^p@)+kxbkC+J2Zz5 zbmQEo(;}@z&cr_mn_5S*S7vZ65CKiDjS%J>MnqeErwuGMBPH5QKOJ3XrP5FHuoJ*= z>UWg7yMCF#GYJ1|7bC%+-fKk;4(qgg1?X9S(Nc%>hIY{X|qDf{2I8f%zqa#gLZstZ7b9Bg)>d@`}%c> zmj&nlxW+e#f?;$b^G%UfXPV!ly?S-BiE$>QYJm5%s+P|g@%BHnm@WP}{uf>%C17+e zKO2H#oJGbYPt40?NYhe0Ta|kC*{xyIlRMEhH7hmCr#*g3ec)Z0CXEsQv}T!VMZ;q` zkLb-=2s$#d^?-?4rj=iVI434eoVSJ)S_KPSAgtDhlliQ-RtU3Em zkOAlQA_*`|(oO#?cQUFEPWo+^F{{oa23)LxXa;CJp@E(=ScI9PKmXjM(C@Au#pvr9 zCHp#b=!tl5R-Vjt$Zl68P^b+LjmIZSxAEq1QQQWH zBnzJM9b@e4*LLoq%8pFB3L}K-#A8eQTmsH&N`uzJWW_=gkxd#zzL?u4o+XM57 z|KvyTBaaj4M>xuK`j0h4`!jOzf*S90$Z8R*^Zlu=mp@~=SEUo)CT(hr8B^!?>tFuL zCuQI(aFmm4)#;D@B3l~Y;*Dea?q@@)1b$t;|KrX7C)C5oN1Vs?UfPe>FGUuHq8ngC zKUxjarb8bp0uYCU5_p?p*#6BUJD5GORiCX-8?x-;K}Ft!0JsENHDsM% zXk#9b(`jwwWeC~DcfWJUgEEfB^?V}k@2j!&-A(xJ-bUj24SZ;q7CMSOgtz%UTDG+Tg20`JX;&t<$L@)VPlL&aC{~wE?f1u+-@;>tlboqAMPXI2hY47; zFjW&j61cU){l8a$o*_IGemC`yIR+sV-S0*X$lQHLzoS0<`(GMC5u#!sT}}4mx+*I? zRI5Wy1ojGbvou)N0awCv!BZe`gZYZK^qVj6yQhm$+1Lz~8fTMm(8nYVCdQEZt3NVm zzxgm-0VUl`?+f1Sz$=W$WrA~@Ung2DF%L$UK`Ad}s&>+}u5ARwEk|l$E2K)pU}Uev zOH3~HIdlMtk+Rp?9_Ai(@C4#fm3$@PcOB=c*_m`+&UU_BmRA?C7Kd5 z%ZPKjU~6k=H2Ty}9wGW~DiN#S=-a$?7ai2BhZN|wMyf+)zjRgz@Bw(2=FY4Rpqa+1 zXe!^4;X7O}H)TF!c`qGNLC^n|gOhRV;yAHf72eb$Dn^^np3p3Id>Wz^PKvZ+>0Bdg z0)8-{-_#4{leA)kN*Po>~MdV z=Oa%0EBUkjsRa$#3XW>}-*znufVg=A;u54z~3A{eX^3lpIEUB3k zer;N$!puC9^250S=dHBppDyR9VeOcyz#~vNXgM3qGa7Qh;w8L8;IxA^k@P-jP=@c| z(*nDtH6&B*Lk+81asT4&MHVBd4N3H-)*zVUiD;s2{wt5lXQ+)i#PD8 z5hTpJ*e#UzwQr$+FgtMvGn&##2m8o!pGjXOa-jknF>w7h@+QWyME?NA2+3GqA|bw|8D zN{Ge%>8wOWsE)a>3Ft7yM?kLWztaXk{pnk(_OOOOJp=9Ao3FNISl*D_pRB1COctg6{^woof_?qCX>)3COVmw z^EmdW))D6+-Cp#5AB;QF3hyV-+FHa7DdEyf5zlX{m21l2F($kGZlQtqJ&kV`I9m?B z^Lo#8eV1m?(o0$Sp=!aeGLRQFgr6;jOLh>kjzbOrORPvfdl%6F^1kdWR&ARJM}IN} zNk8Zz>Y?Z{ExL%x_n`uxnACCmyN=TPDW&@6V9}T zUS7`t%8sNRqO|a|1t8FGZov;EbTivbYJ^MNVZZ(hi_OcF&U+XyHLLDdWG4ha`u!t6 zg0}D3Q@}>}P9?0KE!FdRm2D7?U%(##d29ea0WY;L@t5N{Fr9FwvlPzmB&9`_)_fA$ zhhHw@^>kYt!;#MVsh7n^?%L-dYcF2^d3*pV>dk+@U6K_$b*_!KpJ>G8*sO=5$pj3x z5e+AZq{Og#BuI+(7yO=iM{ROAiCc%`J0qX5eNW5t91Uyx=YoZR4!?sgi0AM3Xs;82 z$N9%k-A=uaiHM&I_Dd%??G@#i^9L-;lZ4XNRwd#ym4(2nl#EwFO z&NbK6e5OkY@>!+PNNf)%@UcVX^g?4(sy-OEX3V5+mqAfo8-+)ZsG9*Z-hoN0O%!L**Egt=I-cO4a2oAklxc`yeSo3n@}Wz|K6$e`)LI!)_Yno-?Un}G{>j&#Gbi0}`e8COR}3l7 z^QEjBFKM%U{Du4(YMXhlq}5v;_C4q zdXDDyaqW5|HfyS1S>j6!q|JfydFZU|oSXU40;>$9PJ^#i1crZf9ogwwo^vb+oJpNeLc9=(~gFwi_Cm1>J5-HaF+U5s9w;|VF(usHA ztfg)KMZxt}Iq4c|nIb3_-YHa6* zu9L#>6w8D121+Tza^Gof@TAQJqc(S+Ag}U<4nMj8dwXh)eOJ5JA;Q0=#;A>a=wW zU_2Ol6ri*LRw0IkxC&qh?G@8fA(PX>e6C1}E;|UMQ5$|Fb1VUznsV=I!5M79TriXBgLC-IF1fuH`1whB~wL#F;+FJDymq5z- zHzJEK@Nm39MS8JX)w6n%ds;Mrx{=B7V)>j(J0mh3R z`c5wfNQy==+NfxUz3|64ImjxQP5NEbOa>7mWWjerX`k_h=M(1Kstj{}k4K}>l0Y7> z<=jxazl09_@%nvG#>=bJf=xDxkdyKIk&|ug@Redf{>$iL&gfWawulEq(a0(TNvENf z28^W0*_u=)u@kv>lS|=fx=f5uRW7B+1-NShsiOV|ln+PXD|BJv`{<4X^vnOPp}^OR z|5j>cY~Kw4mzF#90Y{d+e1f7;N=tNiZ8<`R1c}u|P2YS}5(|>k@})0yhDEl#i;=4M zREu|Lpr>`HAz7-uqQmV~TcJsT97p|Lt0-P1z8|4qGqi_44KhR5Kj%rBO)juJw(=` zy7Q0YV?rN6Tb@%5DQ8_iI`Vxmd&sl@+Lxz2D9qwvm^YvpR;iK^41@ahG~OIQwtkRg zB}*W4=LVbMS-M1>duRIR#d<9RW@iEZ3rQ^R+LSiWGLJh)L?-VhUBOOxUlueyE(Nk=A>Bxtp%eaLf_lb6erm}jC*}Sz`BER4x zNa!f>u;mZg{%0~BViGL{fFs3Q(zZFzepiWTOJhR%%eQD=3spN?*{iqe`EKicOv)U9_H4Ept?h9eYM}hvKubTmNi9udsin`p7T=$AO zzxE`uaKj~desW7uH_AI1S1v=X2!uE$M%i5XtOu@~TF;{YJ;X)N;VvPNX0|%LE*-ZCK**x_rr@Wk18UD zG)v-O{+lJ$7Ms`quCDM^u0}-SY`u_+Q+jBEAg|JXQH4nJ;Z=QwXyP)7W%=C)JYhM$ z9lyy?X&%&kz_WkyeowgILC*X=)}9~dW<7=_4jx9uS3q+Ace0wWW+v>DXp~;A^jy?e z3~$8GVfA7xODS`DhDzC=*T}uTf6S#({Ja*NoGi&%pGPeOLaf>xzA2NE-)LU`s%6Vw zrSgdyQGzwtI>mx)LW_|h)|^6VxHtfM6h@9zGhE#`2oQe?7;h^7=1GvfNNHG3xWw|} z{ePnF4OKXiWQFCV`{z5Ji<>RSPtK=jHkOcf1N+N%fA-qoV?Mr$JKu%7?Q;)(gVxOx z`0youQSjanUd*h^$<`YCn!Ovwyze^3I!WcW0ole;a&TWS277N!WDFf1Bp#~T59DAV zQI7J;oAOao+BWW?^UTxd2`|nFCU>29RPBX;GUygd&jW;tVz(v6Vwla;2^PRP3#q5O;*Xc!+nA-1 zXclPyWPV^{8un5)$7J-quQNV}0RNDaKb8)>gJVbM2wF{fS)NTwa@de^`hqxAe-x68 zc{5z397W^=4uE*#d*H9%uS)Ah6QE>Qk|J~0-@@1DJ(sPRezN3V8(pyoClWzKbWWra zhenZLy(Jg9M+s-wR(lO%5|f8$S0u7(!aF-^MkA%@bRIvGyb^nV-+g5m;3i6a4~J!F zjc5h*k?hu2de6asjBjYdeFIJxcD2^8j-ShnkZc(AY{XU`5LHLbyK41Xy*;16~_-uNco~Ek9#zK^#pAGadzZW%LdBiiLqL8k;*rB z@_j~Js2q!YDuq6$#pR3v?OJWH+MT#s%cqA%%fq$}d|}vTEKaTe5qV(3h@@Uapvlj> z)Z5_?`r@~fO-s2gy_mJB>60plbyX26@4DIXm!GaYJ2v%p?V7ajx((VSpLfwAwWHt8&&+_$1O2x6D?1ecohzVq zd(8d~a~7SD9e%`rdYEn*Je#DKKIHc?$ToO!Ic|n}hm5L0&t#$%b2h8J<_J_H8?aM1 z@-5724m|WD=&nwUE{WZ8tXtt997=kP5$EYmnkU9J@-FZR;Z>hU(!k!}4^4v3Jesth z4rnrxorpCGLlS_}fU{62Re|-1&c{NBAXA?xDeuV`c&)UV7cn~Rg!-|*p;y0qgW2MTt$fj74GbIt)!uVRWH1~c#0bD1&(VQe)@#m`kK3lKS zGFN@IlL@f$G}IfXEtHK%dr~S=EXP^ls_@l!&}u#1-E*>Me_YCmpaX?pffJP$k@{~2 z?c*_K-4X;&Z5@dF(>unHw98k&rC+iiSdjT+DXF0QQEkl+Gl0%4STf`vV?;b2Xq?Or ztE_2xyCZ0n#rqq17i8k(`9kC4a&x|nV`MXx_5$(Adc#Q#&7XFq?YNH8SuLi!nyUqP z9u=72c!_<#MGMi1FJ=E62Wbkl-~2P#hr8#qG;DYtAXRo;Uo<6()7Zbui(oVDLf3|%?4$cU`x#3VJNlAne9@{P$Z(nTk(*zk^m9z9!ufL$bV?Qxkh!GbPV!d78KG zgCJLmbh)ZM9e?1I4P|E?jF;kjxxt3BFvMs}vGKydT}Iwadu6KwvaVPu#Q)W?+G#b` z0?$OoF|e22q+1dvPu%jwaC+<>#xq9X$n2{pYpSc;&Go|722g4%NOKE}?sabY9?)C5@%rZ$95K*#f?dZT5wk5b>Cd;J=-wv-{1WhF-_8 zI2nvM$S&EL^cK5@r3d!|af6f!cKKWEKW``B1m?Z}j(Jq!5QUBEuBBCLTQQosP4-eN z0XM`l>Lsv+UICCy7F6usS`N7H_>wPr^^d+fs`;;o@F0h03@(=d9n_ixpOaRzn*uvhw7pYR2F%g)gUqLX4O5Q#&nRsJ*ba3haHbqXQV z$}lZW21s(3rQnPQYD#KtnZf!0eJ?}cRkPW@Nu4uiATOI!y6mlK4Bz-E#SK2cF=DlR zKP2oBClj+NF52ReCEzWiN^F-Zb{Jmk-#)g`n@cw$+v7hVHU}?-+`B(4e%; z6xh=XGQ3@{1@0*M6&5SFx&E$WWorEOir^!lH5Wb&p6Q<^e$Er6L-eLSi5kJG&b1$` z+#FS7uq;s!Gp_wrRGLz;c2;*Arjq&c}EicWnKT02g zlB#C}-&AE5h8Ke7L9oj=va0vz)kZ%);KC9@EW}JjQWNT>#d^KikBHy)jWJLR7*VAf zMBfD&Nv{cOchIgA2LHrBUdULNKu$kF(7FeC8p{}>6Kcj9tGtisI<_LX?$$P|1K1A9|Ezo1(EJNSXCXH^}CGPHF>?_n&r zoFAH*$ZOZ)jLS*kZ|mN8jxF}~T=@h&*?XOaD~|e;Ev02&aGC8m1V8ACN4EI~viFHs zPhun1thCciaM=E9V?_Hp*G0(4{SDHGm9@X}tBrR4s^#32@|wAR2v)jbYj!cawHg0S+Hg=rGHXEn0ZQHhO^F2NHf9BkKXV&-q zWzX|lvmWuXyS0kfSSySZ&sbyH-rMZD3e8rx+C$U3(8=`}mY9)ozzOb$V%XbONiNsl zoyCRYkQXu8D=)}yz3vr}i?}uu4aNdvC#@=cPbJuRx?dpvVm!tM3yeWLECsdfO~^iB ztn38W#TpTb_9kHnnifW~S$5$s zrzTF9?q1g#S^Gq2)B!Jtsj)N<_`$4fqvX8Z-scV62D8on1QBs1BYT8p z#$oaLyv=|)zhu9N83jVd)%8y8mo09H3`dChrBI(RpMrjBrksX>_cnVc+svnLI6saP zxK?!bZ_|}*uU|sj?7=qfPGCUX-RpLF%7~&M1Wu5P~y*1|6n$ZYCZUqvSWgQKz){-R&H^ zLmuXR=)MTT*`H{__{<*2h$nn*+1MbFViyNknK@7*ZXRlWg3(vD824SS+WZHJq_fh4 zzMV=s2nEEWZxnq7SoY4obh7KiY%H>7n3?h0&HH<~VgUBJ^83 zFWv;M^x@D7ZF%Bk6z!+7*J!NV^Pg)Fp(%g$q&o zLGQ@$e!xb7X z7^mw#uRPOz)8qZ~^UA9pv7decccW1zsh|hvr!MaZMkPh%KY3aujN)m#5)SB@M~1on zsl13dPCHS|XFm5v8VdBkhuLf%jDqwIf9O^KHd+8v~}iK{*%iS>y^%N#4UfACSqwludP_mla_;lHpF{xKUYfoV^J2Y+6nSU*_uGtaIZqWMV<; zvYfrVd6Ygn%~Zb%n7D5J4p`TB%f66c9j2D`e$emYP1ZF{hc`xv&zIkt64re$a?swG4)r}hjlWqp^f;*u-|P8il@nlNGq z_{yYGFQpgFT4!d}F0$4Mn}6*wdNXhBE*)4|=y~L?M<;gDbs%JnJ&hfzK zUa1Oi`riH{+muf}3NTCpAX*QVTE9_u)*A43o^~D{1CxW#*A*STF{nT37R_?&P7CP= zh1=KJx2U1YJor2lW829!-yztWyxxXVDe<5E5e(%OVY8e!bsb;&G$2jofA3K!+cPGd zp?S+M!Yz~<-%`wC)pcIhE#1CwP>c0>Pu)_$>^FCtSWBhq0vpkcr}y*5^2PG$abXFg zei`T~5webTUEhYNF5|}?pnF66O2?6`fx>RvNFHsWIVQH}ip!oVD#5es?)NOHkCdT> zi)8bqqbzcR!ESPGr?v*6gY*-^t;1HIvkI-!4oL;J-+wVl@{fL4*+e zi?Af`b)$?W0Jb!l!VfQ9@K_NDQBb7RdF_cb!+-a>8W6wafVE#Fp}k5o9p)Z(xv4Ga zTSFQm_;P#G&BGqo$c56%_CL3bc?7UH2x#J(X3HCe5pO!KhVzFcf5%~`v|m3qZ#)W2EP+Q#1=NH=R%vU&@j)l&Z+eOB5rR5# z)x?}0Vw;@K%%z<)P#Q%r$S(ObE>}wx%O@qHk*LBS8bWU`dQ8j0%UoIY2sCtq>_jbg zUnxPiodu3`f70)~&Mba2FRrKdiZa31vUU<}E2U*PTNU=!z!T3~S_#BDxj^E$o_TtT zztalJ170>sCdo1vqC7qL3)ul?GAI&blNp=6clnJ5ot?aB9OXVXO{ zB|g`&C-AxZ^>7rC*^u&%H!|SEg(peeWT{GOzhvL&jKYV1Tm%uAqmBYk~6Fp|`x3K7R& z;pRG(Y`GhL{)iC{F`%(tx}F{bL-HkW*Qp`b>HIzlL~z(Z+Jg5x!-S$-VA$C z)*lVIPG$#8(xY|I5WifwAU~YEk&5ap9YuKx6A(I#x?L+q*kNjr2UL9*xO|L~r-l9_ z;po~PcQAqZ-5J2X)9zGq3#Eh$^QnmnG@N_f zdu#T(+6%c^=ylyIm@U0|9;cNtxB3Em1QY%pRmDIf%OGY*rk%Txh#5{UTV{Eq8MKr} zT=rK1;1(=QoZL1dh&uxb_kW>9srs#MK!~ti2jEu2svr1~U@ax8cu(;%0g{^9#zAxe z33Zl`4pYNNZngb3Gy4?mukMt)eg|=kgkMN%tdMqoNePPsHv*i$*+=)5BG|58#~Lw- z8zi)Ee>Ua7llWF3ATv7r&%XQ*l12CtWrbKSiQ0JSJ?VTs<|JX@N2-osZ6`sX^X4-h z)*^KKKemp_mMA2X!1>P}9n>b`PBW;A#nN&w{Jgi;joxkbd)Hlc=V0+Kq$B5}7qiH= z-2(%wPnN2xig)=N7V<=lnK*(|@dp%kU&+2~$9U$><~uzKW^ft5v<{nD$GtdG{)_nFMG{xTvp$Ng}O zjBRGm0I@I4Gb!1tp%=)F|Y^)Z?;?v?w+sYWV1B>RPgMrTwO0<$A zW(gtYZPSY1q))}YaeI0lF|Mq*W!{j6weoZnxvx6`mCp6!HD}hBtxjO)ebn{zg?R-Z z9@BM)U>W^(6Y_~>@G`njv<4igeNcUbY0Qc{L5|R_EdC4BI@GB>SJ`^lt*X!fYIh-q zPODoo$lLa+ojaz{uWSZ0oPT2Cv5~D>8*U|Z*k7kea!JdT2*8}jFw=Bg(+m628vg)6 zu{r~Mtt{~J|K283wDm`z1EOGm zd~v(p=N>3@66mVFb$B9;b+fka_Bb8?S78Kz@tQJqS8)k@f0v}Ues7FXPgET~0+JN| z{Ge%i9-q20!ioP2&%ipx6CL(YkRq31U4F2ekYTW&=bdf+jCaRS5u9tgH);e6-EG31 z@l|J)H+sEKmBRN^+1OBuU|yaWrxSV_bsv7+IdzEg^{JTaR?=c|?G_1ow;4hq4A zcyE+-KxtXpLyy?**3UQpdK{8HThf`7(dNxf@kp1_H}VpAL-GkCLU@v42DK;1AA?GD zD$1%cU%hl|FHu4sP1g9@dq|wUetC=ki-E2byS;2#^)b+hh^}6!!_LYejaC6r6;Y|{ zxnPVNwI-Wa+b-|MU_@72wbR<3-&&!fNCiH&i@}-68R*-< zX5klBwOWio`-W4Y0|=On-3K+q*n6}+9f_E{N;4D7aknB&5rH3l6&E%0KXh9j!`eK+ zpP#p`QTLX|cXMPIp?Q-s&ZtP_cCQiPL859nK|42uNz;R&Nt2qA8h_R>PxxmS&pVeO5<=-9p zo)l~`FS=t58KhkJKZoD#tE9@kd+RpmaX&gAHU>NzK+r1`qLvX_4bJ^UbR+*#@WVF! zZv*=Z&G7mK#|V%`D}tm-ot7T?=B;2zH4tm_WA(nN$mvT=Zy(K1iyqawM9d;{gp|=O zHX1D7&u*GY;Zt2H9JOjgju7=hjY>47R*KU%4D=p3z>asm?$E$-YV2D=soC- z_Qr3AjmMaq-Pt+UBj4{YUnyqO`;zBqce@OO=UB}!mFR$Jw{!*K?u$;Y<7%Rv6l0=7 z8I(VKa$1JE*ao!Sf6g=(7Y3aszI*;OP%JdB89?#MT!GYF#A>FIwTTU%gRW>545Cp} zyQ1oq$&SW6VZ-Svh1_)X9x!UnzRE3EJe6Ae&Pog3RY=ca7c9eUyIAuJ*it4b*<-F{ z?kvcXSY`&K!d(fY@m{371RBHIFRPvIy9 z-X~vs2j5uk7|~LkMvMSImY}1$dF!vMv!zccAw0J=fw66R8sGWS+N|}uM@Nd|)a(F^&C`|A7EUUKyI=v|F_lDO14mzJ zgpL`L85>FZ<7d{9i#;dUoLw#j74;TzjMkO~r>Al?DP-GL^fZ)_mp`}O>Xxv{@Cqo| zt}4!%)&ccfzs+h~+N-XzuZ?e7Uep|(3P@L@!c zUBbY+Oek4%O2GuPM_n8y--frDdJ?*jZ1Y~TX8eiM7>`@6-S*W&MH$tS3u$}sg~2tn zu*g#L=-%a&3=DPwHHw2dAfXpd&TM6rCc*|Nr&k>!edO|_%D5KMB#=#-RXv#;= zzwSJa5%(js4omQ7xml*Cute}-6fvI#cRA5qC0OEa{zf@OeTYUgN~GruqW|EI37~{U zl91GY;Re%23zIzSx?gDx5APuj)p8jJjs`pL=fp4axTFie>wgUqYJdJIC_y7Fn@4H4zQycv|1}As=E1Sny#!M3;365 zr+rn93JFQ3np$mdNL*NFZ^LYQ?z*DDK7KC!w+DrtKAP`1M#|kVKDLdKU(DNCy{VAI zwh>S-L9R@fI zo++k}ri#Z!Ue@q{tRmIlwYLaA!Jn~Ae1q5QXKeBzKNKtzR&%-C8Xcm<2zK|EzBxc6 zQ>I&3S0tyWoGimxIZIs*zTr>&s>Q_{r2J@oeSez}ZTbp+_Y6^Y%uP_$h=hDY);ts_ zp6g;GS*d)mj@03|W9H=JPzZFYkgs=R4-LC5fdET|x_f5<#jIC3xXok%-?N71z-h}3 zaFX{?N(8GvRRPY8{lt~U$sEn@hJu!W8n^sK^$D_7bqXEk3*Ql2kL_k<6c=TNNK!ZB ze&(QE@>~8~L=xcxz!hG4=0_4$R=Zrb#lm;Yb@=l901;J`r%tGL?ks%*@@XlX2YYKo z!{pBTQg%Lo!#cN_@7zS|xZ7F1qNN1RIalw9A$d_yL0)3H8Lb$_M{URGpO2ffX#w%A z>ZcWNwxFz-k$lz1n9_UxPuW%P77DmGwr*o8o_JT*Dm5(VEQV)@mq;Sazu^auRp(iM z6#F)wewqAb2mm5#&GWu~J&Ev4T+!Eh9EWu8Ur!d)Lb!?&4zO~LInIwXjrcvg3mNHEd$E{fP6P|P8mpfL)UE}~ZU!Czuz zUq=g763?IpkXzqPS6MHepqY$_eh><}b$YcW4Xn{Pv()z1OHNTpQM<1~VQkGKzrmy0 zP7mdJ(<;rl7!T(SLfO)3b3P5Bkwx6VN9<<;1MC+G60GZi76dodTN=61ZLK^ep**zm zR!he`b|I}sP(%^vFP+5r45rR)Y>@`KMauqju6P6M`@Zf-Kd^0oXzl9SVy?(wOP&== zQANu<*v~bv;;N1@5Y1sqgaxR@6$p@aRtQ%N*W~ujdHCZK;A{Z2tUqWGy#(c~Fy419aK1E3M zm<+e$dXc0XW-3uVDkMot3%@4@echThIZ3h!>nDuw4Sp!v#NXfaJk}YUu7Uq`0}BQX zmRmH=U7Xer$3zx2dRB^FBEf36*wn^ewx16j-KNzA5Z7+q3vf|6p;Ul|h=Dm#+$nO_ zD}y|trpKDL=PTmptKoku>az@iiTmKo0t-_+&EJw!7%%8PnyhL<9~pNY9NAEa1##_8 zVR*a^kE;T$FZDg#cN^-f{kPOAzT#t~B2;Nr1@x6O-v-mT@VX1Dh$G4g&?!MM?@F2i z!@Q}+E%Mwcm0yUO{XIWX57 z0_GzUjH#!o4lQwuG^5Bh<%Vqo$ScVF@r%Vb-H%}AdVabnZ)Oh+3n+ zROFLV?})RkrAwHU)D94GpLI3CPv~|mbWbob47u%$`x~zCIdXq6+g=Fw2bb_ zb>pHOdU1lX?qKy@)fwW&JGnsGGCrwu%$|&|0|4z&nyb(a^ATg3ol?yK89vS64Mhp4 zwbfA?QEloUyY;WN;fsGiGR5d*bEX`&n(2zl;t&0Uo5a|Dk9%1j&-WQN}?(es(&HK%`);Lng@u&qn&R7>OR$3J4F%PS{ zsA^i+;(h|p7WsGLs>vJI1MpW2gp7mym6Nngp+Fw1nlIrA=0mwO)QbhHjGr&9B9pOD z^EtjVnv{&LE!9gH4>hb2D5?M*3(PX{roc=7cMM1ZCss-SXw{(Gr#7`M*E+7@;Tf>T z9(6MkL(6e@TcEoE*5G=WGfyt4!krCk>)K@@6^DWeskr2nbWo|eQzy#LG_J?tr|&GH ze6wRa4vX>ba4qkzrj^RqFXJ!h#RCq6&R<}ro#?drR|3|`eBG~Ij(Owk>6?2?ng02& z;e!#6%0IDbNgy2CHy%7yq$;I?p*H+#d1dQeZq@8yyt)-NpdA}r?4#MQtnKeAsXL)B z7V4*elJK-|(>^k1VY-NR_6J~>c$Y<$4B*iu7E9N<`;jtvsDJAz=X?*z(MEuvmIXaX>fL5{F8R4$L@krS4XW3ud$Ukl?&C+u@Y7Hmn( z_ktW^(2c&*!JApZtST)%TapB>eVX|2q$b|JR4eUZw11>GM&M`t6rGouxTlm1qrrN_ z&Jsl)U9Gk2W^{!<0FdlDuHCi@cDic$S6ZoSUw6>F^UGvm;!Y&5R`)nB(gS;pFTt2- z%3w8fqE#E-zwWUO8et<*nSa!z_;;nZ5^#6eT_Sb%cU$KT(-Wh76`RYs%(Uu4Cj86b!R++2QA*G87sGra;ZO+AEjHu%ahH%In|9R8&W!Mo zw9h00<#I5Qf70~_c?FN~lFhOK*Qs7Jp6p>-CE>(JT^}=Olg0Wc3_n-Yo5h_x6%f2( z08aO4KCZYKa$E5}+XfHMB(y69sykZu8k>*(N*4hrO*C&cbETu(2Z)qFmRnhtBMCb5 zew$$sW-8#>N&dZzy9NA_!LC4LhQED=buBA0*&kg;z}iGqOu0w7hYRl^*XIvuCck> z8=~gz{!k4nJ%pEVlN%_j!#@>;Yt2CL_joErqgtvGPh9k?0bQEh=Y>7f|5v-Pc=yQy z#n2Q@ET0d=yuZ!Q>&AB|sInks;iK-Ci5n0eU%@3<++!k@=A_0t9nUt%;ic*G&)KBq5&{PB8eCW{fP6imxdH^^@y%hPhjX*i@fc5 zk3#=$ZQ)O(rTj?a?10dax@TW!+aycrxl4rfKO3PkO=;ob= z<|__zfd-ijS#d(cQhdwe@AAV_C;uuLdAi zNHoiT^Uq^v1;90RLbBWfrstom+FBVP+F$_S`m~cOXa1yU|LGi0iC-vfcHQ!^=Zw`* zf7KsLAUOf0A3V)SBn3%&Z|*{u8r6-jiF*T zzpLkjBZ5sts4NMf8C;1(szRXmF^Pd9rzb@+WI(3eA*p+dGzv*j?hTaTT|Y05YN)>0 zAO6x`kmaGl8}{@ABa2DrF;I8(O;Ay4;i=g7qE|b>I?`22C&Rd#+5EVW0`6sR)Z$Y` z32Pf}?5ib9;WeDvR->W@7wa!pp}Q0fjbi%jq|A}e2&7=t3H`t^V7iR}{eee`bit~2 z?YFhr9xI}v<_hegyUxTHl3fZep%UC%&9!j)db0k_@;VMx%Qg+>{w00C2-tF~yG{QJ ztVBeYRY&E8&20NIhp9L?z-=yoQ*^aq0p}7}{GC_GVVpfEgn=h>YU)C z?0?~oUFAt4Uyx^0+{Xr9LqNu{hi#G&D@O@1XXeOnv>)=jPo{M40HIUk7W36pRyY}J zl%eybMLrWes?R@Lqf>N+>_%mTtA?_l1WA!TO~w&~cKKJ!^S|dOi_w)d;y!PYWSvt{ zTfVDeO4cU!V=s8!92bW=4seyEgKbpVejSc$hDBUw42%D;&XAM?HRbkG)T~VW!$kKi zUE@D_$2&x6@fNrk0Ej`|ufGod*f8Pf^wnayHbMxuyEF1McIUs2iYqHEbjqGPaM${{ zHb_Tx=5MsV|LX&Hceynl^0fs%GRapryq_8tL%LWpdCB~@c+Zcxg&jy*oxRIWHY|mM zKhEyV?WvfU&d|wUzZ^LcUCTc@ZnnCzxIJ?oD^7MmQrhwROH>C zTnWhF?#H7Nn+0Z=udY8;IaEEn+mX~e(*cvnx8GRPH^-eh zF!f+&@GqoWWgn}UI~-c1y0qWxNGCZvtT&O~7rPFgef49I@A5)J94to1r0g|@>$l8p zEY*>zKm(ucD@KycEAe=%fK1nHn zw|MI_j2nklZ)_1F1!Orj!k;_qO@OVV1-Cu&!y25!S$Om&F3lv%2g4IAAQWsNRYR`v zX=yJfLK;OU6nba!!|dx~q;8=3SlKPmAopBSE>hg}=kwCE(#5fuckgRi$Q?|Er#Vs- z;nYzJhIeJ(ceNN#Rxx;`yIxD&Z|xLjn8(?HwECPhHFnlNFiV|^lpntAy92HcDQ|C^ zx3xF7!jB<@0^bezepL5`Ln#CnSu070D5I)|k+giMK8piamvM5(xIWj(`F5t|7Xrd_ zQ~p#J%!P%et#9*BbzZi$tdyGK4@kq-Y=21PM$U-EhH+*Nh*ovABe7w*v&DlEXUiR$XW-yskk(24qX3^6-U5=;dw^gD&Okj2Lb0 zwdBb??Fht`_`erLk|acohT}yoy@Cc#~ztAT_?Y&`!qu9IX8+bFzk=#<@}%_79)P_2w*RbasU>SaPW{ zW7mn(Ba#UkxlcB7UGPO!mfgYe0gQHt{K_?4KV%;xi4!qInWj8{XT>3~7GO2;~$`_-87A?$GZFM2TwsZj}hL!MoHKD?y+0DDfO* z@rC_)SD6KMWNFdBl?>}JCpqqdb)dOp`{9n4>B^+t3!A33?oep$^neK&fa4=}A@O1* zkvEtCvV7H+=P>>3wPAJD!FIF$L6%~#_HMiO*&ulFXph3eMus;fY*6^yDVQZyq>I}Sq&$_MXpU|uB z;1Gxk8^}yNwUi<3X@hs0gPL#pYZ^&>DtG1&vo;p)zRA8kr@;#;^rM@OBj(yUSM0)6 zp+(xXu%AHp7my)@_C&Hj5kv?uvvDW6iT!O24b@FH-rnIXg;>AON0@ z`|!;6E{zQ7i@GFgCGR9t=ftIoVEZiC8JrOmf#(*zevZOyzFD&|!{D}e*$IB~6mRf8 z-d5i+>B96VS8O_Rtb1O;9h${L8*Zi8XV}WpZfu6;_+nt6EwF||>j+v&FIvGAPUGhr zupU9q0$|-NSTz3j#ooAiG{B@~eu72NM#H8WOLI{C8t1d(%8-J*i?5vDS++i}`yMF3 za(tW<(GTr18gc^apO#8IA!1pO^ZnFm!xk$$86qGihgUY>ySoS#^1o_?e<+*~^{JC+ zuB~(|>B$i#jRZ#d*;4^6`jk0f3Pl)*Ug4Fky}eTrS)%DZtYCK(US2wpq1~O#RwZ*q z6}~(wf4OSi4g4%LFF2Mlm>rrgnB=YbC4K*GEq#t&S{P)#Zi-Xe&Qzq@J_U05sX=bJ zDf0HulcglwY7qu-Ws1*?=tvu3;KSPUHdi_b%}$$Ud;L`*f<~d8kh3{-coU-*cPz|2t_L$cq4ekC7?|@oE~&DYZL+|D z$d1Uc!N^lL3-Pu3cNE`WX8m;_Iy#3woe#RsOyGO?O_L@onK%i@w98oK9!0c!-(RKW z-D4Z?q@RDbw74Nw3bSP)5#&9}LnEP^oeB@L6&;yAFiK?Tl@mdJZ9QpAaTl?IK<;mv zu6y6ptz9CdOk|CBN4o*O4s>2Xn725F|E&2#G11PB&?3G>DTc|qck8v` z_3^#ZTa~#$_+9CPqvpHw@?_U2^kLa31BB&xvx(RszwD*UumX3J(7PW|T(; z!sj9mqT8HkZ2m>rIa>NyT+Sd~gvdYS`cwY2TL1w=tP_!xje~J#g*z_MK+{zR->vFs zcM*}aV%F`3zwqcnNlUQNCjC;%gfhAT-+(VTnGXrhNf{uzTVwpPm8-X!5BX zG`%dGp**f|5G@d$sXAwfH9gFR7#m2*MD&#J`r0Sg_0mYO73>N|x z2rHKx_`s}iJ#43)_xtrwY&6}WC>IqZ&Bp$8!^bM-F)(1Yz;VMT4y)v*?^(~^0D^j! z90bF@N3Ith?(pd-N}!?#{&a*ChaLz}!uA`CDG2y$5n?%`xYnp$Gk}pbw@R)!;Jxb9 zS5H2r(7izvHpP>-w(M#pyLF!c`W~=wi|F6ditBQm6TN0nhw@u?=8P2^`-y^&oVxKO zN=2wOQnY>ker)VY~$QoGT%BItUOdj@$2MLL@Txm6;mQ3bpi^4GT4YNf4V2yg$K#gjb>bZ+N7u| zc_W-v(BI|04A}qE=Dr$Yc35;XJRAKhd@(mF7Yks2sqGV-zjh+$hww)BVsgr><`$!; zG56PUy*(S`AsyYlHM<#UU)(G3WC`yJ#n`yRKuYLJMmml!nBPCDqoTd43%&DsfTL(m zi2}~Hg9*~FhCMny+(;%JK?$knK(hFBhMN0Qq)ZXJ&f}+q6=>Q(tX^b3S~|ex8CqlTEzWwG1iEQKl?B^>XGNd7qk?U3k;a(@x2j6D$Z>PAgoX zmG*xdFKyKSf_mX7mHAi4)p~Cxb(L5^fRl{!ZB{_6U~%y^(velE4$uLwrLBLUxVoe?SbvMRyB?)-B^DR`+3(6{#BI~ zB&HD9hpc~JB0eY}zazQLpK<`GxE}kYzVGi}i`HJ!;mn7uYn-{fR|3x$EGubRZW_6e zXfNuel3A;3CUz7evLhVo(j<(ae5n?I9ig&IKln!a@)<=*Cao%W(47d7vrFQTXMmtt zOE)DwD|!?LI|7YwiJf=0BbV>%osMrlr{g?(+0V3K;l^xR*>9w5Z-2ZNAqM0G2c&0e#sSBE$8rKL6iZV6Fr}X0@QPe6rQVLO1Sau zq9_f&nHESW8cA&-ew0htT0wWK=+Shhkoj7j7h7NTVYCA2;gY+B7LY-|?l(Z0eZ?DX za^Ke~)Q;RmW1GY|IIl#T&JUHq$L)FE`!A0ek{i67lHXsM{Ba zklsnDe{~E`6^=)SONhR>**W)IQT!F_JN1Olo}Ewq_3qgm*1wK^tQ}i@()`J}1De(u z5@>g4AIUI0v^+tM4YV~$m5@67B+1B3?5&smd)*7*w|nm1McqGmcn6_wVk{XDek5z_ zLPo#7-)fwpIR)zsit24$GX2i-{ysh{KBXSg9i2wvB~gUbgmMMSwKq9!pdCVh@C0k0yfENte0qZ|SXX!($n7{#J6W1uOY-N8p~S4BaHy#GuN) zDCaB6p$$|JFCG4ZLglL?ez)fSXSWFt{l|Yg*R&Lr!j3+Ag7qeOj74O-PEoe3S_=O& zED0LZBrp+Ii%`OXLZqzgtB|DwpRGr;`A(?+vEb z;c#)pN5c^7*a}8Ev>L0f)U@Xhe^Rh4OuK6{tn)q-Z4Nj?B(EXE#@%d@a5UDU+r@LX zaj^bf|Hk;eov65fFBdL9;wbod>d%?;#9QA)@25`U!BW)+=B(p7st^YXwhbDcHR0T> z3@({{o^0g%;ZFe1OJr)abT}bk0FMekT9e|;pGL#1t!3dLZj2spP$EOiq3Bp80jQ$b z@s~SHwo!A$eS#-uOO&-+Mp&h7N~_XJH+E2%(O0KOE&4oK8Z;OMTMlZo+6h-9kSuef zwkR;IDi3RoV^F!>r9n-Hx&DzwAkJH)32gt9OB`ivaWqk; zq~H8GG08G?^*y*e^P9CmcOm>_)D60h<&eFvrrA|M-xJWI)shh+BT;kUEg1T-Jj{Yb zaR930kKz8OFIc?9x!b?v5Ppx}byra%{Bk_}LOLM$s?WX+5~?2USe6jV0(XJ`eeLjK zQ}_}0qvl`hqmST#ZXn2#&PZG}o&))Pz;It>&Wg8v{A?y6{&_9MZT@Rx@)7jTb!}=4@pSjiz(NK z9?2>fvprl~@v(8K`o8E+mX4Q`=&{O>3P z1{fEzidpoqjp=`Ru!%kd?&ASS3P-Vq6a5Tbul~edL8UAAmMS&vE6OXx*67GwoJL7c za;jAiL28mHR#O~z8nf1rQ05-gYdM*Hm3$=K0Pn|*d@Hp24wyTMzS)!g@`}^SgX7YO zME(bXg@t3Pm@}|pLyV70Ksm$=J@yU8AI!`mJ7SIR5R&dva9|TGAKoT_BgPLvk4SY% z$+$p0aDld?HV}nsDk5UZ-muyObjgzZ3{XZ_~>e=Q3)VzbCaor=M(Z^ zwYu1r4xR`HMYN|8FJWC&PHwTFHPOHHg`$msSQj-(=f#jV*V_-Omzif5tX;9f+sw{> zlH4pgNC977QbZfzWnV8Am#&+(7e+|5o;V>We}0BKa zh<**F1VxWjSYe8Un_QM$?GO~5n?MWrc|jk3hb{1mAQO&?p}l9jNXEYEM6~Ys{)EwR zw|8*+?R4}}HFff$tK zZ*U>ho*L0c-E8ik#e6 zS1I6hDfO*;mUL*eNPRz1Tfj_Hg|8Ns;j$*_uhVCIJZpx0*zX+;Fr5^9g(cY1l6<a-8+;d>-9jU%cUP?>=6`<xsJV6L5Dh^S17GbYK69*JOc;_B1|h z{#4#uUEGJ67xrINon>2`VVk6J2?Px;O@h0-OK?em;2PZBf;_kcceh|6xI2vn4ess^ zjl1;n?#?kY`=x(DAJuo)RdrUw2mWPz%T*5}xh08C_?o2+cXNIr;Aoz&7L-{?VAm+g z+!y|8>Weany&kH!|5-2#B;n`XSUESm+mZ7wx=1x`lGugIGOw*7r|3YIaDawsT!zg^ zyY)B9(gY4d2p!|n1%>*z7DHL2)(QO^ZVYa6As!sh+q{wy?z3YCUwe&K#KO@8s@i*} zQF50e%GjFwD2ks2z+H_u*RE>gCa^(5{Yym#EX8IEgT1>YYlJ}mOZL{LXmy+Lxb9)K zwAo>PQ_@%Y_gvo^j-YiD%s9@r1!^wW%hviprCO;CUL%+Ls&rRy1s=i_8;>qA?8)ag z5g5scU1icFo4}<7n`Qj^W zFs8{@jjt(pxV6(4r!uLza%Ufu8=+1J_gX#;eV_bS5SfZf@?GX!5^1;J4jMw8ODOmy zvt5&R{Y~VZf-%4=WFP!7SM-Ui-;bWuV%)|rRUk1KFsrrOC=0u=j#rR_r(cHsu)Cj} zx>P*{uQE4q+5f`p0RK=mNXrWj z{^KtKDw)1#!l!%I9LqtXdAASt<9yXAo*2|J$;dkvSjSw1pt+~u|J{BFnW7FORB_F@ zd!-w{%31w(_6+%a44xTdS-8p)@SeD}7b0zfKJ!d@ZG#b{Nir=3Nam4m3K3Q8dee4m z(>A{47+{Qhobtl+E!jvHBv1p2xv2I^SyM1MnT_TnhKhEbX_&a)X10tk_!%g?+wqTd z$R=(ZaiYP8dH;+#GU)29ng_o!evbT_CjUW6p^|N1=xw)fooHFES#v4{k{h&jjdWe(g=oES$(j@sV<65pIlvaN=V z-#Udk*elwBoy$_X+9Qh2OBd-$#xA}b7lN0mb*4i^PP3*6np5^=flGPrk@ywx)>rbU zEud@J78wt$Gjtp<7REEX9llb~K8I{5>J3IphONq#t$pR%{@E{mQ{2;=gZyff9L5s4 z0fuN1{$%YOcEg(9*YGi5Fzxzb0R1X_!=K|cqa;+vufV%dL!sIx1 zEW0opbf%=f7BOasT}H;&-P9s9@=Nl4jU^i_&!JB_ccFr3o0da3KJ~xeSc_&xq1`4e zeiXYD_EjYPgn4GK`fr`S`g^$Fu(ZM-x0-jH+FfwPmPV-T2#3-MMN{5$j(xybZ!VZ( zI^+09X0yj3*bQKKRa-TGbkZ-9D?oZDiO+m2x63b>lZw)6j2lX4Lv4y+QPyXW?^&38bLWzYV zoU%TVJiSn2fJIzQ^(X(M>9uV-I&pG3y8_FJTPQgl_AJoagolDYOc8?@aJA+M!4hBG zVa`jDYTyv9IZe>N5it$tIp}#Hgr%**6u(Y~%E3lZvEHZMuQrIQd}>jPwkmRW`s}aa z4VJejVBs>Z4mI3MJ{{YFMRWJH5DDA<5DA+Ftr!xS2{Djx&;P`vCnko{9spC2h=yn; zrAkh@0Rc?glTM6#J`BCud?u^SNn|P9?z@OKsm0d%x1o0dXCx*le;s<8Dim)l*Kc6P z{L1?-m}TlD_npz(EDM^$o=7d3y8!c0lM(Yo)pRb+IFl)03%f}MjRc{!OU1>X2F$dG zBU6fHwYe5e>QALgi}Be;*Fq&Vw8*|;ynLn59tN1My(WAQD!311BRXfoWhBSLt1-?+ z>|i(S2o4JdM44WlCn+}ed7vdVgV@^}lcxiWmsrJNba8WPflJ-}d0K5cv{V_k;&|W&35$JSy40DdDyL(e>#}bN#sr z9ccD@*>({7jQ`@mmOp+?Dg;NY{E2dnhj=f2237eAVkKU;Uw(Q__# z;^Yv9<|zC8C9p5)mV55fV;`O<#g5B$y9B~$m=0K%1NCy1*Xvejh*A-PO)IW?e%*x% zExJ$M?Q+i`b4huAZOMZ8F;3YFGvaLJnP<1a>^%1{9nVH{E>^=V2S2_q3Qf({ygq@TOUFw z1?dkexaPge=DM_ZV_S!Xcui@x1*9QUlW`yDmB3}(y}Ma{hqBzQi6;KDvbFufIps>& z;O~26gQ+++ir_o{fFk8yl`;A5qRq6r(B>E|MA2modu6ia+FU*~K_*zhX7Xo)_;|@~ z{fN)(lJXes;F4Pz-E7l7sIottH~Dd#g9uEru84baI{!`v<_^WAj1X3CO-h?j7zZhy z@7F}aLK=S5V|N=1^$p=!cq!StA!ziA@nxWdvSQ zp+DQ94Iqc{#Qr+t>r22$=qL3u{Q0A1-T`#6+g6J*gixVs>(NI~o3B3W8 zk4~$U9FkjZc)U;t_(-0fP;WGeo<3-SN+#*bYqz^)Q*XXFp!UcG5Hh8Wv*3xCS zhEtCSs5!Rks;~6wvlBS}+-eXrk&>H+G||c!)J~#G_GAv7`~lQ9JNP6k05(X z`TLm@OtFMYOZtKxmMgm`I#zeJbta4XyE&iNuV@Z_@t=bpakkBZ1rek^s&^lgs*h~f zd%;R%p~rcQg+>Q@bFtfgkwM9*_z)*&Kzglxw=zXn%mU!iKL#l-#)O*ha+?x$Q%bSI z?{EK}cS$VDu**+&kr8YqHQ7@3Bok^U6U^lJ8JzlbW>=5c-|y%ETbjuSN9=(_G9}*wd<=J!c5|uvf~I9qrvY9&<+97F+8E;JB0nikHp;yGoW);1w^(md=AXHKxx~G zf{f+WE@19BKoM<-8pa*r;=An}pY-yX@9j2}ch$#0A?94DU>PxGyZ6!KxuIYoTXQ^G zIbdnaNmqfhawhc+WdQeGXP`?k8lEOHd(5mi5T9Zo48751DrVb8iF^nVT@{UpXDQS! zChwa68VR`#CMZ~>$5SlC*n*Xns%zwZMYD_G4G`zQ_9A~;ZV?|M(7HAK0pR+^E z@q40?^_*y%hB|fw+M&amKFH5@>2JV-j<2^JscUchk8o=Vp`IHrG{IGKOEU)jh&sIXM6wQA(e@%0)x z7IcPuuRn+Tp)lteV^T5cuMP?_m#0q764(7iZF4O6KIwy7bp^F*q=pwuZb9JFk;~U| z`AnCTd;DJZD`^g-X6>RF27*M9*c0#wAmwjv?kjm3Q7=h3H!EOERR=j7+Knh*>3Gas zqPhCU@^Eka%Q*e=$e6vtDenJ&h>mHc;b@FqofVwPz|OX0zoiN&Afacf$k?qVr@}W>`*{A6qCO;>KL+!QWx2c0%^y3NXaIk0-Su6=Xi|?cRO7Q7 z=H4Ya@nl&tAe)n_JX?S7^^i195UE-qJ1a-$uNu=CWixVfXnag+SX|~Zj^9oaE=ZRh z3!oYJr&lR#m@>jv#nVuQeBQ+b*)~Z0cy9c{<19i^MKqm0$=NAWtwMGFth(jRSN0l6 z(moydTh-AS>;)t&4|(Kp4`u}&ab(2ar#%(^8K!Tbw38UR(dl&%vNH1>bFllqeE_po zXs$-XU0SrBj#)iW$9)!?;rO!#iCeK&u9QYm!z(A^9BFp)65^KMz zMLgc=cMrInI?48|lVCcTl&-~44u05@iy>(#OhFG`JSE@~kvf4(?v*<$Rq@nW^>&+9 z!sX%JkC=6H{>E7zcbU0+>R4ixcKH2KSSPM#9lL?1mG{#K@{>)Ymt3uxG5GWkjOuuy zlh!C`nn?s4N+g}~7&Nh{(d&hQ=DH?~mj?whm!dE5Pz5i8CCK*6G5GmxIQMbj)!`EN zY~16@T_4~-K6Fj@Perd?0z+9}H_lSQm#MGO*p_|rdecoeuvr+9Z$ zOghB1RX4`N&ytk&zL%b1{Qx$&#O$=t4XR!f5)IH3{6`uF$MLI`V3%absbuN zwg7D2xS@4LaDL@iT>;T7zs3o5;hIKUTo+lJYvn|`S=^KIUbt^8IxN=Gv)bT@v2l*3 zY?+iZ?k$XWK;+M?Ee(}6mj^f=Cq2?)aROx&9b9bfIo_fXjU0Tmg&sJ3!vwW#UP^DE zuI&CcI&4$e(M$b4cxr|sG{@&MOQdu5U%9hHX2uVm z>%*5lEC^yg#^HCq(XY`Px)Xs{>syX}${pPB-KusOA+~(92Er6-w~?`wM;MFYf&vxy zwvrmj%lKF_q8eMkU_Ie>`{Zj$BnHY{5Rgk+ zLK^<%oZ)hxtoK6s&w7xbdJ%>x$JAe&C2hh7?0U=}e9R1H>c+U-4KAvCFQ!W|-*= zy4Xy=MA3T!(e9>4ZM~=Brh~0pU)N8Ms66- z+f11rSq)Ba6r4Kf{yx9n8}f&j2m!7fVt+RN$(_)z6Il`gb{asbyD2oFoLq$51v|$} zNw3pxo6DmP%}-q#kqkUvoD6R_{t>b^F*aVbr4l-#nJ5{zey@?5uv)C;qJgzwNQu64 z7;qwm&nes8t~6p^A8Nw{e;T)k{{s^BrfTqK=?t+Y6cpp*!|~3U=)nH17elUP(x2i$^^b^$d1CF zh$&RFD)}8ogM-o?+}_Zc(;n^zYt3j`W|%Mg7ylwfX4R@SCoUY4Yj?SUEnm$T^9*4j4iKSex^j&JD_YH{j?aSw&y zBP2fh@^_ps7h70C8urZuHClR;z2wa@f<6uR{n=@G&-Zr&pfYCrEiZ0_p^QiBHBVMQ zb(GDPQEbFtXlhN@CYx9uWdF;~TCzoyYqLejTq3AEY|$sD(3y+Ic6!?zku}z{>Qa5$ z7>Cwwsq$p_2KD`qgd)B^ShmL`k6aER>!waQ$@cw305-#|UubOINND5U$OeYz}%6{`e%ao2$;)hBg= z(=7%-u3$mo^aIWv<54AfXy-`CbJL>3>#k)P3rgJDuyby9gdHOJm$<`CIsW%Lu426x=zo1g8-;n z&ixtpeeF&hPkIvUz`gK+Q9LOo!k?68+NOj2?T-UeW!debcmwANGsFR93r@rqNGQk^ zp0>@(MoswgT7%>evzwwvJv&TC&GiRfIO9AEW~=%M_OfV)f>@M$`*cm_S7fjqc;4OC z+nAhZy+@{2+AtO6)_4GlMPHxy5vW#~>Izq*Oq1oB-KQoeydaBm!};sk_`#3YI0JbS zS&z|n`cxBPfH%uzMXL3K<5!8OaQ6Ja9eE}=3I?JVrY8*;Klm+Ju5F6E%efEnYQ+N^ zP4a^R-Qm17w00lJud)2;NmiYjmom(Fgjk63n0=f2SDOwNalE*VoYn;PCxJ!XUEL0o zJGbHegrQDBo_^Q@lWN+r1XV$g5hT$HzE55e>}p;&o5xMvi@ZDDxjr^J2>v0kF@om@ z3l8noA`^v?PbFcOuex9*a^Z)3pZx-;>>Ee%blX|b`K=vb(sgw)I`E+nJwt@KlSJoX z%$~IcDMzU7Elfeqic2493=BQ{eoP2oPh@JLAb0&PHSHkcsk`QL_*TQcxZ9bw06j5O`bl!JRFtr z!Wl7GOFY4~ayAh{HY|>pa>QNxV$nqVvxKIPH=UwKz~4EZrEEXX4p8XO?HBp*`@=uQ z9Q2UIzH#cH#|0%Zt&a7fhCn?w@(Rrd$-DCi*+YT#hvU0(2U3?x-0vr8mBX*JVAvaz zMiad`rXIf;1J=h{8RNm3!`k-$7EGHTn+Rdb0`A58E^EwWh(LTyU~}s58sd0l@&4?{ z`D*h1=OrPa=NnXHS%$6e*}LJZx%O;Ja(47LdymT8&9u?QKUH_9bgWBhM!Djf>(y>W z%Za<`?GvQ@v1}IszRO1_`+dKcn9gA2w}oj(=@g_dLlw%4$scHXIWp6kyuDxr*qp5o z$Ya@;5UwEai4I-b?yn=5V>s0-^pVYl>2zH=JwBHLQL^d7bTU^FVy|!mwI@~|)<=Uc zQM(-#)zV8E4943TQDuWNOZeNC8tz>9Zo-l8nrky|N9*4SZLX(v5h?A(dUlsv&oR|e zmIkuNaFZBJnk_07n&y;i=MHjb$x(WImC$|vk!FsIo68HLMg0%ickTU7-#RkWtg2ZF zyPVz_u#KRjUvzejvzaF`Wypm={MJFOc4&*SjPcD)1G(M9#H#Ny$xMGW!ES_KR|19rB36}D% z*tpqbJg^+`1brQNJ-hrohqGiL*M`f8nau#=aJx~lLo{vV9Umk4BX^@fq%W~ zjstyb;0j{8W3OkwUWi^<>|#RW!I}#MJK4D1?!?rO$r|I`gCEQ!HH4SZTx zh(yZ;H)wip-KB)#^3U@tuz@!F6f;UwRe84ZlT0P%(mk8;C>76 zzJ&R$v0Eh8ew>!4FFPYbnr73Z+a9%AV6s5FurKiUNMw+?Y(1Y4Vp8V{`87VoshiU< z9hZPHoF6AQ|As#EKsosU)ikO*XmB4Z@-bkXs3BfWm%nQ_7w)+JCWX4t@2(ski!mG* zDsD@bJe3)jSWSqnN-Fr&-&jlvN%I*0%?|J3==%Ycb*F`T6rc8p4xc*>;o|%e!TCuq zI;DY|8tc?i!}=y=A7f`VG$eL`U%v)W;~l9T^Nbnfu|< zr$;rWYMpeOC7Lq@_7NfI_B)M|cI}b`*3=O`H=b%JdUyToyv!M_5Y?S{=c)3_8$Srv zA*-^TLE3N?5JZi6OMG8z%=1HjnypoMMRK*%zhs(}QhQ)$sk|g?QxZz`II$rdQ1z3T z%%4r&-r~!i?)26GtOOr^E4c%43}3Bij}k7(%kOH+yM!$}?guWML~?!inLMXTCE50s ze!Nk)`2y2ahlnvnSR-R&6EMUD{tA}Na{HyCFO7SBETaJj;6`rqnfk;SH?|c&Grv{_ zKLuQjjX1bvBm1p+-^lC^7yyQ^y=QM3pCtW-9Jyi9SB6L+(CvMyk2N` zD#Ff&4WC}Yv?}8&yK7~J@zcG${d)H=n&2NKX+GMoO9fz_7&qkyicS;n$v7;h!WNKd zhX8pKxE;;l^56tpYJhadha4PYcP{kO<5Th&8f&-dAJTzS0&hD2%Lu9HtbjOpAA=b% z9gJT{K(9}iw!R!k0K;P4I})EbJ3mvQ$*TBT^ye0b@N3^OxroE^1HSb`RUV6Brt>l#Od5rekQzJjP%_`VPgrEy6RhfEXx;n@nd~KGLtpvh+-pg==5La4!Nwl99no$%%_1La{wkC&OVC;|Qzu%jiL+t$;Rgji~aJ9ox? zx0)lOMIObIyI6-Lxc5oMuXbyE8$q3{onDqhWntbPOSbNSG;AK`{J$+&2cA*b83owQ zwzbjm*kO^Xm^Xg!jc+Bcuh(Slc+iE{jXsO0{N*%TGiz$@sRWDeko)+)GoEbWo%*%2 z;0qu;UocZEU2JYT9{OT?pfqQ=G^W6xcak?RoomPOMT2K|z*S&lcnEy5k(;aEhe+c# z8Rnd4_=l2a4mZZ-nux8!JZd6#>fyR>oD8C&Ca!_+?YNSOX(zjg(PMkO zv*Rf-z^QOG1*x~D3UfWc<>&wDBce%ftpYM>x+E|P3ygI6KVdw|J^Y~9p$LMfJrrtE zHuKJ@*yluNj!V<_AZ2{Y&iOS!Avs)og&|6s-ZmDZ8(7X+-ns-OrkUb9|f{$8);LbPF>rin`)0nGB$C zx+T0?%|dy!<(I_R`@q}-@gii%Kz+J1 z>#;FEA%@Y(BM0Ab9geX@GS*2&O%Tw>W-ZA+bmhshjP^|ifVAP?y{t>_jt{H`C0U?8 zuT*|^v<(nh;1dTz`fJut(jbFq}?Au#-jhtZfzgdUN%9Rr|t z()1q$AOk`h$*_sp9it{;@s3BZl{P*{bVm(`P{-n5*-qxRsHK&X*b{N@`hXoiyjo_@ z{ct}9KJDe^i*i%D0~FHC6*#FBUt{7Sr@#(MY6~HX#yJVN(mdkoag%^9wjHFU8?nF% zh*EJWr5BaHc_NcdYsN$>tTu8zBp5H{tG)xbKpJ{HyTkKQ8TB{QAL%N1$gUZ;03h^MlS5 zk|{{=g#Q4omG~6-xuI-sG+Hm~69ZpoVo)(B>q!ERaDUgQv>XmhUo|T(ku~@x)RC=j zNE{x@uUMlcoq@>5)y5Q=#PRDOm{zS+s4f}C^uPIp+c^oTcZ=!H^Z-8g&SZN&vKXaq zdWDL`IkftT>WmHP)N&%!Y(xJLWyfvsXSB4#n1xOhM>H}$Syu!ijFHhvxirOBE1yhj za=uDLE<#%B^aC`sEkdWS33yKGt}8o=BfKKdB$wUphE@K&c37(@wF&6T zEoKwcF=3i#9W3CRYyjHobAOL~X%qX}Z|TwkO;`b524vc*atvDE!vRX2Pxoz?9Rur; zWGf3qr+f=Xx1V8cAeW8Jnnc~rH$I68vN#?F_#Vg(9u`j!OIob9mJ1`cN$97kYrz9Z zvUk<+yunk&()G8mt?uro1xU*ixpShWlP+vbA#W z*b$^#p@e~<)~{;^7vY*)4~QZHJ5Hx*RS{#|3r&8Wi^PY{8h9YJ+~qI-U|o`VfU5!_ z{CX1vU8FNk(F+j#Kio6V<3y%($LbTu;@0by%Ics=;%rlC8aKvPWvp$P01}~r+UiMJ zE<5BHA$X{!ZvR>f+CO!Df2``B2K5Ucho@oBe}8SAJ3sh56Jk7DNwd}krLT{UqrZHx z8TS-=2f}lt`@x)+1bBSpnUcU*15R%e2x^pkKAduKVXyKjXbmkCnr7;*y^OIpAlW(t z(_j02r-ujiZDIyL;gXB$=GXlh?Xer!XZ3qY-34EHJjd>izl4CPpk5_*9v9FP%;zfm zATY23@<041-)9x?VxpVi_LL_GPJH{hzp9uv5qMjDkbc$>SXMpzRY^7r;J&aCAT6RH z|5mHQW}eg1?Y8o?r`L^wOu7io>PNA-6!nq`A$!siUQzaVRNKvqzd}j_bI5tEI1ks889ZXoT(+||1PenaW=`jGJ0fr z+lFR`10!AAm%T2%6&yYX2#}WAG(={yxepvxYZGP?QXO|FTc1tW98~ zvY0TA)Mzq~zY*ns;JBp#;Yg;@&1&TFG5bPo3D<_e|Ap6&KC2)gXI3F?6xMS|i7cr8 z{9>OL({nz4sw=`h z*dO1?Ms`pDNNscL8$%Tp1wWf$OHCLY&DfmoFQRv6j>vGL+#EHp&9QU6O0Bz^gS=Ns)!8LpQq9fg{oXf#4 zPDQ$@eI(p;b_}+ks0oudxB^vMMnWuLg)&+x@-fmY6b_xU-(;=)KWn?Da+i+%Ay)&53{|eXJ{0In+ z0A@+)eDjS)$=nQnjKQuIR4c}OgB5F+3`(^+ z{&LP^gmzPmJ(#_%m^zxky)db^>6iv>1?4OOOhC5VoFY`eNH0g(v*0xOqWx%X& zoa#$Vi=MzxZECdn53s6+VIDj=m^k;bKT!i=yQ0V4XH>V8D+=`HRC{U@Y^jfYQv5&kVD_*o0{~6 zzfPdlqxc(Na|-%uUySDL7IM-+nv{Md7PamLCTV4KH?v5w#)>*q zRk@YZm}!D|N)r70FqTa}BE;!5G5VR=BRlc$`PnHmG8;|NA+DJKpZ!7pVCw-4cih`FWs$ zq*I-Q#zmZ)L25lFlL*A5(mhl?l4&{Gjz9QTrb8OKN-`nDvH8Wa-~h9{l}T;^^ot=VV@YQD^$ zF`Cfs#7aoahc3UP|M-IEEO4Z$nov|w-a0TA^}AOte5aUhXt^Z}y=jLYsZHvgbH2$V zx98p3DX@%7`hvdt^Dkl+VdeEU7mje6O(ba=bk6iT7>J;oK5m8lZ&6HQd>r)L*}xIB z*>k05`;nBIJAQ_leTIC>*8WCb&3Pbda+v`az_9fGHDkH43(cRmYPJu2oL(#h)I26N5Ctk6*~j&3w!xfwb7y zL^?t4RX_D-ufW*RzS*U|PuVfH$y>bC**a>Ek{0fFFMl9W>K!oYRljyo)rm$eOG&X- z20!i68Mr?$(%|=$$&92o`KD}tBt={k&)ATL`qx9Qiwv^fU93zR$w5J- zF1D}3Lu&q-h@CXc+(7__P;>AwDrhuYY8#aX>mBKZoTS>ky^f3ixbTZyYb@l4JK>Nok2FxL zbCr$-C?TtEwg*RW`&2j{a9&8=4sS72!a*}?;wH; zaam(9ga?#4pFb|dd5c(vaWBuyC0fO*!(g6eLo52zAA1B|KS3>kAQ`F4L5up9e~l^s z8tY8gq$9-Fg}dzNd37nsx2vdWuLRwtv19Z_yeLW#>m*9%?TrUsT5t7JP&l^6`e#P{ z*&pd`2i|5zEQ?6p1jj!R({gm9<5`*9bgr4Mt2Y7K z563J{PJzJqMd^c>9!AX4I3O1S`4Y?Z%m$uxL;6 z@;?0uu(8{oqwD2kc!*(_#RQjLjROb>Pdw8;{62uYO9|RPLUB^jec@vdAn;2&hpaJl zJpQZa zZ2L92y$tY(ZZpiM6a(j2Sv_+dpg2oad^EKofHgpSHsP!3UG#uT6xXf(R=SP4&}N7` z@fG4->=;ZqpJTsY@_{XxrZ1+lVPcASDAU=LK6qGo&&V!1@O8UPsO3S=pcN_~^HuAv zG*~G2V(~yhJ&tMEh0~fwt!&h{ViC{WM~Xw)7Tz3nsu3z_1ce>Ixka9zB)+xaWqZWAqcJwx+RF z6zKG#S{@>CSoNmGZ_~P^p5-v5DRU)Y;h@F+z^q4ON}pMemkl4hcEYL&N=T~f%J_-_xkA& zr}vVxW`TszI>Ga(d*+cT;||(KPBe zGRtkOKb3H3u--He=|8bU$57QFkZ*6Zz~Z<4#vad#(xT|&sJ4MWegOHtsb`g0Cpk7S z+=6-SFYh+)pR10fTefvDm()W^5>cqHuWQ;ORoK1$y$wD0b*lLNTnj!JA%1^*agf`k zdNCkwgcE~um}t-Z=Ri>7I~-`@(qu6Qba$b}srBYw`#Ion9nj?P1@C(;>X$BvxO9GHN$HeHYdMpF1OX}UV zj(&q_Yr0AU{=0m~7-I2;&wmXQfNSUjG~qw3%}dYgy-bJKl2%5~GLN!19fHIT=e}4# z5g!^HUVpUnZ@l})@2tGrmXt5=HdvWcG}&ja2+a4&M zdO~vD_lhvM;@E1EpgMKa4}WF5%rv8_U82Lj)RM zUr{YuAj8UfL4CDQ6N-AC-vKfs8Jtkf%hXy!j9dw7BLJ&S&P!Yu3~HleWTPxK58fd8 zHh|qjKB&cL8SXdvouey5@Em6>`O#L+0g)AiZHL=2zkrcmzQQYmbKs<1>=mZIic)2b zgdOY|bixhak@l~V@qigVQK>R{G|MCVXPqwO%ak?ig+Z{C=TBH|1p}g0m3Qr%D0YTi zUj;a7c!0^@(@9r1_{}{KkqtB%eb#!D^j*69RU?GR`X{sFwT(P)KZ_6rw3g%&p|pg- z>wrlul)Qt?zNc!#W2_a@#zBxLx)^mnnEFHj+*5 z6HT3V+U18+d-Yo|BV<^( z#;Su1HJ2Ity4H4-ojdL}>wU?;L*>I+nY89n_Bi!aZc|&<9R1=DYQZBjbYSNw#s4d! zgI*@Im>l_|;-J|42%D@T5E_kcf)J#;F;@<;-)?lqS5?#J_mAAfbB&L2qEf%_`?8$q zyS@u^H?>c;YczGJom-`7WN3Zb^VBx=H$HReM2*pa`i~kk5djt8ZVy>5{Fo{KM+gks z3>M6gx^{;cA;j~v{EO)9zn(rK7nY73Vz)gM8?jd$gAkEda&!W5x5fzv62P#Zco6|GW^IV$C$uZ+MV{NkKI&*_)Z?J?0syf zSFrHq8lqagTE^%_=JMb}dOud6ht3N=byR0drM5k5Q*=;@d759M63GUd-W82>2p}~H z!fN&Bf7!BsvT&IIT-XAZ9uIS0p#p93%{cWna>V_KB7L?vVNOy zAXM;ew8Jp?o)ua~+5CJ^5Idc2xicdsdaNbWk1m)`2&Cekn|VY*KJ#SzotbXm`tg4u zWlf-IziXxKTP9=U*$zUJEf|;UNXhxq^7|Nz^tV2?)Fe)tDM3X4L3u3%iuoi!{G7|*T zHi*3_l~>wb!B~xA0nszMlE{e9U#7O~Ip-hwfvHzvs_9s{A@OEXr^YAJIjx4lq04o< z`uTRKy$#TRWn5x7C4+?c%=fLcfY3KM1_sHcH}2D5o)>1}UAnt%Xr)^LzDJP!+`Ma| z_!^5*@XC9w>%i=vF_HMeRAH`D7~P&jB3pUxNI#^%2nBMri^Z6^PQ$CGP7*RAJ5pKk z1cft33$LmD^)cnyX(&S<@ebgAREu<_9+=^L@)iQ#cIm$dNKHfYA_+wc3VvW8;FPiV z)s*C^;7*0f!6%H1%k_NMtB-U~uJQ<#-oCGm>53!xwEEw6Vu|0a4VEna zzSVmmr~mNXK6fmGyAYGtr``>y5afZ2;ZL6ZM6A{n+7e*`d3&8Pqk9V6JXNiv`%F({ zZCIoQ-@(w`LCiWkx%726Dpz<2!XF=jkfD>@b3f+AN>CG-xVm=2t5b02Lcn8~$lI7? zLRIHQ)6g=jubkgQ8x&$kcN7(1&0&G3*MbH7pk0EJojuR|zO4ez--OCsW=cN#VSf7m z-oG{bAy4bKj}9c~ugI~l8LTPn>+v=df4!btqdR9IKVGbj_qOY;`@YA_dDB795h(k2-=h!bw_G8<{ofDsW-uN>G zwa5bCioWB#B6PSC5fg3X^fe?Mznvmb>hvkF{^N3#5H#praQ1ngbX)nk>ryr; zDYAOdd|52PJ|L9}JKNTcV9@Cqepz3Yt@?R=@toWIro9&>ZlPE`6g=d;nA|&v8XN8U z5Z_Ei8bq$_PF#Ds_{apAVM+G`(oSIFq%;@T?OM( zadt_+@R3$AnUguK#9FI*2HkjeSb7jr_o3SeN7H*K7qWfKq$RPPaCun7Ug|DJEx0`s z3V8S)u~{i#PHQiz`%g7@d1i-tqbv8EcXqCXAdKjo3&QuiK#k*xsTLFc_$&A_`Er~G z^}3VEo5uEL9|(G$x9-nUo+F-T(Igf^*D)F04k)c*8e6(j0x|j^E4c`hrMf?K{?g#y z7LUCmygAMz{-QEw-;zdFUo^F2Q)OYT&NsfJw@(`E@NE8rfjq-W=t`FrybX*IeeULT zlqf8q?9YLJ%C5UR{0%q~a=kXKiCnHGXRIVvLcdTO0V#PPZ}KNK3%{+Gs*t+!;{q9w zNb7qT(H|`S-Uf?meK8y}W}?@9W9}3WJlF-Kbn)c{*W!l-+IL(|k3u4u0c+%iGFq}l z$JvcZ|9rs|I-9zJ_`Zp+mNEZ?&!|9QbZhf1$)cZob*gJfzfngx_?C0NjkiT*Y3m5C zuPh$70TZhI_FfG!pGLbBVi;3QJRQv={NKaj3cOdow4M-8{P@e&bX%mlE@-mcUD0~h zm!N_)ehTT5=rPy5AOBGf(!cRhwq7*{eaymeuCeR8cPG-nL#^Ch#|>sArVH-_^w=kZ zfx2YZ5BDG1C0!90hmP^2hexOu4JZ=9 z2&zN$Wm7o?j|k6!bLlFGMc)l}4|Jw)trW%UXLlTgMkuOX#fxCL*YH5|5&?Ly3g_%) zr2y6dB}x$F9gJP%UT@VP{$I8c4g=AHp!kIY}=D8F;L+!OIPsmLA(<(Vb46p(iHJ1N}% zKK&BiBN=s`i@C1kpOJ9ER$3fJ6Eq25h5#y<{<$=7e1v}c8J5pK9OvLyV!&!A=Ka2n z?UW6OuKzCSyrSTbIR_i{vwb~PwHc`y?tQ^jX)*S{v%G}ZJ;o_8q_Ma z1GIP#!`GkB3&&?8>-XLByH}axB)%7E?>6mio)oHlKKt!7R1H@F*Nm5&+h=ITUw|Yw z;hnLL)SWdOL!xuvhuyoKRphh5D!)s{%b_|I$-T|{XYK<&9gh8vq?j%$*lapiRg?G|^$LXGbthdo6wq?oYmjyd0Lp)K>C= zC^#s4+g0~Yy$<=WU9!B@c(nviA0Ycc3a6UZbhvp){csrsN>&-q9mLG_GjvqpVts=h zcndRUvl0tE)E=5+?~VRZ*pnliRTGH5#73VDRjifHRPM6aa?(LCD{~_o_QmQk%0#=F zk*21VPS9jJ!Pl_r?swnCPXfuz+vus~Gc$4~tn9AaktGfPAF9qWsE#Pg);NR&C%C)2 zYp~!RoQo6OT^maX1TF-3cXxNYNN{&|cZbQ$n^!Yab?W@@I@PE5?!DHxK0fD;u9Nlm zW&jIO$8Z;M71Ce+hfwz=CEZG7b7p_fXY%Q6_*yDh{?jtX%JudB?KT}5cVjhLicsfR z?!7J9r6K>wDSCk`2hkj844S8k;r+X<)kx}Cmu8~kdHHOw7u#c|;`plSls>{&m!%#9 z*)?K*!W;&Q>j&g+MVDXOc|yuS!BtCXB{@9S5N@`7`Rq=}41zc~y=I$j--OuU#YR8s zXK?oZa&V0_!YCnuB0cw7qOHel(P z-!$(a`g!CRu!3vhG@hRn!7$IsdaV)1uGNsaO}+qs$sG=S>jqFKGMaR}b&sBM4NCk6 zHY>Me;_k}D(4*4RCA-cm+L5=Bt=*yvmIbFQ^``JWQ>Zqb{DQJkqku`(I8MhYA5Sh} z#LBB4lsE&LxBZT>mvthbO83G=PpX z3=2({kne#f8QE~yF3!P4!;@*0X7{AfFc_SsOMn0Ilm^}B+u1~%*w91qwZ=cvCnWHI zQdcT;*l<_?ZyPsPx&Z}dqROL-aNBUDGR3dLR5spK_fJ9R2xEVFTA1(tuEKej#xRY5 z7+Ek_L3GNi;7JAGW0CK}ly=FC&4@qO*9vS8_&K3-SFcct;;7(+8jtYK6A)=qI%hPw*`bud@EWO_oo;)bm4XcB5~5=BA=E%bmSgmTu}u_JDC$cr$eHD^C1Jo-G;fb!ZIM+ zCjEJqLW*Ax`H3AAXy@$|>u)FXyNgd$vX~rfR-B^p<8?eCyIIsE1j7BCcOUakmm^+!g^KyFuhX z{iOVqb%VOxv=JYYYUVSnPOgIl$1Q1v2r^Uwh9aUqMCJWHDpxg}tdu&l`>ovY?z+m2 zAY#CZcDA#B$OR#*SyUYdH|x?q?AHzc(=Y=6tz#7C9WAYAGxj#<$nBF$jZAAjYO$QD zs`j`56Qk(U&il=bz4%wyyoofr#O-N>xum+R34>XCT|J&xWgU&a%Cc&fzK={A4E&3& zTiq;USal*3SK=8Gx>ho(QznLmvx)^wANz;tI0@OR-d$9iEb84aBwDre z#bvme7!>_!NZS#665H#;MrEVu10Ab5q86)Trxbm3{mpQ!Aj&*bf8#Y7Z2%QHqiisP^zD1l~n)Kn$U8xO`@K*Fp8@$l#4+h;~4bh%(Wg z=)+L!fSP(j$UG{?4(!&y{L|?jQZ(i(_E3J3(`N6=fv=XfC9F;BwuVt3!gT0GzYJ}` z89sTq|I%`yCQq6LKLRmQx>v}o9GNW=Sb~U`wm0qs&gmVsuJQzGV-bab0CAws1KV~N zxNxPvh?*2%8t;_|h@oFf5-(wg8b*Of$KI3CpGTu?(yZR|UH=#mqqQ@bzk>Ui>D>5+ zU`nek=PXN$lmp1;3fj~J23p+(U@GCAgpIjNs_dwpuEyIEkdWNhPaA#@1ly5aJs;v9 z7Uw186$&J{ZE(9+IA#H4#xhG0K1!zdBtfJWt@vVtqlc*w^@5Jl(`i)Fvz)jX^`q@$ zzto^k0tigaX!>iB#mCF{0P7!bjh`-XSK~oYg3zaxu87t{Gh>M65|&#um5wToX5F#w z1zVn{HNwmg4d;2PmFNyir4&@y_N^kTg$8*73RK3k4s-Q^?=3)p{s%wQJ?kqLyVjoP z1_`~Y=SZZ?T_Mz)BI0?Q?c^UpAFJAN3C7*1K_&*7creRcJi{M}#P2F`AWoSvR~R#B z!^}+l#8N4nlQ_6POuO8a zuw)EM8tHM*R`dp@YVu=9KHy5i3Tw7 z=%o8;!!ZZzACo)>8Ko~{_5D=a4<{~bZZ=uerp>tBex<7prE%?9unoEjc>x|wRF3+Y zr$nwex4S(!E2$$-J)1D1mD@#IO@(}9>qoF1zt`JexyhIWN8q42J5U2?qi+F<8abqHx*$L4lkL=T$e-c+#Aox=-_iVPH6I;2Zl_wWHr^y2&kBL*b+8mR zhOI^FS0*njzb7OK22sx!ST8Fts}1yuT>O*T@M$S^4~e~t6@7_N zxBwu}V-RQL{uq8lCLw^d*t?ckhZyljM!eeO)|{Y zdNVAxHSh8|yFhf7u)#zMDq zHYd6FJW<4oY4oHL>-#oK9fnF$xM`Jx&B~rhiLH~Y<*comU>Ztsz?XcdDpM^}U(GKT zwtC!!_44wKcVKo(P_=Zs7DSPjmbxpSJG8^(GF-iFj+VqqGpaPgxh{9bc>ZSXC!=lh z=a*58<)NiYH_-zOIQi>lvA&iC=@6s+^>A!v@V$CFM`z9wWwvP8kYya9$9Wk9vZm;Vq^*hBo>Vxb(|yG@-_2w zYx*i`JUpaPJQLf`?7ik)6?NSFH%T7i_ZuXl7{x{i2OMk6ev>G5d^iO8#4(U$nJX}& zaND?iS563LBEQXLg4K4jS11b@mDno2%|*>D>UxG$w^f1bsrOOZah~bfB#&2{rWXOD zfZwEVDgG2dy~-o}y@ogK+8xS1=pSxg75eu}9Z3$Zq7OC^pUObaw{}H` za&Y`3W?MPB8y|w^;Y?6H*h-2G%~TMlp-igKw~PxQ8kB>9zJqyK(8&DYZnJ4%F+@`p zU=r!{OWDQy$I-W?Kf3DZG3}OmWnlcw+b*?_1QANs+F^SL9a^!AU|F4n^q?|0h&sU{ zw`b><(SBTtu+FrPAx8BfL)VY0qc&$0GY5wR-R#o8G2XP$K-x?+3EWn6x1vwrfGW+4 zs%sB`|B<;*pM{KgqFlC<;N5FOyU_Q8#;{>^Pj|w{*49V-lf)HJvPYb^gA3&3=e`r9 zf+y9&+$*Z{RT-@*zcm}kbraDrVEe)$d(zX@a-uT;N3p)m>uTxU;U zcPdNx-^3|GT=;$~Fip!YCtW`xQB)Ajm-%mjA3rvc9=UL+7WtRJf?n)A3~KjjdV^B} zHA}|7yb=79hKT38*q3KwyzPbryfXIsU$sqZW4H;RUkZ<>CkI}qgUKJ%SkM!L_8M=D z1T`OKNn>fpNuqKy)oqOTFI%Qm7gMCSD;gi?Ei$9dMcYv1?Csj_H{>ydell$bFbseI zGYfjMOzT5Xe`^*9m4No0a58=<6>a@b>`E-D@7-yc_h8j^*}HsU?Hg_c;**^AwX(qj z_4F^V(vlvhndmtwTC8h;<7B@SyyEi!vX6{9j1KV6r7VUA7RcntL%O{*zrx+ zD?I>dcJl+z#3z?~zhc*ZYNz-haC&Efr2HQoksea-0_*ke;C}Bh0r@>OhVJz_H|lgg zXj*5!M5TqD|Cd$&pL<3s7#4-X1u8x`Qrhd{*~@FM;nItKJ#Am=Nbm`;pN4x-YnHbg zKa@IZHfz(jKKFvQe&Iv3$$>JfUZtF60kvhhivO4~8?q_YC)G8FoMI zlv~YR_;zY3^LO$ASdceftNm@(rRkfpHP1%N!?5KGOQE>qov;&iyK-E6?K{`lQtHa2 zJU}l<#Dj0O2qqp(qM6vN{C>X%FlbHgM$3OQ=+EI(pn#rx zw9KDjr%h@SW?O5@Z9{gH^vUD!?JB$On-@;z^m3V(DxR*`fs1m5V_uNy{Q-y5n zT9a{vtAznf!AQ;A)dA>vI??Mq;b(}Bw>LQ-E9lco@e&HY+*>TR;`&57!;14xNF z1I-rG9=46@T0G=|_qlz}1bYDSNd8s)L7`gKW#!v#i3aSX*kS?KO=A>^@atyug~KY4 zXvVt}PNi56Wu2{UluD58NdBnl-i`szQWreK9+ofP25J_I12EYN? z(ghoI+7eWkVtWR|eFYOkV?pJR?V_33KCH-tf#)U<-}2hGRH6Yuu1aI+`O~B`F$?RE zOQK&Mg@Tok2z8~M)}8nN63=|h+lSZ4)VeFG;2#`PijX&F-L*qSukC%Y6Ze@I1HC?v z-n|xqDSe5AJ8w>;fTcr(6XS}H<5T)J<0G=_U(G729PDsEWfTrSpa1w&i~mlMLSq`3 zCmtKbg%q`P++JkZ9%W@#c$pEe%zfzn-r5DVKQ84WI@7dd|rU!~e9V&{bkR zHZ{4*RKS5FP3ji=Z1i)R=g{b1X3XHc=LC=nbBv`QT1W14r?ep8eiMyabTmiXZRIi~ z$Dg>*p?mY^2F$Y{+v__`^hfSBe*mka^}zT&R<#>7^kn=+>5eN_u}60H$*1d0pYc)| zULfk>AAt+)4M+*dIdn-i8#hrZUif4fMg(+!BvwJ2w$~_<-JWR1yJj$A`Y}z6h+m3D zR6agC$`tDmM|9&p_;#!GD?TEPR@x7noK})Q^rfX|X*c;_@0YfdOi~g|Fk+wU82$L` z{hp(#gOaajW?N$coFXGJNYY9e(D%2v)5k~OU0Pz(2pASj)ANZS)V2JaTt*C8*c|JT z8=UyS#8vR)*mspkYgK2QuEfVcBT3DTzU2bp*E#h7H~*AwtHEDbPs#<2dbtD&uau1e z&K-zYg^=qmYOs18ltdQvI%72s)mlS~&vYx^9oZRo+7Iancra@PrN8lexoe~8gd}RV z;w)aeC!ok<5tB>l&Dg+14eO{t^F{Al%HK;?a>^fR9g0vgE(nabp|C58Z>+;bwrveX z?j3l@1vkX5>zW82$*303m{81!E`*#Z^Pamc*eB4ETfD!AzC}u?whHDNGAOK5eAe!@ z7sC8LjQY^BtR9B32#({q$13oSDHg9W9XgvKBL|vV2HPHZ`~PVU)!$9>V3{t@^Xz-3 z!t{>HanfXQQ4%|>9N_khdPhxhgN%j*#qhPR2W3zluSQ`E)TYe8@;8a9YDHwa=Tp)9 zK*p)P6m$Bel;In;@`CC&37rvkwuCy6-OZkZ!lKi-) zEwuB~n4Ut2r%c(OH3{Ua4EK}6-s>$_E-_y;!MJF??;{wB5nOlvlAr%hD}2zz zIn2xblUKl00<}Zy|ERS8p}V!EybT<$|M{C4L>u`L!K{938squ?E3=*Z)Lm}Hq|&E< zmDVU1EQo|zJc`ehIj(<|o#3rijoqzKLLaW~!K!(=5&Pq1RX+Y9- z_&Ikmd;Dr1iXD4d)wUPKQKymZcZ4IxyG83U^^hxL@#Ha%0R;iGtzw4RB|UmyoqAL0 zg*}d>B0Z&sKI?d9&5-K_%idWsObhHt>~Y_p;UDzi z-i3Jq7ukb+sx z$$s*5^vv#m1Rra)#cz4rHEHE3e}BjxgGCfrM2a+k%~1TpLSl!32N=WOO48Ot;JDuv z$e_WPUL4k?^cafBfqv_H?_sh*CufR}if^h|`oD=DlEw6!ic!5c%h1f)$wTqO(h)jj zCJUT6^<`;?Z21nS3_ zZ=#ruzKDH~!_5u=)%=*n=x1M8Fy9qCn41cntlaT|!?-%JdskQcZoc2Iw37V(1B^to zY3Tpm#tn&>SqR2!BxXECftQ-UBf1TV1m?6n{T_F3{|jFYD+3Au4sNQPs!n|gOs~xa z8ka%w{DyyEyXlLOT`aDczELud#N14pHM_mC%0XXwc0`|)nmYOY%_?=Qd*D%Ge8P8g zcz0quwZv7|k%8y5@(GOW^Ts=GYo9u#5poF=m^`12v5#=kO+C%immnjOZhcBGhZ5P# zrdTZ?UdJ6G?o>qO$6*(;JvP06<3sfXK+D?2nxZcm`XgT$2TIL`kEFlp-C~)V!oIo0 z>4~3N-F<#_;wlUJTwUTS*#spV4eEEmIjwYxvU6)cxUOUBdD5sq`(zNA?*R7$GWN`8 zt&e$Yira8LL%oXQ{2fG&cxkWrl5WL&`7pgD#U#Elk709xhB?nnuE4Vj!`*Zn7nE7H&l8RakMVx$t!b0`hsp0>2 z5b+~{SiYa=(o9CKZ`c^BYuL5D zZU{H)WDY~dMVS0AXRY{2RnigUKaNNESPR6it9K*@i=<_buvMrwmL67F8E595niq@y zZF$e~^|~ZuP!=NDOch$>yl=sUF!{McPTwJdwR_6#JGwBa5}7NwC3S6+Y>`5Lbb9{ks!lR?N~E3dUqqFZ z@a`jE{n?GvgB~O1RZg$)7#3X@O%E(%EahUg3gYoYZ*1<{AA#ML|7E5AF$IEFZ0p4C z^EH@=$*kQ3F52$EN1g^5xn#_}e)Qv@+l$ub40$)-KOzR+BE75Y%lW!!SI^hhs8}Z* zQ_cIrr~#+fYqMy0FSD7H+HvU$W^LQ5OAQ1a(ENkBQ#8|RvshJMZ>E5x+QhORbtIZmY3)oz;+8|a9VU%twRt{GMjT2EjSM< z71@VJ^?9lSJ!@#{wnMNQI~>W0y>y_Xj5eh21+QzO)Cqf`XGl*5s(Tp`-R|!N6vv

    QFyaMDtu1~{x z-Bru*oAm9uqrh%uD7k$6MnjCWK1v#O$&3V_iW#e-yQ5NPxv-WognEhoDEspjNOr$`oLQ&7hU1*F-qK$CTXeh4ts2MGBv+In#f`>4z^fyoZFL!KH zzB136joGgbU)`8=IZ$t*AsvjdGT)2SjdW~!gU^V{x=PW(t6~J5xC!aqfXyXa5ZWw2 z2cK-amD{HQp?~-SD;5Afb{IaX8cpAaAc)%v|MhR|%R+jTTon(0lt85a{ug^MiGflc zZ@dd;d6+b4wFL8t#))=L=U>+sVQ+C|zd#XPVn-TVm!O&!L0(Jy7n(&Io?F8&oH5QG zaKE;_!>I@QVDu*t4~8zYBQv0Br@NVe^xE4zEVj&F|7>|!N3GVe!5R*Uc6MjfejOpx zgXs`!>8FFv853$DSdiU&x!{}hm$n_OB?*sj;qHxfHL?0u9oaELfg)V$et!l^MGP3) ze)XP%X>_9RsWD$;!Fap2Jsu>tOJ0R2C_B=q?3<3I631r2$s|G~!i(>|kB>#b8claN z>zl2P_Jb}V?Igba!Znneotsk?+&!LyZ%4?dO(Xx@M5DhuhFg=&O+A7>@)Lms*b)qF z0bR>FKlm*oW3d{x9Wcu3@p-#{GcvO*UV%+(rD7|j@J4L5^C>ePcYBJ!kYJZnWpQ2T{Tzc%)Mev`Aw`x(hP%`d6Eu#QqfnH3_A}PEE1up zA^AeB9cjb&l>fp+bf0Dp{7cU0CUT&Rc0npuY;AsRqV;Y8mglg%w$}(@wKCI1F)ZkB zAl6!*-z;s2?K*eDlx-#lFooK_u^D{vu>oaY{0j-&{ZC!sy~z9A0)TQ1A`?o9SNa{g zeOhN!WQ8>qyl4?aod3x(s|AT%@h6ON`B%}l^K_95dzg@p?NZh{&)u7&qFdR!)|#Mu z(8BkEBH2Vr{u{eVCXws(z23lau9Io z@O(QA`?@XOAb-_4QVLY7n77vIk0tYN{wV+a%8s$Pc&6T|1JRygzi&1RIJK%)uONDi z!~4k$^7x++$5$FG<%%bU4w1EGHvL#Lbi`h zx8Ed0l8EQE&I^{z`y}%*xG~a@N4+p$x6gw^iCL2zfy8Mhn>cAd;k)1Y$QiE39C*}N zA&>1{jx^F4!DjK%!_DT#vq(0MU$>MFP!Unkg#mFp36zKuF-6P;M>xU|R}V?8O>SRCyMA};ka>q24NV|& zIlC9&Kea`mE8$Ery9)Q6fv2U`NUtOB84Gaz*@HpF4IPhQeELhA?TcBC0yS|AmWF+i z^$pKaGw}s5%_=T5vw$Tq@{Y42k$ig7&E^aoQMZL*F(Q@siZk;hH7Sd$TdZ~~pQ^*G z{UCxXceXK$nXdQR5hVZ7tF5j&f`yDAW9=g!YY*F_n^utyrUzIp0kYC zDlJvwlXYr_d`91UQGU?mbNZn76`DKeaE>--oa!@;oi%}H??NJH?sO4gh^ble!_6N+ zNssEK{>1sd{3n*$OZ6AAg#b22tIenK#zK$YoZBoBR(t_W0h<_%0hmwDZt2eujxu*n z0`}@KZm+o{`b=w*#^zgSa5I>e5mTNxuex?}i>|Yz`Rr`fgZRd+=TFKciTe~aE1Bp> zaQ+QBq97A`OluxbO1_USAxXJ=ZCNwGs6PBie6s`?Ww z9H)@XG36R#`|ZlX=Cv|_=vuk3HU(?u+8pwJ>mxirwl>RFpvpOEcFJ^5gHfj!^Zr_) z!3k&o@!sHGU~$xGyhM{V%}%VaJrTuqN~^S+CigNteRMWykSSByOF3^O99~ere0rH8 z-*u44zABfb;a07@M3FL#xJ482aD$9^on*Z)cKjgI5Rnsh%Eiiw5s<_bVf6d^l6mD3 zDOh6~S|Wg&Ftd%(2UlK(RNxcZ*mdOYaCVSt=%W8p36J<7dR|HWz)r(J)ktOb5@<)O zOUoFs+BEeDgpu7QB&fruF}s_CyPV=0``!_P&nLdODmj|n&rM&~7P$fG2ssCs9d>Am=p+qZJ zZ-X|XA{qA5G_7U&w1$E4YRGTr4gOu#kwdXjePg^=kB_y>UFQ~8W3P=;e(^sY$!w3; zz0&fGxeW1G^A2#i=wyLHaSt*Eg1s5lxi&spK) zNZvr;RS&n3jjPF28k^Twf$-Td6#CS-y{PV7?Qf{BaW26eIA%)zCTU=@XZMxc(tCqM zpGGlH(}(=MDqB;Dj0BX8pu$o3I&>%jqsQ=m_FW%a+tgW4&OP9_HbpgPR$aEHdA|$A z68G_{-5N~2M~u7wtm&vdD3^-Rxff>>TT{WBRxx%QS(oL4oOeWiVg0q;P9T3fJKd8E zg(~oqYI)2<>kg&6V^ZPq$p3ND$uyTJ`2_5<8G_7iSn<}u(imj#u|dY7Pi&gvEPczts9LNdShLD>xr3$cL&-^_My`_ z_-Uso5>VH;<~z6}Y=S0Ry3p7rm!C+^u|#_VXk{9C4n!3AI3pRf{(wZKJIw#gvG?nn z+H41O(A0@W9gUz2{c{KGjNeuz@XSWP0(qqe)ME4Ug5N)&`Qg;$uBH?z#7?BhtN1uC z7|bcfvd1y&PG|-VeYFhA;aasaE%KwtjZ zVvC10v^-#m@hXz-z3Ai`B{B|Z5Gujo>b}?v3zY}aXVi#FJ@eJg#m1xiInFFmJjY0&W8Y7y?O$gHs< zPl7tEsrnfI>53#>(&M64N#$Q$Ao;+cJ-$Hsk@$A<&Gc@~Rxx3R59^t^WX#_i`U`4k z%$aNU{7kDH=MEHHpuxDV@i0>3F0adYi8U(qm-WOoKL$=p-|E%T(H`LC*#R`*3l--^x=K1O5jyl*09x3`lNbf?F~pJy8ifs^I)Z}U36 zan@5bg&U7B$~>xtZ_k2c%vKsNdR+4%8-E|;CjU-2)3E$HS4=p$+9>S5s)In%k#Kf4c#4Ql**>X4UQ2%--kLx3POxaGq92 zuxCNo`1{0hkT(ga_1s2P6Idh9{ZxKRw6aBl*gAPj-z0k_e}!2e2B97QC9Rv+x}^;i zfNr)yp&fRuVNmR`>7w+IWxq?ha~i+#z#TyrqCJwRMF?MdF&e%nwUU9M z1WMy5WI5$+?@SRAXg>M8pZ9%AuzuA|gUae$(-0qZ!6(j(Laqn#V7Xb)2|{Y{KAnv8 z_IRwk|LVJ6xAi>#?%4cpc+tBLhRLJg!33tFB!1=vouk<%P&sskF*N)Ok1T?vl2=i5y?x|1meK@=PW2dgQ8K$S zUnhZL#qqI4ZFO1{2ZU^pUkl9~yhS%1n(%*imaazq^jIc0vj{Q5u5BznXQCz^W3Tvb z$kMCE?zC3vWBsZW!qExU=*;-v>&k31PZ1F6mbv%NxvLI2a)I^srl!08rs5cH>0KgP zIsW=x?XZ!bKxnPL;g#gwpqp6@_NS12X+6$F*fE#H<)6aC+QNQ#>SD`4gT2$Rz;njEhQh zf++_N8Npt1tnTQlag@RfZFRNLkISG)&(lpD8fKX=+#T~Do6^NzR`{|M; zwQ}g8jo2RTu@EH*MVO8HIdDu*cDojiPIdz`kWB~iAbkaWF`OJt9QI!1rfqPX_DU)taU!*_a2I~=tOkvCp zNH1l>xl6J2s!~B7r>S z(Z;as&Hh@rgZrr6k*gZ87kVmdlLykA?H_YQd9o)w4WQA>{UO*(a?a(@oF*!CqENyU zddZyaj>$y|0aIR>It=@qV72dw=S*~~^SxXYj8q+me$S2jV0u75bauUWu-_S(_4{}a z(){-cV{~o58fzjd0N6vs_6!x!e_urvSVuZbat(>oKpBU~mZ2BwL0t-%0*2M1U*ZcjRmER? z2fIDOYSLY`k6}M(IW#ml-t~Pf-_(p`XCxqwaGI+wOIc|5?jDqIX20!Mb1Zb&{=Y6{^0dsPDl1Nayn@ya_~@ zqxe3`;r%!n;CmtMeBqUG9E+wjM^9Ab)q`dnYC2vk%vA1qeY(~1on(D@{k@*qnz?<8 z@>M=DEVL5s0Tu&^aY!-$6KFhfER?(3@XhYHOn> zdF50F2I>7|z~0Wxvm{IW>Tbw>V8~?aM3$@pZeB8pDHZ)^H^nS?;Jk7G?$z&<|E>ar zwkWPe_M4O`h36~}wLVkxp5T`7xP&M%`^Y$HF^p~qsX-#H z=Q_6YNtto{YqTf#{_p8Ce_D5%QEa!`aUIx4Q`yzgOnWFkNF8rjsF?oK`tqj(rKmbP zG&l!dpGX$-6fs2(DLSj*yZUllo;=J`J`4nccTTh5EdxvIiW>Q1P4NYT>Wiym>2ZJh zr|;UZ{+=Nc6b$bw+MfT61xj!+ri&=dB}>}={7;FY5nQ0OV->g#F&7a}vNMlp-VqdK z)E4lA!9E!d*I_)#@C%P?VM(+F9+UE7e>VIUhvrm!$L4HA%pWIj^bxdRP9M zk0k2IQ^$>L8sEBWVO0~41Wvjx~uUCxN(!dqg45lf7}MP#_JJt$AV1&;a{ghdP^X>6+IS{Rax;VC`q#nMZ}sn~8y7 zhxij3$MU*1&`BEHJhx4~mMchXbJx>%n)tlE@2uwMi`TQZ%ng+g_yY-yIf@j!C!BDry zWA8SE;arnjB{(|-n9XRo_Ixn8CO-N3%tx?_W9nINWbQ9S|7}oJGY-Fd$B|PXn?feFa_T}an4E;k6oft1@3}f% z@n{zMX}R{CK!h!zhGnGmmk&|lV6#d7yOqmJ(%R}l$`z&@z-MXPF|EXLdQZY4{lWLEF4xt*xbnjsLr9cY{U}v#2YV zgTK?jvh&gdopaw`NNCuV6#|YMewTm4M_4w`c#ow^kNzTiRZvbObV0doXJKU{xaMVd zZbO^?u(?@jQSV^jt9~o`Q?~Wwj{bur9oaHlI?+m!b(@-N?&uoT)2_IMGybg5Zl5L1yPeFBackV+nfs750@MeAfXkm>hk|fM`uQ5Y^6_LleZ^AAb^2=WDJWozk*u- znXmo55VN;^t^Vzqk9f{Nlj~@ngs^mb*wKU5g#A6({4TI=T>agn4+>$GdATk?<-DPnQmI-1XOLIlvnW&z66VVZ1F@V6f{ zDifWzYGPkzgQWB-6QAi@%Z?9{)K@hCy~rQPQMsqy$IOypu*hQa6_q#rPgr}^n#9-r z7Q;wn=Cd+l=%q1&^nYZL%YXAQcabd+3aa^L*Re5QrqB=q2T%;#aW~~rQdihw(*wUv z4C8y{=qQXEVX@~IwL^kb(BEbRXgG}3kAh-@)Gne2I>WwkH5~_jlb-oC2E<#^ub*W2``1U(;o}m+XSo5(zxwZ!je#?7UeVQ`HrmqpfECNbiSyYG`er zMN&DDR9WMn_O7~%o57w_2W?nCp&1D)3o;jx&Q4WmL>j5g1M~b+RYb+xfdp|X2*-Fh!om---2XerZE-=c+njlU;zp&FaNL|@0A)3~(&Cr&eaDnumm z$g8U6f<}LG_Gi(m@*rz#Ag>utVLr(5IlKK`4=?{$j*7!hfW#x}lIJMcu6}A;5F?J8 zvh|!Y+x73zek-w9n4dYnu|5KG6xHnHdheN_vrH}=)qJw{Jt+9)Y*NWE(;-J1pMdls z^i=8H+;?47Vg>d0)WZ&76E?zoN@pTis)(}%-#6GC^114`_44WERPbUrCF%ym@Hd!6 z@UnYTe$}VSEOp#`Tev&7eVe*LkdsEK5ntRh*$=#JLG6+$-Hs@D34La0yK{d1r#{+I z*v(@(&R;IVDs013TSmw#rYR#YJhzJ2Z{CTVFfh6vy0cHHHh2fnru$IgZSHkoKcUNEyXVPP37|C}K&pwdb*DDMS zg-mNcR_|GC>4yMoo2_y|*$-Sqy%!!uzlTZ^r0=*w#_XcUso(sl!tc8fzwJNV9d5!J z*xqMv@}R$6o{DZ?_ZD71>~~|D*J4x!9fYltO4j?ES$dw$HL7;Jc*~&pzdX%fheSD? zcPR2l!gKb#otv48L$g2KrH>c-0c_1xUQ@TXty-Oehj&1t@;goyME8@7!9P*M`WKq( zAG+9u?v&vV)25i0fjx|3#I$G4e(?v=igTt-SaPBXL>qXT3pnPl`{~u*vU2|2<7lE= z28LVtZ4}?I-TYc;!CU}slOp;CX3}d|Rhl_J)>G9%VutxUY83+(*di*KGGV zd_CeoM>I|Shd14+75LxvV(E?15rfy0c8&Y&c!~W7?>Z?-)_Kg4vzvIRDN0AsaVBKL zKSNHYvf>goNMqgL-?84KPS^Wm0ven6G}_oLj~Dk-&WizI%9o z9`4DRa3#I#mDNbr(J@XmmX0q1k9N=-nzP8by2Cdow1--R9_(}W!zWy(Fb-TI9jR9Yjf5E8n5ME)l{pi3Q1+_6)` z0FNizDK5x?=j^VDd5MCKFDg~Jk>L7OH)14{vpa8s7kAXOeZLz=PR9OMmZO;F+y|6s z|F7X+*$>U3=a=bK;>HN1bI1>xa}2SN|6tl$Hs<``!mFXR>fFyhs^P|M!EZZ6>$TLt zbJ*mwVJRBH`_9?FnJ;C@1;BSfYcxb|Km{M?UGjI45u(kOx^uy}elE=liaU-MhxO+r zD84qI952tiwAVb(db(^rHqlpX-4KqXq?N6t>UN?-{z)|+H4f?@Zn{y=tbLIS_Ux*! z_lmO>y48r`w`VDFqHW$N<#!*9J?(=X4U26vZeWwRi}M_I{p`v16Uf~0Fp+vE!@=&1 zJoPvNPKy$4=1EJXYX<#QS4_i4Z{Qx`vUqyjB<4wi4V^~!uLD+Y8w#XjWRtG%6Yk^S zv2D5PHqx<)Au?<~?KnW-Z3E~l#TLb(jgx~naasHV->;@o}n$ie!=y=ET0JrG4F3)}{%Bu?-lHdC4QaXW z-10}EGSd!wfH-Vs)fRctW8UGm5B2do-ucRR{tJ(g*340t6{q(z`!>Rbe}XW{=mDK73U2SyyCHFip*zA%&9j5kA9w0~Dk~r(P`uSOic0@cr`zdDX0M zKHcp0K0ccGVEFmpKcVecPXI5wo!i&*QFK37q|-?VIMyEYoo^hjGx3zCSI!=u{)4Ks z@Txj$zdVhEfOK~^2uP<$H-dDRfJk1D_$o*V(w)-X-QC^YotN%=?=Z98dFMB4oxkBZ zXP;;9&pt7k__$=!{(^jeh2T`bP*_z8Stam;F5f4Sf%?}R!WZeu{E7hM<+erRZ3v{{ z;b;W=!9gUM0#?~~1FRqS_QC3e%F(?i8)mv5H5Np;DbMoKsg_Qm^eQV;c6xMAt<<#! zO?>}`^QgMGOaO5%Ki7Ms^WsRML8?&>g1xIQF!|}S#2;@s49b>%Od9@pmC8smk^bLp>8RUHffmn z=1&)Dk7}8v=7^@weSRHL>c0opi{PnqPDEH{W4uAre-0Cd2sqN{`efqly1@{}v8V6S zY+~Zu;Q}+XWhxp&F;8&wAs@lfv|3AT z95cgNP3+(|R%Ef?kCbjnd{mN*@#OvCN{9Z@Y!tDbMi!Gz-!)?^gl=1X3%tkKEQNtk z6KRQvisuYILjsjq1A>(2Y^CKogq2Xldd;sk+E6sug3(uI+`m==~_E6Cbo9O{bqae3W*fCnaVT|WozZyE182_09|utQ3#f!5yGPI6ln&07z93EUOSz1 zZk!xG?cAx`jXOgmo*7K+vYkimga+V68(6x_zj^)6PY_ZZHUxFs3O>s6z=>wL-@TK2 z2O^j22GM_Fp<&ip!4-a?j@WR0KEEQ0i~CK`?H12%-s~L{ji*dsLq=`c&@4gT>V)S1 zjjh^pd+K>@G0354rymZ_q8rfTkGNm^9iy@}k`!n6Bp^p3hjP&3Y^3Yi&`eb{%;(lo z&v-RCAEO7WR1w*EBjT^y>mlsqoO@;+8n8ymyouVsIH8{HE?@AqP7MA%E+`f|c*E*r zk9q!I3%?)c%*XiwvS%8Qw?!_|44?GI`AIwKQY0$McS1mKQ*s_)!`DE~SmdRu@orl* zJzhv#=KEh#muQ~#tl$qB<+1FML%y0?I~}ERfz(@sPs>*eN{( zZduVPKL4%El*_(PP2AY&>ESvMF^{y!gguVIJkCcUi6*d$Qh5)0H)dps2N0!rrIoqr zq~ujjF}6{o_pga7f(>BJ*E`~QW`Sxb$-cFJATlFD=f!H6DyqcMwdzjMu1i;>YmX>u_l^H{OtQV5H)kZMoVWi9*2)M2`~Lh%i> z1hu~0$Z)$y0;>9%Webgn(Jzm%xEd}gH@TjgjwL3{)^+e+i?PD5XIAm-$i?%Ej9&bE&r7%k8EthSNC)bA{p{}q0eiJ3Cc*nly@3sG0 z4gOnDwJ29A*4rE)WunrJjgbB`?3}iOwfWy^Ws?AI4aduW2 zrrlEm@%5b%3tLZZ6@CEn`<*ND)}~CT<<{3xzcYOkf&;6w{V(pu)vgR$FxsXl!?q8N zJ6k)2^Ht3G{>^dQfkgf0$aNeh`TSHI`imTth-lAq+A5t{Gw7ybzkAH$whc08)5a)9V zxNdk0H6F1j8-~zOxBFYplk_#`_dcV>$~1W%3hzP?stDauDn&!%yaUERBjCQZx`^t+ zD-*>F>hT_=AdKYnASTSOa{XR4lANceK)m={JH7O~71a*t;+OEwKQ$J8Dsr^x00c8~ zK!n>XJ|AkdU~Xx(#!193nDBD7QH+&{znIsR{_9=C0rShvcIiIbbj)L!!qqw%n=bMF zi&#vl#I|DjTCt$hPZTuP+#98@ypsB>nz^6l1@ym>QLj=xzY{iyCBXe3F(p~b=X8hZ_ zaVI6q5iM&Gx%0W>diOp~hqmoAoG)*V z$>i#Ju@$;QIURRW(T_S-oaw=Iwv(JU5vKOOjG3~Zm%6{GaP2h zKu=`R?Y%AT#F%Q~epTFBxlozhB1jHp^uChDn_S@}U}ZdCY)?^X<7=oS8p1<)*R}5m zYKQN^)?P=thgMM9uON}LUuRWrNr@5Dc%%R~%iYIVlMI||I-}%F@ef$yU$f|xVtZhw zU~vioo>;*xI>#WyJ9C+SUof1L-I(T3*ygOI-g=pV&K)i>jg#2bjBq7qL7%ZPhyQI56yUWx#%tw5lJ@54fs1*Khs9?Z-$^BS4K{XKpbOYASa z@nG3c+jq3jnSRX|ckiKBw_KjCFv5!0t?-@j8?XsW@(;yr=d%+}iq0gWyZ67yu!X;N zd!IsmL9kcF`;w@XeQn=KaE&x1shSbc{~4xbOs zVXE5+LzSyog${k!_`RKX%Y0_zJkH;y`jx}@s>+RGA+@)nxi><;RO`W|EzfLp`o4K} z7(cCC1UFrb5p8ETJss*=jsP|f1A?!S-xzD>+&3fyvODu{KQJQL*cUQbO?`fy zKXWc5R?L6=ux*T&@87Y32j9};E~UX^uK8e2<7>@wpSxLp(=flogP|mO6{xT1L?h9@ zd8q!>p@%i|OOBnjMsZjYT7t7{?&1R;_gS~_LK5WZ+F>wkDqMSY*IB&?JWe*2w6S4Q z&LZog0l)oCmEVjH--d2{D~W(kl;Wm+ZSCswmiAx^UUpLOgrstbONCjen%Z1Mpt!buj_HAOxH-i* zA_U35sC@fqSU#Id>(6DB@x@GbP*q^fT(|)@3uvci#4BOw^+Zs&Q5_;^toai;Q|3Z>p0_a*o#;#nrg$M3s=1`a6f^OSK?XEh?>2V_ z_YY@$DM50;b0KBq!V|n)7Xzx#&b(9q?HR=t`BO|ha!u`eF=10`xl1&Hmn!XzGKbfm zNVdPJD$~!yh&YR=^)DurHzlF*8XijifKa^``Pt7JdZCcJjOXxRLP@#AZ7sB+QRO+p2x`k#M$_1tk%lI_pPOSF4*__=cAEG zFTOhJr|a~2b}Aba?2;Ye%m}u27dl3>p0Mw5K4YuGEITKNm9}g}HDE$DaX_@N)i$os zoAM-nM=k+Z*|c2%3BF!`e!h<|eiJD_%%6pg!s$M4iTjpL7#_^o3?rWyQM3F-X~6M^e{oU%4HNhPhTLu(ftgxJb}y*&@ zwz4`+UrB!PYga-7DuAY=V8Ps`~ zW)2*Ol;X`#*ue}=#8B=n?sY?VGHUlG*3XASbSuWj`Vo2%El!{BFXhB){aiGbDR|#` zoE~>#4t!})0369IF3o@Awl*K%D!$Xdxhw#I#j{10^e2aprFSl6UwvWMo$ypbO97-{biz2#gM@qtd&)%?Cfva6rcy>xmsZOU&-OZO6MYiQEAF@+nbkE;$pEGG5)(U<&*N!Pk68mAK z*h$$)Xc>K^d!95>GHt0Pt&{Xgw)G_wLh`cLtXUZ%#Px~3$(2;|`zM6?vbwU1{h#6q z+s?o292u?RH06jN1%UMB@-!cv8=@se(xBWb9iW9?5@`ax+@JAh?_V6wgT?T#llq=S z+;o0l-w3M5`y|q$hUl+$Ac=3WEdiYAKBAZ3z*rt=WZ*#A%lBfTpjE-xY0ZJ?KO{en zp1=wwi}#p?THZ5{hYJgMQ&?75JCZCb4$KUH_yd|8*tSz{+p4g!sB9gp4=>$Yu=Gna}`jax`G&1Ft{TF{8~oR_?ma`{oW{C=xITM9W-#(Axz6}M?C0{ zK4LqDGFcn>eIfk)J^DI2Y|)O1_vFypEf$!4?`&y!?t5Q@w;06+{Ry}m&=nR$O|Alr z{Txz6a`s_{AVxka98=2X)W;Q+?I>p? zdWTY&*~O_hJxR@H2J^=sr{^s_fY<$mCqu;q(goNU(&GhDqk@WvpJi$~eb(@y$=iyw zJfElR=HAaceTNqye}^*&C2)b^-UFV$Kp^nN)|Rf%+(b{l&}jiuE!D`pi#%ZmvLaKkYVV;E@QOVb#(%s{;fM9z4#+OYA z;guJ6_(E!HF+LFgnWOUivW;V|U45Qo#k%_=T~fmoQ7pscfx=hL0~Up_nrBqHHLP|D zp6-OV7`0F~_y``c^hjvQvYTrq2C_F2c@YwY`S1}SzI7&unUdU=NRDz~M|(gkKf^=W*2*$40WA!En+} zTK+;BSybl{K^vULCc1|c2H}dVEmHaozkGZ_?%T`x0N~vy+*d!KF?FC?eHE)GgGhx; zsW~b6R-P*w`8$igX13=M81gOY)J-q?G|KgLug3V={WH;aeyXH2;!qkT5Tj6jbBmxI z&1ECm-SK7zriFW+8hY9*mx`ZLJX-zfTta$QpR+aGkwYh#ZrZjx-_lzsClU27{n>3K z{@og%3&bL?yf>>blsoZXzx^Fc_&MT>4Nj#^)bD>8tc!_^C^++edS3ywue6cmD5RE{ ztcmYG$zXd;dJn?&zWf{9<(^;DUd$c(d98hwbImf!_F#7_5Rpv|OHG;xny>KwleH|N zoOdU@1M=r25U|rc)V&!d4(!k==pD;lzy9T0nhRK94D$N2NiHSA!XB~4bl$x^Yp?H| ze_$nXuxZlY9C0JQNUcR*dm}ri2#0N4g&pMZpcCzuU$! zASPi^VJ9WFyU@RWDpo><(Tx1UN1a9e@0m@qedMzPDpQgy=RzrrOkRI{qNm+yrB3Gd zZV~W0zRuwesqG1Wb#GX?+E1!%gxo_l#_UAVPf_jro2%VRVA{5=w>x(b1;2}= zfC?u*al9pU!|gHM+3}C$F<*$!=X>H=Qvm+5Grhf2Hm6@wnUd$8K6l|INU$9n{84Ew zvzWB_mfg~6a`f-3Xz(j0GHeJsTk2N!haf89oHJJFUpR}Z-y%_y6xV1^yI}6};*oxd z3wER8pg60BTY#0Cyi+i({~MM1;;d;ruy&!irb0~TJp7~r|*9_n) zQSQXJ0{!>1XZ!5F?YJYmeWK|~j$BG<@vnQtpAK+yL3VF3hL}3i@U0uu$s8}stkeWO!~K;eh^s*+lo~~hecvljFIV@q7B`tpVcEX*%7R} zVAul4q8AKzn73DA4evhXl+0Eomcg^!S*umtfQSy?px z4gNRoCFR6&J)h)V-(9^49==d^CQnp#E1-7udr#4Q` z#=Ktc3m^->5eaIu2W%P?J6t#IdH51I@2Gq;=5#anBi(8Wixp9*Pwle(u z?>h=gw^yv?W>pRNu@^n1SsoO36r_a=Je|0gi|v6p6rR7B@ZP!TLQMOyp+@rXawg=8J=`zz{wsj1Xg`blkn>;8Yv#V}qa5Z=;$Zo%qGXOE> z)FLU+RGV-4#xk>u;%WN_&P)JO8VSx8cU5yZkOor5Buy}DDm|bb7U zkxt*ZOI3TKc4yo1T8raS&CNa*yp~k>7NLYn?(vd8gH^1whS9|N662yw`&^EdxuapV z+NeBGJ+J0xD^L7}xj&$7k{Z(_)@THNO~t%59nDsNuSfT8g657DQg+Ss&Nrh z7M_@RdiHS#B%f~LnLJufsURYdf=+=Dus^;%WwMwX%lUKwd*iQ`7hpVURXGaR^WZr z#HZa`_N8~ppR_E*yCAn(<#@~TWl@?-0Vby@ywN1`&7lly7!5Hk31u3zTlWQe zN2+Ij@V!Ulh+-A_o;aWvFIN{$_K}kvP$6Vmq9U51;(>%3J;Qe<;e8-Ide-ge`M}lA zl;qM1EZjR?oFrym$aXBcD=LzZ7u{R7^-$xeW^H_ON&%(^d^!`A)*9}?mv`qJ(5G82 zVx}o>NB2{K`f`wW187Z0EXt4O{sFotk=9}eySX$O)k%idSfN2aywqRLhElg4_hwKHZ@{`P65Xm)Mm~28gv_Ex!&s7XPMC2ko4o4oswU6Tik6(5b1f`~C6m z?d35>TuOkBg|XCR^q^c1pA=Xu6u;>m{MXjX2tD*MCwJp0^XF`=XQy)BPfB`+v8pfS zXeMAFs;eTbjaPaAjmJu7=80pLhX&i5&CI#rad#O^YLR}B@2@n;5R_qy$0wPSx-Nv< z`QNYutcZb#Tr}C@X}lxT1t)_PTqL0O_xcMJN$-SkWS4hY5XMU|8ye z0?ZRwe^d$Ir-fJY(l;8Z!l=DGTaGOx1#n)SL0zSd-BTq9_5K~p1oJA!#2j0*tOpsp zJMj(@{Tf6VyeQE`CTvgcjS9RcZ3S_D$4ch#!p*bU*K_q_De?Eh0QtwS9lwR}n6$ii zX+IcXQNJ7=R}ZpZXtQ63O(LI(MV4?VbipH{d=iF|@TcxB{)!PO);+pfWvL_iOOB!n zY-UMH%uo}7Z3R*sHS+{fB^gsnZIvKf%Awp;V z{X2K@R$GO3o>oHGp(x67apcF%MR)WBl`q>`++*+m^iCbshnBi`Kb3a$SrX5P#)VEg zbh}Fa1M}^d>v63yU6yZknc8>oLs*0hrc6HYxRcj|cV<9a3~lP93oI<4sUWfIj{Tjf zy;I2{R1JTL;?a}N6wi?vI0PVKdo;V&>RA~yNrhm^gY8SxUd0Zd)Ia8eB)zaKtlY>>&^=y;)YY5+Z4A4qInL*HF? zKW96Se+cln7ZNW~-Lx zCWQ%=eugofPXt_hw_cWaHV|T?g-1AtO=sUSr8(a)v7AZM7;9zg6qnnSLAR-U)EQlD zLf01bFnr!7t^vPF+p8wndX~xYssmsr8 zG&MSCbUSNJ`R`WIz{{EiuU2T?FP)-21OKi$l0j0)vzAnAB-@9gw2|;|?^$hbM|j3r z0E4{Zb}Cj_t!w`IMxey`p-5(~;Bwq35o^Ya0LuBVbsQs5NDxl6$2sF6c)LM>91RY6y&J*;Af5f3W`PUta3z^^)^% zU`UAqZ3~MQu=b7(^sp{LCGuZzF5#lBxM_|O`xDG3E+}D?QHxCHgW1HJ|55#XIcY%= zjrRdPa`P&%S1}R+*FYIR=}7D0&qyGbVqPvtH7RHHOO744Aea5*X8?!Ps6$bx($nuU_< zs}%k`{(1draozt%&Smu;I;GAO=Ar2^8{hP|dC&L+9iFw}p2i&86`#SGBEfd5AK%u&^rG22bJg=^IJ$ z-owqxfrID8SB!2%r{gW6!M0#NLf_h+1dkP2K@O%!Jy`!(X4T{TGt7}FBKFqvpDi#* zp^OM^8@;#vQAGsfvHC!n@lLyeHRKbxe$|mPFi7p)FA!5A=0lKI9)%vNj>j5c=g}Dl zE}nHQt$ZzZdunC9Xy-q&lk1?pD84$b#9RcWP*Kc_?e2;Sc=f)7nVf#KiWQD{;ct{b z*NSCV=en~Sy|&0{Ys8dC&o|nzU^@6Wmmzk2&VTkJmX`_|Vzg3Ub}?jkymQB|y?oy4 z(amPr6UG(%bIt1vjZ`v6t;iIRYn#l7s?h*_Ai4YKd0a*K%p$#YGi`!Z`00Hjou@DJ z+!Xr1+=+DT>%SWO`veRomCMI1EoshzdZm2EHJ)4Df9UOiEG+;NsO&z}=~cKN_^p%F-z?k|(^p&~~lUJ~;Tqlz!4 zX=!Ht6G6$gc9<6;3m8HpcPH$-_O1%99@6!2DMEEP!gG8$x{nnBmN_3Z;4V(8?j-U*6q?outH!gzvB^memuGpM z@A#M3Xv2#^zn8;{Uj-v(`N9d>x_zYt@^$l@TecDJ9dWM+64p>K+B|yZV*|{4Nm&(i&J9gTg4EZd< zwJO@3c(2&hw!CXC^V6ZMA_g zdeK!*s#^J_iDtzI*R?o`NH&5-T54|2Z}>D?>3z&Q2%3aj&a1oi{#C=?NW~uf8b>!F za~q`;Y{(zWbwo@IBYBhb7_vNua@>pJmnTC>EW`k*FDL{tB7AJ|mhwlIB)JyCt~Q^5 z)!KT>)*X-}?(^mifEKv5>OVjK#H1C42+W22eg1?VdVWnKm*d)A4!__xCjyvg5- z?&;FFc$iSfSS%-uNz;UyzpA;8BM+M3W@rJWCj!e3j{a#R0{x7%Pu3T-$exFjMfli7 zblzyXz1KQ&&AHS`fb4R&VProjtDu&b>e9z(uY_+L?ccbR0hL;TSIA}Fw`zkR4EjN1cxGjJ19kUN(C5H0 zoP}d^#Q7i{csnIQLf-XX5z9?(z6Awe5+a9FCs8u?`ByX zgy|HJEjQh>Za~~p^w}P_FOCNB3BH&?{cXjLSb6kC9hBVq2iR)ypX224hTO@;Q%m-> ztuck#*O2SM@}^b?I#aG+9W!1z8)N4<*vLx_Xnn+h65{C*zpC34Il%HUZU&U+rAF?T z8*aQ}ymM7|Polg?_yNPS@2(Q8b*gY!wt|UN-tu&=w!122@#NE};Aiv2#}L*YZNz#z zB!RqQlB%Wp_(7;N1NiV!foj3XIdzlW*0o{f<>!hoj;WceifX*Av^b$Q1{No(Xyiix z3JvobA(4!&+oGnQB=AJ#IFyjF-GrTKK)M<|o7fkyx$^U5QmNs?OT?uaY3<-$vf$g@ z=vLaNCMd?0SjXCT?-=?=eUo3?;M>n=m;IaGB*G{ognZw_5?X^^1 z(l5nuGV2tmmC4`K%w0Os6PD# zJR9Up>JQ|b*nB@v%l8|>;EukQ=lh3yHK02YVv*jg2Z7hujSz>d7cvPX5=AqJh})97 z{B73p$Ws;pq~zk{u>EfoBsY{YhRJq~@7ycr-wCxCp<<@kXG(Z$Fa zgLt|~zQ&&k_#+X5;(hlNC8@f4M_HLDDyzS?uTa%th*E)Gl%?{Z-A=T9EE46ejk_p1 zsk`#*l59x%v}M~}EJvHLq*hCAl5MV<73GC$ZVJowVMq!D*2vlAXG5johL=?bBKNdc6xuQgipqWe(a zCvszciNwKM-ujU1Culmc`B`uF6VbUGI{8pSJm!3!=J#d7X39Zk1%g5RNz2A(CrSx_ z%uhs1UwOlvO_N?eHORD@qRwb8Uq#~r_=;%E`7&8Wm6R?YZnJ{F+3PIyC9z=W5t*Xv zaB>8(=gpY*&So>|ZIkXezDn0(^T6CC{I1^bEPs%)OX4IYvkly3NaJSwsYC@LQ?IID z|J)x4c9^IN`h<~5nbW=yZj4f$+oR==N(Iikwz*K`0WDE3?b8hc?QRC_-%2=uLWUc^ z4irAut`2YBRXzgp-n0k%j$%jUj2{M#?J=bo*6k+I2!bZ&A6yIUVu5_Ud9@T{vcqt* zmOnM{i0o1A^RDW6s@t7=1V~))iEeZOM|hH`4npfG73)CB%8?~o~SXcHz zH~r}EzH_bw4h}grxs`WF6Q)&EYDWHTaweCGJ%2yagS;rL=s$X(Ms&6NoY#64Jx!5K z$=9bCeoFn24mv;BC2vKJobV=`op|h6?u~^$XSn7t^w8R5j-ruyFVmX=E5T8}q%eXR z0=An1f<3S;tb^%2OnKG?Hh37V%9)!<+)C>G3PT7p^A0JuKukkQG3C z?2IX*nVMg`v0P9x+NVc}U9_MPPVe}{r}(`+!v zxy3{!7lVIY7M}r=kb(u+h^!yw#lZydQQR~NO;N{&YH5oA)!gDY0-}2h&VI8A zVic7Nn)Oh|Bx7vbBWZIdlff*tgxvDQsWqM96GD~DQq6trZhq!N9ouIn;6Nz@_k>7W zsO*|1?$hCo$ZC9eHglKiiqiJ8Z^&Xd{hQestd@b1Q778B&w^P8-PjSMzw!rWJztsZ zG_;wse*;EZ#OhGH2Tf>t^>Vk?xO|#wQb|nd^#=;$3dR`;iI+NeR?l9KyhiQHt!^wzMiP> z$ba3CiT=(*#TAwAdH~OfjHwmDZO)o-EjD}H=ZD<#$J&jT(61Bq ztMJ%VVZPt%G44bVZ{-cX!U=UGQ#!B*3=3Pb;nsU-oTeArb>z~^%O!ZpwTtj8?Umok zJz>vX41MC73(6nk|EO+(WEPez$G1x8i#_%oiYc_lYCDzn4E#fvP@oy8b>@Rq^BSQ| z1gaEt!w&2!cJwNUR_5Iany?~B0{i8SZ37ujQ*?R*ckxd#FrQ#odrYk@&nTc3U*cjq z9v?u#hf#{__99;12Ciz*wTbdG!JR0LzZB5*8xTeKKcNi6zXKyl%c zs$2M)N0wi6c|u(({rL{l6gWyE-T>j@KaQktdro?ku2{v_tZMZwItRT!U8SDFjo}94j@=b#u@QnDoe%R_Zv6~SOuD)xS>GYC z@Qr$eE);8*3|KRsOh`HE-LZ&Ln^>+HCYfQUTsTdS`UBobq>OrY9$I#Zv(gQ;l?cB% z^yZYr`ak#nAy;d86B;plz+LmG{*z8~FM_Xiso`x+x&mAI3@EXtY{UhTbN@8(MbE9E z({pcjPL8j1p-3SaA&z9DQzhy}HzMR$emN%joL|&%P;p5dAnz-+L!|ELzB>7p>mfEN zp;}@i@($pXP`t5+^uN$({%!bKo1YSN$O^qN=VSAu)eJg?L@Lm+++jOYc&*6}ZjLsM zIFE0%$5uHro-$6jTbys;xC#Ves@_!h_A(mORao2K`9@mm9(Qn_<;Hw_8aG%<>W|rH z^2k|s&gdn`&{gxPqeMHq|03k8y!SjQ-^uv)L>1s2Sh($CxE+UNc@|hg-2uBukVp35 zZ{ZDkNM6}P;2TmA$2Dv=SIIo`ms`TpNaRF$O|+w!1d8N{nhqw9j2-vsvu>golS4`{ zHzpL#9E*LZs6K4&geSv>s$GVh#nlW8Gf^vHMRn~$uUmroy{ivd1zLq1bYJG}m`9}v zKG2=+(eY3u?6Il(THi-&gDBb5L?Z zqJmqH=1|*H{iIupy*|--cKVBHov~s3KvY}^M4Sfw+5 z4)A{2d0BnV-LMTsKmOjJl@R8%<=%fG0ETt{_!~ox6VHho+bxM5JtHl;4-Xep#_9hob8~%hw@6hvMY>9SPlF(@vufS% z`3XCb{=)v{Vz0vVky0c@<>^uEE-?~zb(}eHz7YxhW_rcHqN;^_iZXYK`GDyVah8cI zJklydP-M$`IVpWZ=Wjhd^{BC{A5AJW#$DRAc>i~9IUe6fFg}(-2*cdWCGUZ#Q zObOR;^jM*1sAZuCNl{7oZ~oY_xzK|j9G-OUfp}qmRZWUA&BK0hK)kB?dSih`yv(e@3}#j@-itYS#Lxgw5zFQjr-JV3-?7 z2gZ=LivtWeiHUVM76inbzfTj5a$Bgt82q3G8?4(WzlhWgrR14#0p%ahZ~jFZ-v2ae zc>67}zFyFvp38)ou|N$vp4T!(Ec;jxNP1u28+z)F@)}z2#C6Z(!s5h1rwqS4>3)!h zMi_Or(h4l5wM)@rFgi#5_~9A6DcquMKQg3j+XDo0=oOUwlKoL_i(Zd8@=#{k1@9AwQE7b`tE# zsYd~@mE2;sZ5M-->AH8kp6c%yu61}N_S>oAj~8U`zxn+Ibp_#Ye?R<4${Q^g?je!I zBDE-pB7?4`XY>ADvM`TF`e`m*^C4niKgEWRZhp=A>ZSFVk@c+4>n#oMML~1C31a=B z(QOkSNBqDjLoix^xtk8=LsE|z#8Q+F`GHUtuGNP)lSL*tI;j1*+UE$E{tFVYUcYi>XCM6X2i znAT=8G$y*GZi)k1C=`xD^#|N{^?Ttk-n^BOP2NnCt$qXbj#=C!(Fz$#sS`47#G9uC zHciz4*G-dIxv^ke?dS0}bnp;{k~uF`_iiutftq;8<}UdF$mVMNnd$S#RsR)CvJX6! zJIY!%=B<-J;!IoF^-r%cg~RMI9_AIU`*J z2b)|#`|cANu^%q4u^SVK%nI61GcWhg-}>EOVSCoK3E4dzZROB~d>o~=Q;-|jpi%{- zJDF@!wIVnLcGgNZjABMRwnvcb^-aP`DLOXR+6G?j13g@cAMYx#C9=eKi+?q&cp94s zb@&EQc7mGn{hKiGEIIs&>YzQvFde~N=J=mcb~HBDWTfgctP0MB3qEg`u)^)2G{<7 zQ^}7l%nO7hk(}ZgdL2wc31RVl_ps!u3SU5VET-%d7LJ5QMzqQ5^)iaJYkg}GrT1;)fPHEiJQyL z*_k&hPlZ7QpYif50iy`2ieh(L<@se8sOULT1pDLt z1NXj?PRLOhn&F$|E{mPuL}g%keBWpKhLZ4`XYXkx;l{!gUAE6O*Ig6w+VFVYqU?XX zw!V-(*x-nm5|aM)(-qA15%zTzjce}0!#`td9)>qqSdqe3NG}aXeCMF^=6Ss3shcun zw2K1|%gpF|x^?eqaMV*ZU}XFpXsa`{)%$U7`TJwte4-8wJWFdP{yFiAs_S9bUg5!& zV*SwLAOv%1$S9pj3?kc>rambk`$+Y+9y`|I=I`$J)<)M6XtY2#C_LgmRFND2&ap&( zaT+2p$w|wFVr7qMM^iSJrgg2ZRfIpA-^4hIKh@nOq4DX8I-Q6BM36U7n#{mi%bmkH zIBJPCE536fzem4)`8!9ZES{KE{e76^y5(N31rz4gpyE7T`C%wF6F~m=bU|(WnM)=i;G8@P5 z4u4A>7N|)TtP=Z(gy@=$Y7W#i-0%z7A7paowAXZ`#dPrjh}|g7WrMk#6Jc*i(0Q`y zGEQD9gcvXHYWqaFs*Qt>ihryw@UQ?!T)poa{ufne*;ZxSb!|a9Bn70qyBp~g5Tv_7 zx|@?OrBk{?q&pW#hjceAx)$BAUY<|yef@*u)3MDt<``qI6%shKni08#S&a=j=KE(Y zKycbY*5}^NwOGnwl$9$>=c!j4G5Gm{9AgCraV+W>2k#JSE#&m*BmYV=JnP6Rzp3R} zeLffmJ)O-4Kba&2ygmZGt?Kt{FTNeV7wBEBPq)cE{s4gsTX?s89iyHZ>3UD?`@UY4 z_puX$LeUUV?~hZOfF*!|cc9q5@NOJ>_IlUS{`0Oh4vHcYS30iUXT68gQHX*C!VBKu zAUTPB5F@Sol^s}8^lEmpiY|y0AFG8l5~zX3JM2A4M%??`mK8v#aC+A}I5j_T+*Vom zuxovp7cv+@Oe{>_OP|9!jhKi$`xvO8SM1F-8xf;yP!SH)8fu^>}hq%p$C{w)^+=88t!Uk_Df?b^OR|PKtP=8dd58 z=bv+&wuQB&P_sGSLAlbpzlE(bTL_VuCLml?t|-sKblXo@IFk}6wVUO4OC+aeE_6Cz zebV?+;my<&-@|LuHd2@o>EUjJ5hPf~;@9th0(sDh-&VotdX^MC`>^y;X@2Wh%5T)f zuhbz@H8mqf0~V$vM+@FP+?B4n?1g@odcSHk+*tQ{vimG(M!E<_%n|Wok`Iy#Qw*%p zU!~~hH>n2$-hUx;>Y{27K?$#&%CMnyqryvej_}}As?@)QBdz=x@8$uP0<|;K%X0n! zaO|n_uVYFua2I3Yn=qBTFjdm2J~JkE$`M9dOZz8d2emid&i{U_n{Ab`t-?=Bg4L*M z#dstI5mi$wh1?;~WIT*FJBvJJ?3fqpIiS9q4*RnJWtgv7`27B2Eac~;JQ9B%CRni^ z$Hku*^20eM4|6IXQ~a>kPM0gHK-oViD_f~kmi)iM zaT&V9$h?@8s|CxpWVh5r@;%Ry%MhxIoP)J%WtV@w>=%Aq*M1S8I1e#T;GN#3^Maq) zLu|V5oUy9-7*sUp?y&vx!>So;_<(QAQYz9xA7SFg7e6bA4n=IH$M}O86aC{v6Ykr{ zZc|FGlS+vqMC0@jX@&ffUyvbaE|GC17CE6iuln~HN1i@(53jJ-*;&jxW3uFgA7hD@ zCf6S=z&h?!pxU5etDo}|z#S8TIBghlx)EbY6zs9ziy;l=h`~Q#6fw?A_lGIGxhUB% z0=)*`rAeJ6?7TE9y4=jXVmY2|Z2h~vO(bsX1qD2VF1AM5yzVZEI##-R?y3Xgo)j5x z>jOkr1{D{x83ajP7%^TxZRo$wM*4le`8WD@MvJ&2X>`cc9~?LY5TX}69nVkKJn!>B z+i4MGs0K+0gxD!`_AocNs6{WSq7fFC&BTXxw}N|*a*xqO<{22K(-=gjbQI^kKZWnY z$)t}qZwJDa^_yoo0{AHo^h3$3i$%!V12J%8sn63;|AD{Z;<_Q@wFOn-lt(Io;Up#F zMkER3j3N|8%T)IP>Lxat{Fn?WyzRkrfygF?$0=T$&V){LKUKHZ0m3Qc-0F`lZJn;m zSQqssyp;fA7(Vc_{JV-ZSU~0GPH*!b@XfL{6NUc) z0N*o^1Ec7!ft1Vca`Lg9feVSZ#r^lE*ZLn5Zg^NY z1ZT)dxvVkTmNP`47nXUR%+S43tO-g-v@7sw5eeAB3fbM z^CGiS<;GMiwSSL`eK^kLl2&XZ@>UF&XP583Ez`b+sI*pS{DrXGz;7D+e1)MBl?)hJ ztNKE=__it)l_7f{h`kV*m0oN`Tt_P>Ud~HZAE)k3#Mu4Fqa8s~j)2@_$;*G|!u+BQ zwsuZnU{ciZBqblNcbiiuH%y8;HQ z?+TB<6g3@2_I3A1Ts?s9p_*})c@Fg`=#OEp-@0-Hd+LU6`yk$zh6mP*6h;B7?)BLh zUAC2vY;c)@Cx1}weMX}mT5qiL&h;zwQcuc;q*Hc&l^S zs?G&oe*)0w!TJ@mJBahk=q~83yCBU~_MGC_k!=Cwd(nQCeo&Y@@w6XLb51N36rPuU znkt67dDScH=X(O%1;pfnUI{vsUw=BhS(iwwjXDN$MPEj}?P^C4%F=`a47z5+bZ4Mi zh3=C^LADrFMJP6xLZKoAC@R~Mmvi3){sG06U!Jm}yi8iN$2N%UwCMn!?Sio^v zpZYYa^1q7H^}F##+l6J((($>^5UyqD#60+d?AU5oxU#D9pzcTv-^01suSz_j9~Q@g zqg5Aa{`HFCCY8zVX3w|PNoNki`dW}X&5Az>Z+KSkRtz)N!u=j{9JB4$h9i_f&xqY{ z{mWQ9+n-z4JtrR1U&Df8Djpk56>laL*;nQ3BoI*^^kY0tqg*hk zt_zp8c=jDB;_3Hz&E??I7}<*G#6XxWROrqo4BoCTZ2e^|n@%ERKz2vszz=o>6w=2z zY>3?us=euSD-N@Wd|P**D@pgoM$we?q3H5AKZ!bbyD$(x#$vIXu__1FqCD+PE*Po^ zca;?R7Htu;P|Ric(8fxpb03jXFL{o-L0~!`q%yRX1Z&Jv^^}GGX_H6$q!z%m@W-(k!cy z3^W7Aviy#};Dnj_r?RN!nT)8z!4K)5u~I9369HRQ1bIii3aXap)Jog??Vo5)T9TKU z{lb8lh5%wFIw#Hk{fYCW?#JH;9_&GL+cL#pswp&YMO^N7TPQq#MM@S%t#opPbFT>o zJhkUE7wZ$MNtY_V(JdM@G8AdhI)}y(7mAPQkT}bQf}vEcX&wANi%R%$C>?yKH5Nj@ z(STfDWjSbZp+uAf*~+KhE)1b}^JjiqtTa@T2+cK|RV{0ZVqv=J-vBJied z8zy#d7-84vp1+kuy_Exia@rj?>>(3Hko^~`S3mg=P`?hY`?E78Oi(8jVZ#u6aIos~ zimc(hIfoFT28KV+n}Y59tl!tKg*pvnI1p1ItYP41OHD)HzExf(NY!ImFT)O ziC21lWP*>((R>!qiScZ&2crRLjVI(Z3L$r*`^A3UZi~lxv*Lvq5)!ZS-kxc zZKGz-zH=ozA$i}-ApUGZ6vud7sErce@%v&c{P+`w$Nmi#^3CV6v16lIC$K`a z>}6?N1tkT|xh%>m#GM#iKq=S}x)Ka(cxpSx7mwYOq``s8dX&EBM8`SkOft-~KLl z=5@L8#24X$ki`A*g`4@)YMK0jyDvik^^E854EHq*_pEpr22!?EI9WiJTs*!Vu$`C_ zk6@b(1lmwIiFXK%Ked=3>a-XlJBZY68uPoO+dg*%$4;8AzOP%N4WvKVx%2zYA7ZKW z!N%S^Jnej}znq&mjC>eag6Zw^EQYgJ4_L-|`r3Yl=6*q`reU$|nG^)a1}6PEPb;0e zeXk+EsB;j#SG6j6ye`J#kj9N7{!M3ZLAj3 z8id}(z{pr9wwvBH(~no|xKN$R6U~Og&8!`b1*?YF%XVI3Z77c(QjHHmIl{CBLz6|s zfI6II2rs66+Ye5e=wEmbQF#?R4(pr?$N-(P?EVVxin*u<{*-5IS4vS(c=m0dw0B^y z;04?##Cg;;nup-wIP%xQ8E*O1%B6HUoHChr8^|=%=Af&RMG^a3yXQ1yR*b(^c_L$8 zn>7QrygxoH7*^Hl&m#s8?X{zg<9xYg4$Y*-5dO4l&^Ko0@5>p2gI*gl8#{#H!UtR~ z-5%6Ut;P*1kNDR0c+>@-1{SfbtCjfU>rSO%D$DEiE&3l7S$oIYDL&?>=2`G78}hy1 zznWb<>_l9P6|>r&*g;KgAVKbld4{Pxe@mUyMv41J7#2CeXNCV9&7$&|y&^dT4^5LS z|5QI8!e*cef=kp)*pn>PkK4f+X$M*&49B9v^o^{iFvJW6{v^0p4Ygj8ceSBe&j1V3 zs%q(rOyZf>>Jrl9Jh_4X4V$fU*;WL(oA8O@|1=r4c9Cx#LW*w$o}lzEc%XzYd80cI zKRgq4kLC}yyKpAw-vzZcUlujMGKu;aG~?TizC^BS528$=m5GY3FlBTr!~n_u)pUN0 z;!Ltfw`pu*TW=S7HsrN8&AK-C;w{Ag>?IFJ20;zz>};iu1PS0Yy)h&Zg=S8XR9UX@<;H_6zWnx!g5x}fWtTJH%}8<5G}K@4G{PX_ z(;3H`3p1R;P%Gpxu+Ia9(*NgIdOO68Gu=E&L;_+>T(<%*Z{v|`(l9h(`l2mdrFvn- z@VI)=+s|2+#dL~JZNgZ>*g<1r!WQ zq7kO}2r-t2!@Q5ENF0Mg12DK*_nlF44@EJ?g`+orYMu)ad^_YJf@W~`xj{Q5UM#h~ z%l~9l(YQwU%r{O{m`kX__HcAWrbIe#-byAo)zB!@C39ZXg|8i=IZC zK;gQL+)GPWWPjw5{mevg8j7_N8x>eRIEKWacWz1LfKcaOver&vzMy3^L?(pUaO<`h%x{WKCg~~BU0j6Lx@Tx) z7bC9*_}cH$NQk;F&}~9K10Fv29a)fntJbXCg1CTA#Pg;CMc?>N&{h_t)$OVKxrAft zg^GtDa?Y~!ahWeRmR98RV*k`zBho&rgM8(|tLdXki4{xKRs-hD@vH&)i)>WG;UAO6 zdv`!P$iEC;r+2*lwG<=eUU0lKE*Uu;eUY|)r&NS|532MWj*C(V?}E zdlfq_O}>jc0rdc`q_WJ_Z1S56_cqW^4`=$V+D$^wXRl1ODV|Oh7yU zL+XO<3;2nV?Aea)5PM$JRkGZ5y6C!&J#5V#w{JhT4b~tG9lGJ`mcW?*Za?Yx`ME*r z3$m%po4wY8ygmURCY=y@qVTTHR*ix9pD~%Gmc*S$U zskW|kMyBF6luylxZLL>{_zLa`QvV8p^`#$|1wT%d^w3!GJw9aY6u{eW4c=BT&kEOw zyG_beg_a*_F=qtF$}do+bX`fI$~_F%+;@lMSxliBE9knCg;fd5=^QHL{{rJhVPcr` z8m38bvXOTkN|fS{u!Z^^3W5RcUmGde)j@EN|Jta-mMt(TbeV3_coW-gxFeVW{L>0b zE$ehCI-T$GR}|x1BxxYTZyc5zCfBP1rUt37(gyXfTJfl_3c1r=Mm==rNT14Eh(UZ8 z%XTDV4qgRhJOpCa$xa0(_~e9Qi}dE0LR(`2)Pjpe5m_iMCiQilHwqrSQ}QnhydH2S z;~s<}Hod9@biZe8l4dI|DOVAJ^w>3C+wzUWv8*-;lD|Sa-Nl!JCet>=fnlSA;58ey zJcY<;G-`f$mmEFpAtg3hRc)pizpU58m-#hUU1R292SP3jA`xihoTQ?f5PiRF*%jSS zG6n-?p&VxL#d{zTcCB&om;w2Hk2x>5^A2Z;cHJ6B^vz%rrB%vzK9UFEAk<_#i6*pc z@VF>lYvKXdy@vPeLZEMeXJ+G+KJkNs@?X>yxgfgLXw0ll#b1^vYfF4f)a!g=UT>46 zcYyP~r{e2??q?7Fx9;KrufqxfA8Ml5ki?1i6ibrJW|BpZ*t#&5I+x6;BF|jbdoAkz ziY=CgE)`xwURGBLXg69w5yC1#Mm|Csouj+W*LLN-?Ai(?Y?``v7R`DM4RG8)Z+z=} z6fj!p<0_NjojPLBxe=81w55MS(`r3$?pCHlVp54Ef9^z8j)Mf%9Y{#O>hk4ntGKo~ zErd54U7P%HwD-q*I=%hjWI?FlY8c^nvUwi}8L$G>=)@Qnuve=AF(ryjvWJtpT{9ci zXnU2DYv~aIoZmAti(;B#o%$kAv3KZ6$!a*E_DG?@B~^-s9{%0cMu0$^pQy)svZx*^ zWItj0yW@r2abL%?^&DAA5aTrI&r;^Ms6x4qQxPsPdh_jcy_M zcfJMn$p?zZY1RPZXAuu*SC?-X0KF_7CCIJKa5Xjfd|#hKoT?`2kz(gq-@bzP@)0&s zY&e|7(kG3iC|u!FE|B!{v+DSLWbK~G%GUtfryR5Tm=Wi83pwH2A1|O8p;d7Ia0m)2 zZCOxs<_avwlRk5D-oYp6c&aeM1VwhNT@p_25-%5Ij1MT}c!B9GfSO}W4xnm3*)tw0 zk|*9HzgHl8cA6~gch{UbgR{3#Ex6R(7OWwqB>P=g*Qs?;xdulDXWpdd9S@M5==z4M1NCt zt%$0sh>m)1J$%5U`*oM$T={y0XI&HTqx;yKZ`3!)UQc>px~NgH&Gns!MoC{hZr_S? zT7`1aU-r0n7TS?FSWZFUT^2?qK$%IIa#TXIgwEqvP9=mgqb`_GAokXRHm17l2Umrv zlQHpoUOvjVoe|be&I*woU8?mB6q}?bf~1;|pZD$K+@JR-d5V{{^&PY}iKL|5`=a0s zxndrzM13@jWTlDUkLv8{kzc^}qzBF|(}9JQPT5t$#>^+XZL>y9jnXb-z=jBa8aUwZ z#tqk#j(_t;BH6MOxhdKos2!h|EWy^M_|o`~Nk{RcFP|jUcBoIw5PhhkXnATF%6yo{ znA(R27x9UWHHVgmT<`as#7oE*z7en%#Ee$w2KJoSR5=w`&kj9E1<5x*_#*a#EDUV; zhYLgtYvfFwTu7s2)oo}c0fx3f^}-FG&S&hV#^p1G?xWMDlx!(dy5pMA7e)`W9@}<2 zOTjP*TC5|vEgerJgSjK>HDi%%2m;a4d;=l_GSP6wjOlq$Y!#eoEVV6}ApU8_$$pha zyRwhlrmH92){i$Lnd7$60#5|EXqeTUjV4L!?=XG74dV(K&_rGg;I7nbEFR444ot5&6dAjc{~s45a8OW#e8M-@z#K8$zlM;5Gc|5m0eUdhe zX{mipyPg=4S!eD_WmZ&8}r*mXo{fghX)^&sGmB(n7a;UjyTw*zO+^nMD%^2S3bE zAwR#K>2)juhMQM3CLK>taWSz*w-ax6rq=7b+v&4GS9xEc{OJiF=pKx~xZfHzLh!TV zDy9~(`JFI$cq-d^Ct!mrv~|tBL}|E_)hIbK42SKvX=5skM9yIm5zuJ!9){{hzJxl) zsurp7eR*(l;v&4HtD$&r(COb@P}<69{kqmm@Z5XA+K{)dN7Uy4bnp#yAFgX3#7#i@ z14@HOxFL;w6pKe6q$S1PDID3;4I8ltF%w9e4P9SlircL zJ0a3{zd>K4^FM33RXWd*|BOWRDy(qvu@S;nTQ%0Bxt*ZF0i9EarwLZ!SOD6k>F9oH zv)K0L{`Wn0n7+W%x!JdGeCb**?Nbo1GSurWP=~sLbmHdqYMr8QWRBUsnhvI{*`ms- zPrl#mrmj)?s}{%@KrgZj2^l@u7?eOJ*pql;g?{{-+P&i5iiD=8>PIp*@+dBAvXEsH z9IbU;aLJRH8^`?4@3VO2J|G??modF9k?t{Fj5ZS}RTl&^thgNP{v>cPD?=s9@x@!D zU9Ftf6l+LLw?q2f^lS1?9Fm+plUxjs1nk(4QB$p=sN6qrg;mFC+*IkaPgqAqlwry$ z8}fwL3V*;oTnm4RS4yLDQ+HZ5&iys$SIeUL3UdJm*r1h=hn8}b-K0-QN zy(}F=e2=;HGOjOurvVan>vMKnpYCSUP2};NB}Rs<+zKB*dqhRFm0mQ#V>_YKzrT#A zbh1`r)e<)bN*O3(u_i@4-wV>gaYGe}7lckuX^bCLH7wuLcmP{MhiXRRUM}o{v70iD zpK_ir1-SO4d#MtoI1~A;)QPd=eGt3910gb2Kl} zzp!~5QOtNVY18#B@=w^@957R8YR@*4#F{uIw4R$0Y#jy*TeHZ@87>mDPm$=c=4mPd z%jyV!1B@4cg-)C=@?gJ?zfW^x@d)?CD6PcL5ZlIT#%TgVBfJZxGLzH%7c2cca=nE- zxgoP-3kWtC3I*WdggV?|x>9rm?=z+7wxvIv_I~{{<9vTs(HEK|!dBZ<$2q)VyYt zcz;KS(FWkSdIpglLY+@~BI4zXB%9j&I6H`~aNE-AZKi$V`4-@&O7o^C_Rh*2%}#JM zKHBd7Vdux&^hJ ziD+8LEW9|Xp@Y67sXG=|_s0i@e6QaTY2vm%qEwP@#04hb-}4v` zh_`DmVg9ilk&kVjf~^J;7>;sKcmBoZTPmO`)EGXsv>L<0OU+*<-s2fh(4J;Ag1b2; z+tYCAu}SG<=|xNMxVc0Rc72UI1G;Zsb5#-TMP;k!53+pz2 zJ=3=Ii@@o>THibDEK=&U;CCw(u6&r(qP2ncexu$&9IF$62bK5;B>QZZ|MiDK_?~qO zeU1&fVj_BT|Nf+K7pI?J;g)64X<;Dd}rUwFomJYl&oT+bEL2o6C zJV~_J?1d_%qxP>l_6yfI$sQ}as_gY2og6$J5Sdhpme#SkrI*7paFvwzI+%l+M`O=? zXRn@Yk}-z?n+U~2k9!eiD+ND}O1_md;YV!l!mpMEUi@*%ZTFw{F<9g`T8ia}qC0wK zQsTTFoYr9jt#Mi?tD{bvE)_YdiZpH_kXF_D+HGx`m*fP`v>$YXxIX=mTU^?2(XWJ| z8yg45tB=uUdg(8|^t`uj75$fTA|#!iyoPR5P_B>+T!q?h6kERx=BRyHFWS}{ep@c5mfHoZjdu!$qI_;sHS zKnikqS>!0pfvc9E^tzQgj174cy!O{>WfanE)p=*NeV?V{ z-tps>U-ywe@qU-))5G@J4f4htW~%L{t2{0M!FV56`L9cwccAe+LQ;k2RBf!B)yy{w zcJr0(D<%&@J-I(Uw)J14x?#?J2oJ$a!U^UFhw97Z;EwUGoXMLm4|J&WgWm5cjv5Tn zu8wEV=CviEOs=ZQr%hjYnQesw0RvOA%IlDZ-qEh^vQYGht z;0e8MnIP&4Ia>y%>cY;7nkBp{?LSDQYKq4?wAGX)-EJ@Yu16WFv*$0Rjr3VK3+N;i zB<7F1QUs{AW7H<$7TZ@`;^pclQIvmTki~+V5?~k&+%v8?!#0QCl%mp&_^e$W_%r@6Ei>)wYou-gI^GV)GG8r__9YLK!6%%P-+wdT z+$G*IxzS<0ja)^!wivMc7d6@Q?kro?UOY&#V9AdOlNScXRj9urFY})X)=Sj(Akdr{{H8%NG4ytd10n>5t^*zU63&7N`P)t?kDQ-up(RNc$8B( zvHuK$v=XJS>z&B=cDFN3u3xVv#VYWAM2HwgA(8+#9`+zK%B=^;^E)EVaRG1mZmbhU zl38k1O3XdQ-v%s_@dgxYgr3|7*@8+jYgYQ3oY-nS99&U)_ujrWZQb;tp~uI+FvDY3 z+=tDx*1ZKtIj73h7p@bz8@f8|Zb?nVf@ou5JY_OiAARZ8WFp3S^y#`njOMmz{6C9g z3;F;UFB63+5mh)bFgwHIVP%EcB1K8#F}Oa|tJsf!s;JJX?dG%1sDm3|K6AWdmP%m0 zg}htg$HQh>OE>sleS}%F_IYmCq&q3j<0vF)Z*l_TX(F}Z4LIdbJUVvueDS)GJ7Jd3 z*OAbCzxVo7MknVteurv=JpdAY_8YlD^^OAgR#q0pPYS>t0`tp`koC<{MQr|Uv<%K% zS49e1Oj)JE)Xmm;T61RjR^>}?v;h<3jkv0aSnA@X+t8QuHo=a_o#@W_e9&#)J30Dz z5vGDLzag9Nk{?qxC+9tS7vWDt5udtOgY4!s``QX(z`X4zN_aS2G+{SV)TP5WwwmLX(1oaZExpz{s&NInexQH>o2|`$z4a=%QKj_aH$W@?+N_6FpG+{V8rV!Azn1@Dg1Bavq42gng$TJImu)PQ&BWS-manho2F^D(ZEgu>?M$vt z9>scj@V`nMr&p!HyC3yD7-1}@oV{J7;<}#gVbbAO%2eRcVx>Fcs?e$bzI<~}WD zn@R9Q*yYI4oG8>w9PahQFq=M>Q^xayD;q|Om^u0Q5DyH3sD|46iP4pMcr(PXOQVT@ zrOPPBlXx(G4;lsG>p>u9%pNs5f$2s?Won0@200|{G)%KAGrN5=Ic@lFjg9!rQAQi< zEDU#WAu^e(Fozzsi&Bdmqg!k{%0?SEe?7#(ULy#uD|5$gEBdt5Tg&FCjO&&;( zQIO}&6kzzeYyVmA2+OZz(Q|!WN+8i#llR!O-n$yXMfRogwCHLgNz_-=TI=U`0cVMc zLM(TxPsB=P+h*WhhlZ;6S8_om;uw!or9IDJZv|o8(cX?M9>b)#58#3{Nq`=^2~|6Q`tqzr3nB*YAnWp6 z&ZB8NDaEJ4E8>lPigl7Vd8J%Q(YNH`C|F&xnLAV|3R8&aTha82#q#Myyj~^emeAiG zq5k53Y(w5mG8-YehhodU%&>wktAi`@7Cf7$+!gAvGt8F5&PPFVDF4{0H`iR1ll-ih zD3<@6o+_XJ56Cm4t>k_g5D1HC79f;X!iLI;=a}A`4i@YTY!NsSdztr9G7MTUN zA47xI7esQfrz{d<#nzY~p3m;NkXSJ=(k?`g*!DmCU*YZeQ|`6dA;kL98G5t&29j~# zu!Eexo@J^dbRE3)&A4Vxcpkg9VDUHTp4IkWU`HYKl?}nbqd5j1Z%z~LjUaVTOaHM1$TNtW zZ_3Mt9p5x9Jo^0+iEAeG)m73zJZ|oOUfntS{o=Z;UvWpkn#BzcyB#C*Kmh+XIA(ph z@mWQT8;mtS>6`t8KuTpYSAM1+H~8HtTBzgY@fzM#xuq=$-Sx!^Z+`Mo1o#(M zLY&RbrH{h1x0)C3To8(mMgQYNDe(OGc zWtaPM_Mvr{E>$sL!z z0J}r`Tk5nt{ktPpDf!j3YB5|!V6XNjC`?&dwJ!gn_KTNKCM%V2sY1NazQXQ1+H}V# z{81}m)z2>z=agYx-8&9)DletarSH9U&p$|ygg76uqJdOvGxiI zPOP}4_zDJz#0GkFE>kVF_w){$A^uWh^Q~1~uNPsT^cuo=|A83cbd=a+!2^{OHQjRu zTL0^k%7W+c-)+Y@qFIE!JgHZKI{VbnLnU_(urxMt;W5%wbGSizE?;K<%5~qQM41dG zm~z9X_qbn`M#l9<>2wtXm`)Olo?G7X3$oj@IIJgT6sKRO*y@NlNx;!H{UOxlTpDys zGNrC>kWnL32S++3itKnhd>tpyVq99x}#sT|j=bWzle zoO1Yr(S&4POKrhrW6z!DERIX?)~@k%;jTRCTpI8Ny8B_*gl}4>i`U1pv$z$(<~dr- zv}0*nfLu?mrPscmHz+Yaq+7#U2|;DX62V6qp}@a;f;Sw_X&M^%{vtFS`-|dMQFY@C z+(+^iZq|16B|6=6!gwXz=<46^o+6)UD4w>q7T(V0Yu;m5&KIl5#GK_Oz;RsP8LQER zS@y;T;MoPe<41TDCmS07*=EGLu%VwglDSTyBB~lmq6K_@$$RrxZeqwlMb?e{>dff` z@md+w6Bu~2V=$j{_V=gJZ0JEx>S1YG^upB-Z zp0gWne_#({>XlGD0H$m~UQOY-2Ge!spep z;qU+oK?YW7h=Sf9muTFj86#34T&q}q3#_@)E;h(|7G;=l%CbEj-6Q6+JipZ342+Q# z1geI&qdgJ2zZxgadp6<-D9=S95D{(=7-zHXI+piPOP>>dsY3Z+SFOeW;J!PB9k`lL zi|a;SMEE*)Hne&p1+pW#^!D4(4mgQ?$ zmTTqIl)N|peDNP5b#Ec~wr;_k?mbXP;FK3D0$-NmmU2^hqp>G2XDX)E2dS|)@EQfQ z3&RkZvP%X*KIW~6o6QA>37l7YdjjRB7jIzmR6svUi8;l&=Hn!>xvn2R&#JOuR) zMDeNUU+Kp`n!6U(<;+D)D0kWAzKJZ$sr#xf`}WZ`xkpQR(NI#wqvYw9hjCxjG+9%JZ*qWdxA;vm zSB_Q>=+s1g;NRh4<8kcmGE~OHMp0o83+!RDkz|>9Q%{xD_-U04U0C3wtM-Mf626~5 zx%n(ksEjMJ55N1Bc>63&3~zA51bFe360mrrd-42aOvq~`(yqEJomms{nj}GORL?T1 zs%=krB{%f&6r}Qc-jYKURe8-c0D7uiDkCZg(#gJ6Sx)hAn%RWi5+E|Nsi$9TzeAV_z}bVpPY+S5d%>=MxI5QTf%sc zas*^GWI{n3>mV`bi?!nXu{0019e6`Kwiz}PvU`5U$JhQ=$(yV%$~x1!;P_dUti zGtykYjgo#vjlwJ>f%_4d#+PY6t%QS$XGf`wMs|QCTcnW$7!w24-~6?6 zmEuGgEO4ff&`-@W#b=!r?IsFm-qw8f615@M4x{8}VTd0fh9D*`NvdJv^}pgycJd&0 z!CUHG`SP~>^`7s}zcSbMu6pfw_^{NtbNk0fZH;ALM36$s*ILKT@`mMCNAGJQ)~7D8J;(kfXdRDam9|ika%f@0w~1U zU$PodIB*#rKMs=Cp|R`^!=xZN2yRim`#w}8jB@HwVy;0~C;m(J?}*)9w|RVxnH|1b zB@MDI|3dbGu5*E7j@+lZVi-10u^l>_^cUM-^)8|$AgaK_E1D&wfxDvePV;)w2%!VP zM`FlG%#PAejOpDhE`2e-oZWXv34omFTl&C(kq)v1;Y~D3c;_rm*m6Mv;6zaKjBLFI3!<<4kXZHQY=v@ zQYAtrSo(3^%jYJjE`$9H_n#aL_6})(VUl=%6iBLU1uuC-B#I64Hgx)XQCrDF!6M5= z6(+snB|SbhgY2-L5j_@{evUU$jw|lAW1HYN4|s`@4KcPk^t@H%1 zbyJwwCqr=RMd0?6cVz|ySYAgom~_DXMsm_#SZBId=Vo;VHf2J|5TcB%eOfv#iYY-o^M1=NDJs+48BNKAs!5{ z35w<@`E?(KI(Z}c(hSu~$Y)hMh{fQ**d8IJTPj6d`FD7PHqqq){TBV^wpw*pXeDd! zPYFGG=~^8c1X`wX-HfpWe#C^^abk-wNEk#f5_I;$X;_wmFPQkhj?$k*k~d zygcP$$)YyF%MUp^l)deWfZ3qq?(WpwMRzEO>3-aB^xTOybiWZP4swZmP5B5{(R@Ki z8lf5sL$PN8jX?m&Ml1V%Zq_lHd|8#@_gXXTZ)3F1cFNIeY5=`oQzADyo*^u}hYdv( zQ`)ZcJKyC;Sq2f^OApMXQMUXlA#Xx|ob~^hddsdjyJ%S(clY2ygFC@BNN{(3fZ(o;-AI5C2oMM^ zjk~)y?hXm=8lY+10LY&M;hO*|9~m&;(C%FcThS(# z_4$i;hbBZM4HPtp+DqQ)Wi6*S<RP1TnS&e~j!;-GHxPxDBH80`$H zRO~$xN4ZlkCI()?O?t24ygGd!S|A4_BPl>+hnxrK2P-EDmldM9pd~3O&Cy>tKGMWE z)N@S<*)p{Z?|!aL)~l{SM?=OduY8?O1GK2AGbz46KawGBG51Blv0QfZl2?_YqUn3W z3L$)f#MOcc8vlLPc*w^jt~*g8rNmBWt?p*764Kc1ZT1IyS0^bM^?WgvKUZS)n!5pF zd*Ok2XM(g!{fuTBknf9cLtE8z1`{(ime>t&BI+nQCts8Dp$vl0pLPQoGx})H zMo7A-`0u=J&|S@hPWbfw81gzbn z#o91ii?R1eIj$-9@ViMIw{0w#R&)BMi(OKcY=B`(gu8|lwpo>{BM>ehhd`?rPoF3i zj3@h)Baz|!sB*OqB+T-7>vCS4IEu*X0A4-eKRu@*zF``rF63yCiewDwQ{Sno5C@AAtaOKivStNUs_AsUk<6Cu$8E8E)nw z+5K>?$-Gf%x={gzvBnLAAAC7(UnQ$Z<{(VMwN4 zk5d(N&Mz5{q2b2uH+G8ZPJAsf0iM*1Hv}k|Sgtd#%~esp`6#(n-cL5GKrO#rY<(i= z^?qvgBkj=m`<{TD4U``I((J4{g+HpO&Jf|v=-&aJek5v#Zm58ha9=j>`l=k5TNWs^ zBiA^b1c%(t_qZ0E_emc8nDTZRM`Z%>7aHFy3VuVC_C(?T#`nO5(=h&83v^i5sEOJ( znF^qO^9;!E6Y<Jw-pSlAA-aV~@2CnN0{LU5agW4HgrT1{s zmkTy5@<`ezOJSOe_`6>@fV}SsFN`;ZaS{B8VZRFIgPrZCBR9821WpPS3v1?a`0c-| zTlyhSQlWz&kmoyM^FE1%CtZLw*i*)jo@QaS4a~5dwzuN_!$mM-s1qY&w>EJ(8Cy0K zviq|YVNYyGZrBrEZxB^|aF3OP>0YMUK~kKjvwVIy5$+Geq{})1@c;Ub0A?FvBFI6Z z*za2-^J(WdN$nbT0!>o0#l*JwAMP9>w>nynZ8rBHt*r5{vJ{j}Q~_>T*WSa8NM`V2 zC@!!L_Q%Ri_xKbg?QGpq8<_}F1OMF4%AX=;7hXZ3%hE*0;BcG9qiqzcZ91>4`T@F* z5TfibdB9o7QXtZD8Yc)VIi{2V+k7OYk!$Eb?r^F=+-}R?AISru{_G@-Z7CAZDd1A8 zO1Ilw#g~aeKWw+=)#H^upCbBE+#F2=J3lZHE zj&Z@_SZj?DrRx*JmDW7XI&w_B>kJcE&VSNPVgNYB4?@#*0P%@LteDp3LvAl32}<+_ z?ceDbH-TLUQqs~GBaidD=&}*|Bx%-u{d@9D^8EgWMPmUn$nhg$Ep_-$Nyp<6k4g%~ z%0w2&OmQQVc4B7jYre+xiY}PNJ+og&lQo%)M@207FS8XYZg1~+UTAh>%@CWHRMUb$ z9z3>HH^N3M^x4&>^FVddZspBe*`aGz#Py2{x={M~{Z>hg|OxXPo%9ze4w88AH-f%;o!{j#%Z_TU5eCka1nSr+v! zDUy7-LvF7{J$Oo?vqJt3LZSw0&U@n=c_N4>fc0b2+A8r zWhK;XnXLO)MQYXqZ=~wN<_}U#y#4rBL362_-bcg2e1jGq62~2ssQP^?PrhnrAk*%t zAenpLIWZqjg6{?bx7KP z$=zN{ivM_1MX}`HM|G0TPXH#x7B1cWGx0U)8C#mA+tpMM2Q=HPn_k@Kb!7@D8(O-? zvnE{$udjTwj&!0^4Oy!eS(Cq;ahF~izJ9C7sb3TF%cR1mJYvqs`)G9Z@lnFJ8*>mr z>)9%+Pj(T|-3dU;>4ja%CffpsydwZB%QkEAR|&oE{nXzIKjRR)XO5C8(!)--zvu>Kv&42hKb+NdwS5Ysio)f54Fw$W6&t#B}0N^tU4!RxwUDUJNK9#UXxg|JnO=rz+qc+}p<5?@G zU5xd?`I~UI*~kk^4Jwyjx8{g=p~Z#@_iT33{;d? zhhbv(^ORZq>fPRn=k{Oc9KKrtgVr;QV#n5+V{vBmVC*4B2w>$3mxI`Eb*-eYUmP*4 zT?>LkB3c^{SagGiekVr%^k~cZ-P3;T1y9Vb4Ew#`zf$0oPJusPzkm7)e<5iE&)X?_ z4v%sy{x{c+#V}`Rx-};*xS4|LAbO=c^2s7TbHynqat70k#GZ9Ngoxna+ zvWCk|+Pv7GG=0fJi?8hBB61(Y>xO?d>pcbff$L_8esZIU7WnX_i713@diCirlG19h2?shB5K zc|IU2I_aFsY~ycRSlbgnog>>`EAv{~Y3J%r*Yz2g+*z*nT-ft$Iq_NW&PWD5;7o-u z>h~=`mt)~1tpI$ihJDfA{H;)u7;;YLhHJuaq4_JD##;Hez3Z3{ldK86iTrep@fNY? zPeyiw9C~{+)wHjZDKFeP9y>doFbSOT#2fuMV$NM}%tVj+S$ufQrnQeTR5Ovv4YZW( zJ{ebmwI=boW1sF#5(WGJc2&slh<#*L8YH#{o(x|jd;&2(XU}WzF4@j2|6y)FgV5C} z3qFVig7@fFo$9Wz{()j~RIKc~`Z_&SvHdZrQgN&=#|gB1?LYh1b-8Ceb^dkM0X^>( z>`o~(2AK1-*caWOZ6Eyt>MCo^c!%BSMw#|Nbgd0RQd*R z4<=DmVn5(`gdBv=0O;+0i8W%CjNVi_i)~pwKgf$6T_y@w(3m6sicX)*8->&C)V8@( zF2>Sfddq)OK`TE^ViW^SPyQI~hDfdgP8CR8ly1nY1}ztsFu(W2S5fZU9x%lbYV`G% zfA}Hyw|RHUM#6!#KIWcS>oYFW(V!k>P{iF~-P9fZ_S5-8;@%4}4d-3FRet;(8YFQs z73r!&83G=vn8fzrd_;!y{y#Xko^ zSFEheD(euok@)ZHBv2BTpNDeCU%`)#&`v4b=GY&H7h=MMpdj38TOeb;aFukdZD`q zembJR&)L%PVFe=CIYSFB)T9net0CsSo=7r>$VUY+T7o`Qb^i#~x9%VyrxUN-N^xPJ zaBgw~jLM7faXE)4!HjT>*;2O0VB4FMn5_LhV^P|%z(bZTPE9!`xtPALf!?_t+s0=p z((vg5@QQ4P0`IRL;${EhS4o;S1-EUGkJm)B^(OlDFQA71aqjPv_scX7B~CkL3lIIf zl-=uBz1rIgLU~3C-Qi_lj@Ii}Hm7j4lPBURZ<$F;Wa@^WRB^=E?VG1BcVuQocSgJl zfG+hAr=sb-xP44(Y5^rWm--(xVPtxieRTbzZQSfD)7+=@qb^5S5Ec3^J|(uOIyA2D z7GmXF1)yzAp7f`|M+6eWE+ma0FgFsfjQr8Zug&(p z`foU&SuM6C=hOBHJeh_!yw>S#`spf-M^61?CF!~Uj~Wsq$?flZbB&R34+K-TQH#N_ zDea{|n>)_MY195V*#g6{8y{*biXGK%4}|S#Z$Lz1@j(vfG>z}hehO+(5OlZs$8t;S zu&PcE)H-9l2H$=_bP2l^e>PEly+4eGl7V*MIF^Hbh24=`a?T=eUDYW7fplIE- zKymREyJ!dJb!jM*VkUkwt=+y?pqYtfPh^_%JKVFA87iPOmXNvKu~9s!K@@x*lEB5n zKVvUWCP8@|vQ9_w0xb-0C@u)12Q- zk8PyaAWtTtnm4Nn3Z9S5AO78wnZ0(2CXFeq(i;Xc6vUhM5993C6 z=J(PBSMr)-(ti-RY9T$RqVI95h@Y|eN^`p**Xyy?OoD76j(f;(_y)Pk8MvMB!favK zsUuN@mQ#yf43GpkG=KU@3x`09{*9%R3nA(y}1ek@=A!ogNjDW zH+(uV5NYuHS)E}5;zoPMR3;~Y&wVoT8RP>e`S-KB|6EeeHLlXVyxuhI4vgL!d|j#j zT03QX-WvYU^6R0?#{6Oz=MOLZOYUTLgXSmCx$@csW6Prha#DBSm>+%({3IybPBGD@ zJ@b)-I~*r6dvbUT$GqsVR$Cg-7&|a}1*g|HMcL#{#mUdmNo(N@%7U<&v{+~dMAk2oOlGFirOX4XHkr$ zvvS0zK}~v&Of!4qY^VJYX0n(qRkKJ;+03sI^bBp~9nFRG27$llx~5mU=U{kj2ZW1@ z0_ytRUkggc<$MT_J)YwMO682HqGgg`E~C4}F;`>4s+RU)8*Kh2e|R?bKFyfGpPeaM zfX+Ij4b_O0Ej@BD!~IMZx<=|HeXe%sd>3BCq?AJ99L-ka_LskWTDtc-)ngn`xN)7# zM@Y5r#})21;z)SNr1KkH%Zf1uoXO=^$@<%&FH*}1Ra=b4tc~TskqRVFd3K_fqk@-F z;il7Q2xMfUZDG%+e#E^C@&d&|2N11{=Pb9y4}Roj`XpOyXVt8BuLmZqW_LBLh#HP% zLXSzY$hr23!K`E*yEjUxtJ1|>Fl!O)h#{O)SFCP=GtPPa892WP1>9kig2MR=f@Fd{ z48@ZE^|jg5&1irq&vFghWXt|juFCM9lz0`KR6fpiOt}|*sVs{Sfhdeqk)#>ohpWxp z8=UEZ;Ln}qN}5kUe7?j%DYm+tA;s-njj{C~Xws8gHIv*&Tw}nrWLB2RJGKFVinUU7 zd6QK2%5Fv|*sI5v&eH@-BzrN*me`4hj~WBX1zSY$+!s)A`SJD}bR%vmXGPk__hxDn zS76=r0Fw$w-vx2bw!3MI3t!N+tg3SvYEt1 z?`%3_Ir7wST1pzDA!wHq-hQ%+G_~#+QYr$x+yaM=cTmWWw=3$U7dqhKHWeM!hTB`% z>Gi_wY+66z_8$ULM5B9?Yjwe-=ZZ#Uad;wNq9x zzzogCTX3fj3fN7xB#x#`fa#tN`7bpzx{?$jC4_auTx$CoM~RD{OT}c1f%_n&)6xwe zFheJ)Z_x5c@f5Q%J+XlbYKhOc;VxC138nmp6;naJ{C30~F;-izj`B;+ku5kR&^HK- zm#FwxlPly=E8IkkFnV_`y43NzHD-Lroee5Zdn+_qwDLq$s*BCjCa>wrAM6X{yW>(ao zaF>Dr=IB*eUW$RfJn~QK?*@rfc1_J}Agu5UewVY~kIF*%_LrzBpM|E5P0Oa>ZTVsH zb7vdpwE+S14~;@>^rad)nMbT6jeofB8gIncY!1dKr|{_3{2wfaX*zW-0L-Vf%0{HR zOX8BzE!rb!LmMQ-WeI|LRf-f27=K+qtPz+V=yYJ)+U9HgyW}z~dQx1OJTF9Tieo+7 z2qr%I5b#UXO8Si}L80z&z;cLAr_Tj7=s(30(~D(c=eg(C>f#%pwW3vmDpv`N@niIJ zJRcd`kb(4g1wmARE2Gg1pqn_rI{y>5iRNK^%6$`7H%^TGq)`O-xsepU_G%MO{zD3++PND=i&=h!-+S4Jt-)-b%Mj)2RvMR)0-;cK#LdcL8LOk>z* zPxkBe*IHxBVg1IYsCO4WrU2`9&VmKl@mvz2yZId5x21dkqa!+PFWL+4gL22#qZ2o> z5Ze~#9r?c_xuh??fQxw;T$#GC&|8H%+2qk?U+%%l;?&)dWZ7?pE!y8Ey7ANc=7>^9 zKo6V2&STA>$Lz;N?OCyE2tizCB0sq;qiL#Ie<1fB8W{XG6k_@}N}#v&OTn9iQ*Gxo zg608L4TPEx?HSm3-K}>7nn0f#in+JAjwYWVZ;!Wc)i8JfEWH5);3CA|0p`#aHbtM; z9F^Dk8*z)SxPPTnZUeHww8c4S;=1PcWN`lK!>(uQ1?bZ??(T2}wC0ur>T!)WwJ5_( zywvB6KAvx$YIY@t{Z(oO(frG~k;$U`sKgH;Ln%h+<;Xm8tJ+Fj3gqk;$MdvT+>H59 zDjS;EG$X*$fl`LmcSXQaJt!3xFXoGtgR!n|iH+=ftex(Q2)f;lnzpseEQ_fD5RfGOS}fy zHSUB9w5WUgN%hUccbcg}XaqzW@?(&?+3aM2f^mSNn= zC(D*wiIDSXKh_Z2{I29Q#=MB-+0e49?#pp|?HAqHKa-N~)GQ;GAz9M=%_t0(x$0z; zyKvZ7yv|{@v9mJ^)~>gNsdA#BHmOTVw;}AWmsz3VIY(Isw*^j31$$c?qw8@%TpT8( zFpOVSOe1m;G)3JqqOjs`=2CsaXY!+H;p!|{Jq9UORf|O}{gW!*UYC z`*Hu9KI)sOgf^8`b02w!VGk6bKs+>81=i z)Oe$)0>Qp0o=OoQxe+evP17y~PfG5vhpxS$&6tCQx6cZ}6-Jt@Yij5KFG?FfjG5-g zefh@c-k3a$pct?cS};h%prAXRsC4y0uXt0x$9m_dRdVvH!qa zT*8SuSM*Kjm5Zaf%AHv3Dpo&5hd7j3xsTM z-S0&=r1eqWLgJgxn$#O0ClGI#vVL^c&V*9>_p zr#}Ab%@sfWbC&jH|6c&^^5^wl`s)SN_TAf*>s^j$$n648Qvq>w!CeHz6MHD-rMi3Kg$?{@tzg&V7(`)Qg%TC|K0*3?s+q z4)^P!FfIPWVpn z5&i$IRj8kX=$Zp=??P5BEnh35`zxOGp+T-~fas%S(%W*gdD9IBv)969{tlXyJVxUN8Kt-y55g*8%hq_{uDN>Fk>q%yPpT>%ba8tfIdPWe5 z+K}VFL8)dqJ17*M%@(TdIZCX6WUMO~6LQ}a9qxVI6FTla0m0U*g|#cd4~t}gW_G#& zjD7-6Uk6q(*+4AhL1_y}2%&&G_Sx6iv6T|^?^M$4NitKcUMRnJ%u<7GmvImGID3Xn zjmF1&bVlEKNQ_fS>8VMdCQ%cyWKo6jY$|QY<`kYI8_9AO^$t~ATl|QHHLPK z9)x`;=DvMkX>x=vpFe!t7WN4hTlt|B0XUe=<_6zK5`9! zYg<3m&sBE_7kpJZg{%)1s%e|mu(Q$P-$$BX5cqQH&4jwgxU~EbhN6R0q5Yizimx_g zvw+fD=t68I?FwhlcplV|D$U*?vFL1w?{mPnQQw5Pz5zXyzK=!3Xy7Plp5{kEt;YvT zE0Drga?TyAiw&dP|5{P*oWxCj9o_s(JoCGsYH!wjr|3Ul=U`^CZdfr3#OzD!YXDwa z+;*}qeYK$A^Lr4wh_q%Ds+%nEaSANNO6bfSyxREK5oSC8h2_0YiD5un4?4Fb7J|T+ zN%jKlEoB2h$7hr*2eSXSWSSY76>B?Lx z9(SfKH1?PaH@&N+J+-N`!ul^he%vpmxh%KLutxndcZ6ZYyne&k;3VkWlFhgvv9oKp zST4e5H!{V_DGYx1u#ImW)3XLLErCZ8BTnOsDb8le zAcIl4thn(1%!1c^irgHtF*v?0*)z{1C(4(%$_=UPoWD8Pop^|H&1j3|;Zsj1$5cZQ z$%#dw(jlT`|9+DoPS5teP(0)DcG30ihMJdhQEh|I?>^?G=?EVvoOMoDqE-!(c-X&h z2ow*_RuF=`bUd#H%-mryfZc-U!2823;F^&0HY0q-3uI?ybqLoaR58edt1P zcYYYLAfLE#k93|udz<_?i4Q}L!gyUT@}8ZDiQ#zeCOgL@Hi>QWsXxkr&0VZrXj_DS z76L@}O!(`P%B=HI!%illF8T?sPg`F({bn<(!`Sz`F|yq8N#+uqXuIAi z=RiuWmZ$9rE$$N8g zK{xh7Cv#4BliM&lkPQmmw=wrvQp-B?(E%M(9q%Gk%Is)nI%JWaIMP2n0#;&$Sy=1n zbvx$u$^N6+X*eNnyx&f#P%vxoXdD9i7|lv1RvcX92Ev|a-XC#8!{U!B>Lut^)wt3p z|L7w;suLhug&uF&%5a*ZcZ6IT@tOn;OH1J&KqVwE4XCqiZ`bfE$FZxY_Hci*qr;`yQpRK$QAuPJTH11_+81x|VYNVJt9prOezM!Bq$hHtK+=4LtQ-?CtXrt0{87k{y4*FKxrag>$;xs~veLyR0Vx>Brownb)bC){eeBoLZh zE@HR;^5%GGXEbl6wBKyq9E7@}fkz7wdOQA=C0aARukxAq6YePTJnyi+@%YT%(@bYT zaVYY*Ns50E=J7D(wRRm}+DL_yoR48Ip&O#5CI*bZj+JY{-!(!Kw73>X=zBc43Ss&d zzi%Ys1JwoxGA5ZZF zDI#4GKhhm1R^g?T-Dgt(j#Yl(#%&XWF}K`_FaccSoKXK5?PTRQlKXF9p8z^e+r&un zho2}M)-MUgIkYuoY(rkX8YI8`-dU1|e9w=1#%QF0ejA{`C!{Gk{&bkSTeG+h0UjVQ z_&?wpSA-G?gf}cN!M!_`!|F{*2x{20$YJ&RKb{zk{<3G(c^f8hAM@&Fst?9@ueLom zMP7uSWcI<{rc8SwOI)N(hjqUXeZ!`=EMSgjd`%sN?q_$EmpN6=L4!yaEYSdujlD#$kIsax_ zxIf6w@G04mJ!uAD>G#g~JQ~=YYh_=Vgt{K-6Z{`|p>v$%el>UoMIQ#9$3*_FH*r~i zYS%#D{+Ot%ANarIoVOkwH4-0{wPfi2qbt{2*8`0yF#Td{4~m$tc5*$`ePwH{aa-NP zP=A%wrg`W&d#^fd4)*ykAP7CCmdIi|ZZ-}Wr*dho8gFfRHnB#GMW!+DG)Zzg?-}fS zEl0t0hLHLvwIojq_WxeC6M5@PniCuQtlp<3U6aj8ZO>V-)fE>(cqs)xoR(0bC>)lM zmMeg%ufZzp*!kh7feuS9b5kjLR5-%qV46qeL`zD+vKxb0~Odb1Rkpy>V+4j{KK*WTw<7#-E?!j7QmP z6B9XFt8q1x5xD3(^4Mv%`LXY$7mGIWNr0OjjRBcYkK>$8%>Ni2yHpYxpH`Ci)1wmAHHIHIPA9er7y`+I zCGD!n*5)K+DoLEqo5{q>Gl|7gu68CjdvUH*uho;L*$AAVHTz-uobkRZ;$fF0wKV*Y zdiEOOuJq6akeE{--~HP9w@1*|d4X&5#sfi~sgU4SH*RyUPSgI|h5TZNY!7cJP(rgCm#h2y zh-JzJbfsQgOFrzU9n^#N?vj~Rhv7GGC=09!4!TE=m!AZ_5c_>D;6$*>Na?C8xQIEV zu{A?Vss(>Xk!>89X#WhP5a5rKBIszm!r$S4@nZOKk04X|IZE-z+k4cm$wrC>daVOS zvsQP1sbPUs`_E93%axy4!eodd!`pFFrH@7HF|I}(QL-%kdOZWQ z2jlifzi$2!a-jG7W5S3Pws`kzy_uV-i?!K#wsfFCx%VF+U5mm1VwYqmYUs4odFsWk zE-dOIJl1#p{zm+p_(J4t& z1w-&XwSVP{8Wu;V<2OBfLUwYtjC7^|{L8LNZ|OfOxw!S)Gqd%ct<}xsS!vWC2)n7+ z-6z$jy4DUU<>JKvDx{Mk?;oa5JvTW~4b6(#Qwcuai2b=Jiw99<<^(TX_Mg71vjs-P zNN}R6wJT=1*sYyV|Mp$)TE$+ok&5_DC?};~Ou#8>K12u?{6F^9|H#ZVX+Gh1>ml8o ziMKZrqVEl;jM^J#neiYc+4_HptH7VB7URc)2Kt{w1VBL0U$jCo1MhuT_AtwTKGZO; zD;LGpVr)rh`HO@@<#=HZ={0oRk)zv@Txr@}z+*P$%8~f}%6|l_kF@tHm}-V344Np} zx>t5bhC-A@V!g1%2*e~QGz{vlv@1h;Lh zM1@k)Y(@d0D~Z#O(8*s}$z4ZBYs;vsWBeNGF%&66cFH`t^M2AXu6F$n_;HutYcbS> zsxfc49_Xo!3+q~y$>1EEpTFX(4RShnreQiUkCn-txNiW4ty1txm+X zg+KHdJ4>V5P;)iUjho!(H&C7-{yZ~jPN}IHUD^kX&ojuq{55fBi_yk;FPqQd|F$Ok zvaJ5~q5Vx`v^yv!X=Pxf5LC=3UGy=tU5vsus8PT|CFo!GpUlAL1e*_In6onHe1S4)Sb+7fwb*5>q zF*V--?LP!*{0xg{8$+Z2MWo!td#c7f&x589GibasB#$YaM4en=@uqL=WJ^l@Uq>Re zvIKml2XKrVsbr>xS`~&}v2poY3EnZ7Gc_51DoOby)@W@8k2<~hm!E60{)9~3%Sd0y zzZlU1tLiq1#*7n@cS#?IaGx$~rRmCE7x*y*e2Rv-Q@RG82+Z}ke<~DSG=)uVYI40T za2D5V$I(KSbaf!KbYmSa+{1Z8=J3Y416DKC!<3^Bkvbh_9ruV?O3zVv@Lwn>tTNr- z(e|COF*C0&Mmk;MUurg&QKhkW*;nCSW%i?9F&*S-sJeZ06Qkme`!Rb+9~LfSQ!ytC zP#-E&*Kfv?)4OA_)oG(AXJdV6`?lN1WrT>5tBB)|pK8k(ghqLCZ*1)6YLWII4!7^I zZpi&3%7yXiQg$!?mFbptO`rlS6keAx1M8q(_6wY_;8*gc%nj1kNOqUhv%C2Zr4+hS zAKfKH5=mL;`vgJ%W!qBWLA>aPi3`~%0A9KgCFyBuyiEBrGq(@20yg#`67AyOv!{$a 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 AO8@`> diff --git a/docs/ibc/apps.md b/docs/ibc/apps.md index 11fa1e84b7e..be0457f4fe6 100644 --- a/docs/ibc/apps.md +++ b/docs/ibc/apps.md @@ -40,86 +40,86 @@ Here are the channel handshake callbacks that modules are expected to implement: ```go // Called by IBC Handler on MsgOpenInit func (k Keeper) OnChanOpenInit(ctx sdk.Context, - order channeltypes.Order, - connectionHops []string, - portID string, - channelID string, - channelCap *capabilitytypes.Capability, - counterparty channeltypes.Counterparty, - version string, + order channeltypes.Order, + connectionHops []string, + portID string, + channelID string, + channelCap *capabilitytypes.Capability, + counterparty channeltypes.Counterparty, + version string, ) error { - // OpenInit must claim the channelCapability that IBC passes into the callback - if err := k.ClaimCapability(ctx, chanCap, host.ChannelCapabilityPath(portID, channelID)); err != nil { - return err - } + // OpenInit must claim the channelCapability that IBC passes into the callback + if err := k.ClaimCapability(ctx, chanCap, host.ChannelCapabilityPath(portID, channelID)); err != nil { + return err + } - // ... do custom initialization logic + // ... 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 - err := checkArguments(args) - return err + // Use above arguments to determine if we want to abort handshake + // Examples: Abort if order == UNORDERED, + // Abort if version is unsupported + err := checkArguments(args) + return err } // Called by IBC Handler on MsgOpenTry OnChanOpenTry( - ctx sdk.Context, - order channeltypes.Order, - connectionHops []string, - portID, - channelID string, - channelCap *capabilitytypes.Capability, - counterparty channeltypes.Counterparty, - counterpartyVersion string, + ctx sdk.Context, + order channeltypes.Order, + connectionHops []string, + portID, + channelID string, + channelCap *capabilitytypes.Capability, + counterparty channeltypes.Counterparty, + counterpartyVersion string, ) (string, error) { - // 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 - - // Use above arguments to determine if we want to abort handshake - if err := checkArguments(args); 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 + // 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 + + // Use above arguments to determine if we want to abort handshake + if err := checkArguments(args); 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 OnChanOpenAck( - ctx sdk.Context, - portID, - channelID string, - counterpartyVersion string, + ctx sdk.Context, + portID, + channelID string, + counterpartyVersion string, ) error { - // ... do custom initialization logic + // ... do custom initialization logic - // Use above arguments to determine if we want to abort handshake - err := checkArguments(args) - return err + // Use above arguments to determine if we want to abort handshake + err := checkArguments(args) + return err } // Called by IBC Handler on MsgOpenConfirm OnChanOpenConfirm( - ctx sdk.Context, - portID, - channelID string, + ctx sdk.Context, + portID, + channelID string, ) error { - // ... do custom initialization logic + // ... do custom initialization logic - // Use above arguments to determine if we want to abort handshake - err := checkArguments(args) - return err + // Use above arguments to determine if we want to abort handshake + err := checkArguments(args) + return err } ``` @@ -130,28 +130,28 @@ closing handshake. Closing a channel is a 2-step handshake, the initiating chain ```go // Called by IBC Handler on MsgCloseInit OnChanCloseInit( - ctx sdk.Context, - portID, - channelID string, + ctx sdk.Context, + portID, + channelID string, ) error { - // ... do custom finalization logic + // ... do custom finalization logic - // Use above arguments to determine if we want to abort handshake - err := checkArguments(args) - return err + // Use above arguments to determine if we want to abort handshake + err := checkArguments(args) + return err } // Called by IBC Handler on MsgCloseConfirm OnChanCloseConfirm( - ctx sdk.Context, - portID, - channelID string, + ctx sdk.Context, + portID, + channelID string, ) error { - // ... do custom finalization logic + // ... do custom finalization logic - // Use above arguments to determine if we want to abort handshake - err := checkArguments(args) - return err + // Use above arguments to determine if we want to abort handshake + err := checkArguments(args) + return err } ``` @@ -159,9 +159,9 @@ OnChanCloseConfirm( Application modules are expected to verify versioning used during the channel handshake procedure. -* `ChanOpenInit` callback should verify that the `MsgChanOpenInit.Version` is valid -* `ChanOpenTry` callback should construct the application version used for both channel ends. If no application version can be constructed, it must return an error. -* `ChanOpenAck` callback should verify that the `MsgChanOpenAck.CounterpartyVersion` is valid and supported. +- `ChanOpenInit` callback should verify that the `MsgChanOpenInit.Version` is valid +- `ChanOpenTry` callback should construct the application version used for both channel ends. If no application version can be constructed, it must return an error. +- `ChanOpenAck` callback should verify that the `MsgChanOpenAck.CounterpartyVersion` is valid and supported. IBC expects application modules to perform application version negotiation in `OnChanOpenTry`. The negotiated version must be returned to core IBC. If the version cannot be negotiated, an error should be returned. @@ -187,24 +187,24 @@ like so: ```go func InitGenesis(ctx sdk.Context, keeper keeper.Keeper, state types.GenesisState) { - // ... other initialization logic - - // Only try to bind to port if it is not already bound, since we may already own - // port capability from capability InitGenesis - if !isBound(ctx, state.PortID) { - // module binds to desired ports on InitChain - // and claims returned capabilities - cap1 := keeper.IBCPortKeeper.BindPort(ctx, port1) - cap2 := keeper.IBCPortKeeper.BindPort(ctx, port2) - cap3 := keeper.IBCPortKeeper.BindPort(ctx, port3) - - // NOTE: The module's scoped capability keeper must be private - keeper.scopedKeeper.ClaimCapability(cap1) - keeper.scopedKeeper.ClaimCapability(cap2) - keeper.scopedKeeper.ClaimCapability(cap3) - } - - // ... more initialization logic + // ... other initialization logic + + // Only try to bind to port if it is not already bound, since we may already own + // port capability from capability InitGenesis + if !isBound(ctx, state.PortID) { + // module binds to desired ports on InitChain + // and claims returned capabilities + cap1 := keeper.IBCPortKeeper.BindPort(ctx, port1) + cap2 := keeper.IBCPortKeeper.BindPort(ctx, port2) + cap3 := keeper.IBCPortKeeper.BindPort(ctx, port3) + + // NOTE: The module's scoped capability keeper must be private + keeper.scopedKeeper.ClaimCapability(cap1) + keeper.scopedKeeper.ClaimCapability(cap2) + keeper.scopedKeeper.ClaimCapability(cap3) + } + + // ... more initialization logic } ``` @@ -223,25 +223,36 @@ encode and decode it to and from `[]byte`. ```go // Custom packet data defined in application module type CustomPacketData struct { - // Custom fields ... + // Custom fields ... } EncodePacketData(packetData CustomPacketData) []byte { - // encode packetData to bytes + // encode packetData to bytes } DecodePacketData(encoded []byte) (CustomPacketData) { - // decode from bytes to packet data + // decode from bytes to packet data } ``` Then a module must encode its packet data before sending it through IBC. ```go +// retrieve the dynamic capability for this channel +channelCap := scopedKeeper.GetCapability(ctx, channelCapName) // Sending custom application packet data data := EncodePacketData(customPacketData) packet.Data = data -IBCChannelKeeper.SendPacket(ctx, packet) +// Send packet to IBC, authenticating with channelCap +sequence, err := IBCChannelKeeper.SendPacket( + ctx, + channelCap, + sourcePort, + sourceChannel, + timeoutHeight, + timeoutTimestamp, + data, +) ``` A module receiving a packet must decode the `PacketData` into a structure it expects so that it can @@ -284,9 +295,16 @@ packet a module simply needs to call `SendPacket` on the `IBCChannelKeeper`. 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) +sequence, err := IBCChannelKeeper.SendPacket( + ctx, + channelCap, + sourcePort, + sourceChannel, + timeoutHeight, + timeoutTimestamp, + data, +) ``` ::: warning @@ -306,49 +324,51 @@ The IBC handler will then commit this acknowledgement of the packet so that a re 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. +when appropriate. Any state changes that occurred during the `OnRecvPacket` callback will be written +for asynchronous acknowledgements. ```go OnRecvPacket( - ctx sdk.Context, - packet channeltypes.Packet, + ctx sdk.Context, + packet channeltypes.Packet, ) ibcexported.Acknowledgement { - // Decode the packet data - packetData := DecodePacketData(packet.Data) + // 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) + // 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 + return ack } ``` The Acknowledgement interface: + ```go // Acknowledgement defines the interface used to return // acknowledgements in the OnRecvPacket callback. type Acknowledgement interface { - Success() bool - Acknowledgement() []byte + Success() bool + Acknowledgement() []byte } ``` ### 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 +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 +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 @@ -396,22 +416,22 @@ is responsible for decoding the acknowledgement and processing it. ```go OnAcknowledgementPacket( - ctx sdk.Context, - packet channeltypes.Packet, - acknowledgement []byte, + ctx sdk.Context, + packet channeltypes.Packet, + acknowledgement []byte, ) (*sdk.Result, error) { - // Decode acknowledgement - ack := DecodeAcknowledgement(acknowledgement) + // Decode acknowledgement + ack := DecodeAcknowledgement(acknowledgement) - // process ack - res, err := processAck(ack) - return res, err + // 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 +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 @@ -420,10 +440,10 @@ timeout is reached and the packet can no longer be received. ```go OnTimeoutPacket( - ctx sdk.Context, - packet channeltypes.Packet, + ctx sdk.Context, + packet channeltypes.Packet, ) (*sdk.Result, error) { - // do custom timeout logic + // do custom timeout logic } ``` @@ -468,4 +488,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 index a24032168aa..86a53e6f94b 100644 --- a/docs/ibc/apps/apps.md +++ b/docs/ibc/apps/apps.md @@ -48,4 +48,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/bindports.md b/docs/ibc/apps/bindports.md index c0cfa703191..bf35fc0df0d 100644 --- a/docs/ibc/apps/bindports.md +++ b/docs/ibc/apps/bindports.md @@ -17,97 +17,97 @@ Currently, ports must be bound on app initialization. In order to bind modules t 1. Add port ID to the `GenesisState` proto definition: - ```protobuf - message GenesisState { - string port_id = 1; - // other fields - } - ``` + ```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" + ```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" + // Version defines the current version the IBC + // module supports + Version = "moduleVersion-1" - // PortID is the default port id that module binds to - PortID = "portID" + // 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() - } - ``` + ```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) + ```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)) - } - } + // 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)) - } - ``` + ```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. diff --git a/docs/ibc/apps/ibcmodule.md b/docs/ibc/apps/ibcmodule.md index d5864435700..510fd5bcf31 100644 --- a/docs/ibc/apps/ibcmodule.md +++ b/docs/ibc/apps/ibcmodule.md @@ -14,7 +14,7 @@ interface](https://github.com/cosmos/ibc-go/tree/main/modules/core/05-port/types // 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 + keeper keeper.Keeper } ``` @@ -22,10 +22,10 @@ Additionally, in the `module.go` file, add the following line: ```go var ( - _ module.AppModule = AppModule{} - _ module.AppModuleBasic = AppModuleBasic{} - // Add this line - _ porttypes.IBCModule = IBCModule{} + _ module.AppModule = AppModule{} + _ module.AppModuleBasic = AppModuleBasic{} + // Add this line + _ porttypes.IBCModule = IBCModule{} ) ``` @@ -45,90 +45,90 @@ Here are the channel handshake callbacks that modules are expected to implement: ```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, + 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 + // ... 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, + 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 + // ... 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, + 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) - } + if counterpartyVersion != types.Version { + return sdkerrors.Wrapf(types.ErrInvalidVersion, "invalid counterparty version: %s, expected %s", counterpartyVersion, types.Version) + } - // do custom logic + // do custom logic - return nil + return nil } // Called by IBC Handler on MsgOpenConfirm func (im IBCModule) OnChanOpenConfirm( - ctx sdk.Context, - portID, - channelID string, + ctx sdk.Context, + portID, + channelID string, ) error { - // do custom logic + // do custom logic - return nil + return nil } ``` @@ -137,28 +137,28 @@ The channel closing handshake will also invoke module callbacks that can return ```go // Called by IBC Handler on MsgCloseInit func (im IBCModule) OnChanCloseInit( - ctx sdk.Context, - portID, - channelID string, + ctx sdk.Context, + portID, + channelID string, ) error { - // ... do custom finalization logic + // ... do custom finalization logic - // Use above arguments to determine if we want to abort handshake - err := checkArguments(args) - return err + // 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, + ctx sdk.Context, + portID, + channelID string, ) error { - // ... do custom finalization logic + // ... do custom finalization logic - // Use above arguments to determine if we want to abort handshake - err := checkArguments(args) - return err + // Use above arguments to determine if we want to abort handshake + err := checkArguments(args) + return err } ``` @@ -209,7 +209,7 @@ Just as IBC expects modules to implement callbacks for channel handshakes, it al 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) +![IBC packet flow diagram](https://ibcprotocol.dev/_nuxt/img/packet_flow.1d89ee0.png) Briefly, a successful packet flow works as follows: @@ -232,9 +232,16 @@ module must trigger execution on the port-bound module through the use of callba 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) +sequence, err := IBCChannelKeeper.SendPacket( + ctx, + channelCap, + sourcePort, + sourceChannel, + timeoutHeight, + timeoutTimestamp, + data, +) ``` ::: warning @@ -266,20 +273,20 @@ for asynchronous acknowledgements. ```go func (im IBCModule) OnRecvPacket( - ctx sdk.Context, - packet channeltypes.Packet, + ctx sdk.Context, + packet channeltypes.Packet, ) ibcexported.Acknowledgement { - // Decode the packet data - packetData := DecodePacketData(packet.Data) + // 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) + // 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 + return ack } ``` @@ -289,8 +296,8 @@ Reminder, the `Acknowledgement` interface: // Acknowledgement defines the interface used to return // acknowledgements in the OnRecvPacket callback. type Acknowledgement interface { - Success() bool - Acknowledgement() []byte + Success() bool + Acknowledgement() []byte } ``` @@ -310,16 +317,16 @@ is responsible for decoding the acknowledgement and processing it. ```go func (im IBCModule) OnAcknowledgementPacket( - ctx sdk.Context, - packet channeltypes.Packet, - acknowledgement []byte, + ctx sdk.Context, + packet channeltypes.Packet, + acknowledgement []byte, ) (*sdk.Result, error) { - // Decode acknowledgement - ack := DecodeAcknowledgement(acknowledgement) + // Decode acknowledgement + ack := DecodeAcknowledgement(acknowledgement) - // process ack - res, err := processAck(ack) - return res, err + // process ack + res, err := processAck(ack) + return res, err } ``` @@ -334,9 +341,9 @@ timeout is reached and the packet can no longer be received. ```go func (im IBCModule) OnTimeoutPacket( - ctx sdk.Context, - packet channeltypes.Packet, + ctx sdk.Context, + packet channeltypes.Packet, ) (*sdk.Result, error) { - // do custom timeout logic + // do custom timeout logic } ``` diff --git a/docs/ibc/apps/keeper.md b/docs/ibc/apps/keeper.md index 6cbba0fbb8f..809ac46d8d7 100644 --- a/docs/ibc/apps/keeper.md +++ b/docs/ibc/apps/keeper.md @@ -18,70 +18,70 @@ In the previous sections, on channel handshake callbacks and port binding in `In ```go // Keeper defines the IBC app module keeper type Keeper struct { - storeKey sdk.StoreKey - cdc codec.BinaryCodec - paramSpace paramtypes.Subspace + storeKey sdk.StoreKey + cdc codec.BinaryCodec + paramSpace paramtypes.Subspace - channelKeeper types.ChannelKeeper - portKeeper types.PortKeeper - scopedKeeper capabilitykeeper.ScopedKeeper + channelKeeper types.ChannelKeeper + portKeeper types.PortKeeper + scopedKeeper capabilitykeeper.ScopedKeeper - // ... additional according to custom logic + // ... additional according to custom logic } // NewKeeper creates a new IBC app module Keeper instance func NewKeeper( - // args + // args ) Keeper { - // ... + // ... - return Keeper{ - cdc: cdc, - storeKey: key, - paramSpace: paramSpace, + return Keeper{ + cdc: cdc, + storeKey: key, + paramSpace: paramSpace, - channelKeeper: channelKeeper, - portKeeper: portKeeper, - scopedKeeper: scopedKeeper, + channelKeeper: channelKeeper, + portKeeper: portKeeper, + scopedKeeper: scopedKeeper, - // ... additional according to custom logic - } + // ... 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 + _, 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)) + 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)) + 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)) + 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) + 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) + 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 index 1871eca8915..1d5061cf03f 100644 --- a/docs/ibc/apps/packets_acks.md +++ b/docs/ibc/apps/packets_acks.md @@ -26,15 +26,15 @@ encode and decode it to and from `[]byte`. ```go // Custom packet data defined in application module type CustomPacketData struct { - // Custom fields ... + // Custom fields ... } EncodePacketData(packetData CustomPacketData) []byte { - // encode packetData to bytes + // encode packetData to bytes } DecodePacketData(encoded []byte) (CustomPacketData) { - // decode from bytes to packet data + // decode from bytes to packet data } ``` @@ -43,10 +43,20 @@ DecodePacketData(encoded []byte) (CustomPacketData) { Then a module must encode its packet data before sending it through IBC. ```go +// retrieve the dynamic capability for this channel +channelCap := scopedKeeper.GetCapability(ctx, channelCapName) // Sending custom application packet data data := EncodePacketData(customPacketData) -packet.Data = data -IBCChannelKeeper.SendPacket(ctx, packet) +// Send packet to IBC, authenticating with channelCap +sequence, err := IBCChannelKeeper.SendPacket( + ctx, + channelCap, + sourcePort, + sourceChannel, + timeoutHeight, + timeoutTimestamp, + data, +) ``` A module receiving a packet must decode the `PacketData` into a structure it expects so that it can diff --git a/docs/ibc/apps/routing.md b/docs/ibc/apps/routing.md index 1095462dcba..73d3daa7ee0 100644 --- a/docs/ibc/apps/routing.md +++ b/docs/ibc/apps/routing.md @@ -18,19 +18,19 @@ 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() + // 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) + 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) + // Setting Router will finalize all routes by sealing router + // No more routes can be added + app.IBCKeeper.SetRouter(ibcRouter) -// ... + // ... } ``` diff --git a/docs/ibc/events.md b/docs/ibc/events.md index 2c584302566..080c284deaa 100644 --- a/docs/ibc/events.md +++ b/docs/ibc/events.md @@ -11,7 +11,7 @@ with an attirbute key of `action` will represent the first event for each messag being processed as emitted by the SDK's baseapp. Each IBC TAO message will also emit its module name in the format 'ibc_sub-modulename'. -All the events for the Channel handshakes, `SendPacket`, `RecvPacket`, `AcknowledgePacket`, +All the events for the Channel handshakes, `SendPacket`, `RecvPacket`, `AcknowledgePacket`, `TimeoutPacket` and `TimeoutOnClose` will emit additional events not specified here due to callbacks to IBC applications. @@ -63,7 +63,7 @@ callbacks to IBC applications. | Type | Attribute Key | Attribute Value | |-------------------------|-----------------|-------------------| | upgrade_client_proposal | title | {title} | -| upgrade_client_proposal | height | {height} | +| upgrade_client_proposal | height | {height} | ## ICS 03 - Connection @@ -81,7 +81,7 @@ callbacks to IBC applications. | Type | Attribute Key | Attribute Value | |---------------------|----------------------------|-----------------------------| -| connection_open_try | connection_id | {connectionId} | +| connection_open_try | connection_id | {connectionId} | | connection_open_try | client_id | {clientId} | | connection_open_try | counterparty_client_id | {counterparty.clientId | | connection_open_try | counterparty_connection_id | {counterparty.connectionId} | @@ -199,7 +199,7 @@ callbacks to IBC applications. | message | action | application-module-defined-field | | message | module | ibc-channel | -### MsgRecvPacket +### MsgRecvPacket | Type | Attribute Key | Attribute Value | |-------------|--------------------------|----------------------| @@ -216,7 +216,7 @@ callbacks to IBC applications. | message | action | recv_packet | | message | module | ibc-channel | -### MsgAcknowledgePacket +### MsgAcknowledgePacket | Type | Attribute Key | Attribute Value | |--------------------|--------------------------|----------------------| @@ -231,7 +231,7 @@ callbacks to IBC applications. | message | action | acknowledge_packet | | message | module | ibc-channel | -### MsgTimeoutPacket & MsgTimeoutOnClose +### MsgTimeoutPacket & MsgTimeoutOnClose | Type | Attribute Key | Attribute Value | |----------------|--------------------------|----------------------| @@ -245,4 +245,3 @@ callbacks to IBC applications. | timeout_packet | packet_channel_ordering | {channel.Ordering} | | message | action | timeout_packet | | message | module | ibc-channel | - diff --git a/docs/ibc/integration.md b/docs/ibc/integration.md index d2d9f057ea3..26b55c9f3a9 100644 --- a/docs/ibc/integration.md +++ b/docs/ibc/integration.md @@ -16,7 +16,7 @@ Integrating the IBC module to your SDK-based application is straighforward. The - Add required modules to the `module.BasicManager` - Define additional `Keeper` fields for the new modules on the `App` type -- Add the module's `StoreKeys` and initialize their `Keepers` +- Add the module's `StoreKey`s and initialize their `Keeper`s - Set up corresponding routers and routes for the `ibc` module - Add the modules to the module `Manager` - Add modules to `Begin/EndBlockers` and `InitGenesis` @@ -28,15 +28,32 @@ The first step is to add the following modules to the `BasicManager`: `x/capabil 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. -```go +### Integrating light clients + +> Note that from v7 onwards, all light clients have to be explicitly registered in a chain's app.go and follow the steps listed below. + This is in contrast to earlier versions of ibc-go when `07-tendermint` and `06-solomachine` were added out of the box. + +All light clients must be registered with `module.BasicManager` in a chain's app.go file. + +The following code example shows how to register the existing `ibctm.AppModuleBasic{}` light client implementation. + +```diff +import ( + ... ++ ibctm "github.com/cosmos/ibc-go/v6/modules/light-clients/07-tendermint" + ... +) + // app.go var ( - ModuleBasics = module.NewBasicManager( // ... capability.AppModuleBasic{}, ibc.AppModuleBasic{}, transfer.AppModuleBasic{}, // i.e ibc-transfer module + + // register light clients on IBC ++ ibctm.AppModuleBasic{}, ) // module account permissions @@ -44,6 +61,7 @@ var ( // other module accounts permissions // ... ibctransfertypes.ModuleName: {authtypes.Minter, authtypes.Burner}, + } ) ``` @@ -85,14 +103,14 @@ func NewApp(...args) *App { app.CapabilityKeeper = capabilitykeeper.NewKeeper(appCodec, keys[capabilitytypes.StoreKey], memKeys[capabilitytypes.MemStoreKey]) // grant capabilities for the ibc and ibc-transfer modules - scopedIBCKeeper := app.CapabilityKeeper.ScopeToModule(ibchost.ModuleName) + scopedIBCKeeper := app.CapabilityKeeper.ScopeToModule(ibcexported.ModuleName) scopedTransferKeeper := app.CapabilityKeeper.ScopeToModule(ibctransfertypes.ModuleName) // ... other modules keepers // Create IBC Keeper app.IBCKeeper = ibckeeper.NewKeeper( - appCodec, keys[ibchost.StoreKey], app.GetSubspace(ibchost.ModuleName), app.StakingKeeper, app.UpgradeKeeper, scopedIBCKeeper, + appCodec, keys[ibcexported.StoreKey], app.GetSubspace(ibcexported.ModuleName), app.StakingKeeper, app.UpgradeKeeper, scopedIBCKeeper, ) // Create Transfer Keepers @@ -139,7 +157,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/13-simulator.md). ```go // app.go @@ -175,17 +193,6 @@ at each height during the `BeginBlock` call. The historical info is required to past historical info at any given height in order to verify the light client `ConsensusState` during the 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. - -::: tip -Only register the ibc module to the `SetOrderBeginBlockers` if your application will use the -localhost (_aka_ loopback) client. -::: - ```go // app.go func NewApp(...args) *App { @@ -194,7 +201,7 @@ func NewApp(...args) *App { // add staking and ibc modules to BeginBlockers app.mm.SetOrderBeginBlockers( // other modules ... - stakingtypes.ModuleName, ibchost.ModuleName, + stakingtypes.ModuleName, ibcexported.ModuleName, ) // ... @@ -205,7 +212,7 @@ func NewApp(...args) *App { app.mm.SetOrderInitGenesis( capabilitytypes.ModuleName, // other modules ... - ibchost.ModuleName, ibctransfertypes.ModuleName, + ibcexported.ModuleName, ibctransfertypes.ModuleName, ) // .. continues diff --git a/docs/ibc/light-clients/client-state.md b/docs/ibc/light-clients/client-state.md new file mode 100644 index 00000000000..b72b73ad217 --- /dev/null +++ b/docs/ibc/light-clients/client-state.md @@ -0,0 +1,75 @@ + + +# Implementing the `ClientState` interface + +Learn how to implement the [`ClientState`](https://github.com/cosmos/ibc-go/blob/v6.0.0/modules/core/exported/client.go#L40) interface. This list of methods described here does not include all methods of the interface. Some methods are explained in detail in the relevant sections of the guide. + +## `ClientType` method + +`ClientType` should return a unique string identifier of the light client. This will be used when generating a client identifier. +The format is created as follows: `ClientType-{N}` where `{N}` is the unique global nonce associated with a specific client. + +## `GetLatestHeight` method + +`GetLatestHeight` should return the latest block height that the client state represents. + +## `Validate` method + +`Validate` should validate every client state field and should return an error if any value is invalid. The light client +implementer is in charge of determining which checks are required. See the [Tendermint light client implementation](https://github.com/cosmos/ibc-go/blob/v6.0.0/modules/light-clients/07-tendermint/types/client_state.go#L101) as a reference. + +## `Status` method + +`Status` must return the status of the client. + +- An `Active` status indicates that clients are allowed to process packets. +- A `Frozen` status indicates that misbehaviour was detected in the counterparty chain and the client is not allowed to be used. +- An `Expired` status indicates that a client is not allowed to be used because it was not updated for longer than the trusting period. +- An `Unknown` status indicates that there was an error in determining the status of a client. + +All possible `Status` types can be found [here](https://github.com/cosmos/ibc-go/blob/v6.0.0/modules/core/exported/client.go#L26-L36). + +This field is returned in the response of the gRPC [`ibc.core.client.v1.Query/ClientStatus`](https://github.com/cosmos/ibc-go/blob/v6.0.0/modules/core/02-client/types/query.pb.go#L665) endpoint. + +## `ZeroCustomFields` method + +`ZeroCustomFields` should return a copy of the light client with all client customizable fields with their zero value. It should not mutate the fields of the light client. +This method is used when [scheduling upgrades](https://github.com/cosmos/ibc-go/blob/v6.0.0/modules/core/02-client/keeper/proposal.go#L89). Upgrades are used to upgrade chain specific fields. +In the tendermint case, this may be the chain ID or the unbonding period. +For more information about client upgrades see the [Handling upgrades](./upgrades.md) section. + +## `GetTimestampAtHeight` method + +`GetTimestampAtHeight` must return the timestamp for the consensus state associated with the provided height. +This value is used to facilitate timeouts by checking the packet timeout timestamp against the returned value. + +## `Initialize` method + +Clients must validate the initial consensus state, and set the initial client state and consensus state in the provided client store. +Clients may also store any necessary client-specific metadata. + +`Initialize` is called when a [client is created](https://github.com/cosmos/ibc-go/blob/main/modules/core/02-client/keeper/client.go#L32). + +## `VerifyMembership` method + +`VerifyMembership` must verify the existence of a value at a given commitment path at the specified height. For more information about membership proofs +see the [Existence and non-existence proofs section](./proofs.md). + +## `VerifyNonMembership` method + +`VerifyNonMembership` must verify the absence of a value at a given commitment path at a specified height. For more information about non-membership proofs +see the [Existence and non-existence proofs section](./proofs.md). + +## `VerifyClientMessage` method + +`VerifyClientMessage` must verify a `ClientMessage`. A `ClientMessage` could be a `Header`, `Misbehaviour`, or batch update. +It must handle each type of `ClientMessage` appropriately. Calls to `CheckForMisbehaviour`, `UpdateState`, and `UpdateStateOnMisbehaviour` +will assume that the content of the `ClientMessage` has been verified and can be trusted. An error should be returned +if the ClientMessage fails to verify. + +## `CheckForMisbehaviour` method + +Checks for evidence of a misbehaviour in `Header` or `Misbehaviour` type. It assumes the `ClientMessage` +has already been verified. diff --git a/docs/ibc/light-clients/consensus-state.md b/docs/ibc/light-clients/consensus-state.md new file mode 100644 index 00000000000..26166bc41a7 --- /dev/null +++ b/docs/ibc/light-clients/consensus-state.md @@ -0,0 +1,23 @@ + + +# Implementing the `ConsensusState` interface + +A `ConsensusState` is the snapshot of the counterparty chain, that an IBC client uses to verify proofs (e.g. a block). + +The further development of multiple types of IBC light clients and the difficulties presented by this generalization problem (see [ADR-006](https://github.com/cosmos/ibc-go/blob/main/docs/architecture/adr-006-02-client-refactor.md) for more information about this historical context) led to the design decision of each client keeping track of and set its own `ClientState` and `ConsensusState`, as well as the simplification of client `ConsensusState` updates through the generalized `ClientMessage` interface. + +The below [`ConsensusState`](https://github.com/cosmos/ibc-go/blob/main/modules/core/exported/client.go#L134) interface is a generalized interface for the types of information a `ConsensusState` could contain. For a reference `ConsensusState` implementation, please see the [Tendermint light client `ConsensusState`](https://github.com/cosmos/ibc-go/blob/main/modules/light-clients/07-tendermint/consensus_state.go). + +## `ClientType` method + +This is the type of client consensus. It should be the same as the `ClientType` return value for the [corresponding `ClientState` implementation](./client-state.md). + +## `GetTimestamp` method + +`GetTimestamp` should return the timestamp (in nanoseconds) of the consensus state snapshot. + +## `ValidateBasic` method + +`ValidateBasic` should validate every consensus state field and should return an error if any value is invalid. The light client implementer is in charge of determining which checks are required. diff --git a/docs/ibc/light-clients/genesis.md b/docs/ibc/light-clients/genesis.md new file mode 100644 index 00000000000..234cfa4002e --- /dev/null +++ b/docs/ibc/light-clients/genesis.md @@ -0,0 +1,36 @@ + + +# Genesis metadata + +Learn how to implement the `ExportMetadata` interface {synopsis} + +## Pre-requisite readings + +- [Cosmos SDK module genesis](https://docs.cosmos.network/v0.47/building-modules/genesis) {prereq} + +`ClientState` instances are provided their own isolated and namespaced client store upon initialisation. `ClientState` implementations may choose to store any amount of arbitrary metadata in order to verify counterparty consensus state and perform light client updates correctly. + +The `ExportMetadata` method of the [`ClientState` interface](https://github.com/cosmos/ibc-go/blob/e650be91614ced7be687c30eb42714787a3bbc59/modules/core/exported/client.go) provides light client modules with the ability to persist metadata in genesis exports. + +```go +ExportMetadata(clientStore sdk.KVStore) []GenesisMetadata +``` + +`ExportMetadata` is provided the client store and returns an array of `GenesisMetadata`. For maximum flexibility, `GenesisMetadata` is defined as a simple interface containing two distinct `Key` and `Value` accessor methods. + +```go +type GenesisMetadata interface { + // return store key that contains metadata without clientID-prefix + GetKey() []byte + // returns metadata value + GetValue() []byte +} +``` + +This allows `ClientState` instances to retrieve and export any number of key-value pairs which are maintained within the store in their raw `[]byte` form. + +When a chain is started with a `genesis.json` file which contains `ClientState` metadata (for example, when performing manual upgrades using an exported `genesis.json`) the `02-client` submodule of core IBC will handle setting the key-value pairs within their respective client stores. [See `02-client` `InitGenesis`](https://github.com/cosmos/ibc-go/blob/02-client-refactor-beta1/modules/core/02-client/genesis.go#L18-L22). + +Please refer to the [Tendermint light client implementation](https://github.com/cosmos/ibc-go/blob/02-client-refactor-beta1/modules/light-clients/07-tendermint/genesis.go#L12) for an example. diff --git a/docs/ibc/light-clients/overview.md b/docs/ibc/light-clients/overview.md new file mode 100644 index 00000000000..a06ae4afa31 --- /dev/null +++ b/docs/ibc/light-clients/overview.md @@ -0,0 +1,70 @@ + + +# Overview + +Learn how to build IBC light client modules and fulfill the interfaces required to integrate with core IBC. {synopsis} + +## Pre-requisites Readings + +- [IBC Overview](../overview.md)) {prereq} +- [IBC Transport, Authentication, and Ordering Layer - Clients](https://tutorials.cosmos.network/academy/3-ibc/4-clients.html) {prereq} +- [ICS-002 Client Semantics](https://github.com/cosmos/ibc/tree/main/spec/core/ics-002-client-semantics) {prereq} + +IBC uses light clients in order to provide trust-minimized interoperability between sovereign blockchains. Light clients operate under a strict set of rules which provide security guarantees for state updates and facilitate the ability to verify the state of a remote blockchain using merkle proofs. + +The following aims to provide a high level IBC light client module developer guide. Access to IBC light clients are gated by the core IBC `MsgServer` which utilizes the abstractions set by the `02-client` submodule to call into a light client module. A light client module developer is only required to implement a set interfaces as defined in the `modules/core/exported` package of ibc-go. + +A light client module developer should be concerned with three main interfaces: + +- [`ClientState`](#clientstate) encapsulates the light client implementation and its semantics. +- [`ConsensusState`](#consensusstate) tracks consensus data used for verification of client updates, misbehaviour detection and proof verification of counterparty state. +- [`ClientMessage`](#clientmessage) used for submitting block headers for client updates and submission of misbehaviour evidence using conflicting headers. + +Throughout this guide the `07-tendermint` light client module may be referred to as a reference example. + +## Concepts and vocabulary + +### `ClientState` + +`ClientState` is a term used to define the data structure which encapsulates opaque light client state. The `ClientState` contains all the information needed to verify a `ClientMessage` and perform membership and non-membership proof verification of counterparty state. This includes properties that refer to the remote state machine, the light client type and the specific light client instance. + +For example: + +- Constraints used for client updates. +- Constraints used for misbehaviour detection. +- Constraints used for state verification. +- Constraints used for client upgrades. + +The `ClientState` type maintained within the light client module *must* implement the [`ClientState`](https://github.com/cosmos/ibc-go/tree/02-client-refactor-beta1/modules/core/exported/client.go#L36) interface defined in `core/modules/exported/client.go`. +The methods which make up this interface are detailed at a more granular level in the [ClientState section of this guide](./client-state.md). + +Please refer to the `07-tendermint` light client module's [`ClientState` definition](https://github.com/cosmos/ibc-go/tree/02-client-refactor-beta1/proto/ibc/lightclients/tendermint/v1/tendermint.proto#L18) containing information such as chain ID, status, latest height, unbonding period and proof specifications. + +### `ConsensusState` + +`ConsensusState` is a term used to define the data structure which encapsulates consensus data at a particular point in time, i.e. a unique height or sequence number of a state machine. There must exist a single trusted `ConsensusState` for each height. `ConsensusState` generally contains a trusted root, validator set information and timestamp. + +For example, the `ConsensusState` of the `07-tendermint` light client module defines a trusted root which is used by the `ClientState` to perform verification of membership and non-membership commitment proofs, as well as the next validator set hash used for verifying headers can be trusted in client updates. + +The `ConsensusState` type maintained within the light client module *must* implement the [`ConsensusState`](https://github.com/cosmos/ibc-go/tree/02-client-refactor-beta1/modules/core/exported/client.go#L134) interface defined in `modules/core/exported/client.go`. +The methods which make up this interface are detailed at a more granular level in the [`ConsensusState` section of this guide](./consensus-state.md). + +### `Height` + +`Height` defines a monotonically increasing sequence number which provides ordering of consensus state data persisted through client updates. +IBC light client module developers are expected to use the [concrete type](https://github.com/cosmos/ibc-go/tree/02-client-refactor-beta1/proto/ibc/core/client/v1/client.proto#L89) provided by the `02-client` submodule. This implements the expectations required by the [`Height`](https://github.com/cosmos/ibc-go/blob/02-client-refactor-beta1/modules/core/exported/client.go#L157) interface defined in `modules/core/exported/client.go`. + +### `ClientMessage` + +`ClientMessage` refers to the interface type [`ClientMessage`](https://github.com/cosmos/ibc-go/tree/02-client-refactor-beta1/modules/core/exported/client.go#L148) used for performing updates to a `ClientState` stored on chain. +This may be any concrete type which produces a change in state to the IBC client when verified. + +The following are considered as valid update scenarios: + +- A block header which when verified inserts a new `ConsensusState` at a unique height. +- A batch of block headers which when verified inserts `N` `ConsensusState` instances for `N` unique heights. +- Evidence of misbehaviour provided by two conflicting block headers. + +Learn more in the [Handling update and misbehaviour](./updates-and-misbehaviour.md) section. diff --git a/docs/ibc/light-clients/proofs.md b/docs/ibc/light-clients/proofs.md new file mode 100644 index 00000000000..fa4e5a84126 --- /dev/null +++ b/docs/ibc/light-clients/proofs.md @@ -0,0 +1,62 @@ + + +# Existence and non-existence proofs + +IBC uses merkle proofs in order to verify the state of a remote counterparty state machine given a trusted root, and [ICS-23](https://github.com/cosmos/ics23/tree/master/go) is a general approach for verifying merkle trees which is used in ibc-go. + +Currently, all Cosmos SDK modules contain their own stores, which maintain the state of the application module in an IAVL (immutable AVL) binary merkle tree format. Specifically with regard to IBC, core IBC maintains its own IAVL store, and IBC apps (e.g. transfer) maintain their own dedicated stores. The Cosmos SDK multistore therefore creates a simple merkle tree of all of these IAVL trees, and from each of these individual IAVL tree root hashes it derives a root hash for the application state tree as a whole (the `AppHash`). + +For the purposes of ibc-go, there are two types of proofs which are important: existence and non-existence proofs, terms which have been used interchangeably with membership and non-membership proofs. For the purposes of this guide, we will stick with "existence" and "non-existence". + +## Existence proofs + +Existence proofs are used in IBC transactions which involve verification of counterparty state for transactions which will result in the writing of provable state. For example, this includes verification of IBC store state for handshakes and packets. + +Put simply, existence proofs prove that a particular key and value exists in the tree. Under the hood, an IBC existence proof comprises of two proofs: an IAVL proof that the key exists in IBC store/IBC root hash, and a proof that the IBC root hash exists in the multistore root hash. + +## Non-existence proofs + +Non-existence proofs verify the absence of data stored within counterparty state and are used to prove that a key does NOT exist in state. As stated above, these types of proofs can be used to timeout packets by proving that the counterparty has not written a packet receipt into the store, meaning that a token transfer has NOT successfully occurred. + +Some trees (e.g. SMT) may have a sentinel empty child for non-existent keys. In this case, the ICS-23 proof spec should include this `EmptyChild` so that ICS-23 handles the non-existence proof correctly. + +In some cases, there is a necessity to "mock" non-existence proofs if the counterparty does not have ability to prove absence. Since the verification method is designed to give complete control to client implementations, clients can support chains that do not provide absence proofs by verifying the existence of a non-empty sentinel `ABSENCE` value. In these special cases, the proof provided will be an ICS-23 `Existence` proof, and the client will verify that the `ABSENCE` value is stored under the given path for the given height. + +## State verification methods: `VerifyMembership` and `VerifyNonMembership` + +The state verification functions for all IBC data types have been consolidated into two generic methods, `VerifyMembership` and `VerifyNonMembership`. + +From the [`ClientState` interface definition](https://github.com/cosmos/ibc-go/blob/e650be91614ced7be687c30eb42714787a3bbc59/modules/core/exported/client.go#L68-L91), we find: + +```go +VerifyMembership( + ctx sdk.Context, + clientStore sdk.KVStore, + cdc codec.BinaryCodec, + height Height, + delayTimePeriod uint64, + delayBlockPeriod uint64, + proof []byte, + path Path, + value []byte, +) error + +// VerifyNonMembership is a generic proof verification method which verifies the absence of a given CommitmentPath at a specified height. +// The caller is expected to construct the full CommitmentPath from a CommitmentPrefix and a standardized path (as defined in ICS 24). +VerifyNonMembership( + ctx sdk.Context, + clientStore sdk.KVStore, + cdc codec.BinaryCodec, + height Height, + delayTimePeriod uint64, + delayBlockPeriod uint64, + proof []byte, + path Path, +) error +``` + +Both are expected to be provided with a standardised key path, `exported.Path`, as defined in [ICS-24 host requirements](https://github.com/cosmos/ibc/tree/main/spec/core/ics-024-host-requirements). Membership verification requires callers to provide the value marshalled as `[]byte`. Delay period values should be zero for non-packet processing verification. A zero proof height is now allowed by core IBC and may be passed into `VerifyMembership` and `VerifyNonMembership`. Light clients are responsible for returning an error if a zero proof height is invalid behaviour. + +Please refer to the [ICS-23 implementation](https://github.com/cosmos/ibc-go/blob/e093d85b533ab3572b32a7de60b88a0816bed4af/modules/core/23-commitment/types/merkle.go#L131-L205) for a concrete example. diff --git a/docs/ibc/light-clients/proposals.md b/docs/ibc/light-clients/proposals.md new file mode 100644 index 00000000000..c2a0a57adef --- /dev/null +++ b/docs/ibc/light-clients/proposals.md @@ -0,0 +1,32 @@ + + +# Handling proposals + +It is possible to update the client with the state of the substitute client through a governance proposal. [This type of governance proposal](https://ibc.cosmos.network/main/ibc/proposals.html) is typically used to recover an expired or frozen client, as it can recover the entire state and therefore all existing channels built on top of the client. `CheckSubstituteAndUpdateState` should be implemented to handle the proposal. + +## Implementing `CheckSubstituteAndUpdateState` + +In the [`ClientState`interface](https://github.com/cosmos/ibc-go/blob/e650be91614ced7be687c30eb42714787a3bbc59/modules/core/exported/client.go), we find: + +```go +// CheckSubstituteAndUpdateState must verify that the provided substitute may be used to update the subject client. +// The light client must set the updated client and consensus states within the clientStore for the subject client. +CheckSubstituteAndUpdateState( + ctx sdk.Context, + cdc codec.BinaryCodec, + subjectClientStore, + substituteClientStore sdk.KVStore, + substituteClient ClientState, +) error +``` + +Prior to updating, this function must verify that: + +- the substitute client is the same type as the subject client. For a reference implementation, please see the [Tendermint light client](https://github.com/cosmos/ibc-go/blob/02-client-refactor-beta1/modules/light-clients/07-tendermint/proposal_handle.go#L32). +- the provided substitute may be used to update the subject client. This may mean that certain parameters must remain unaltered. For example, a [valid substitute Tendermint light client](https://github.com/cosmos/ibc-go/blob/02-client-refactor-beta1/modules/light-clients/07-tendermint/proposal_handle.go#L84) must NOT change the chain ID, trust level, max clock drift, unbonding period, proof specs or upgrade path. Please note that `AllowUpdateAfterMisbehaviour` and `AllowUpdateAfterExpiry` have been deprecated (see ADR 026 for more information). + +After these checks are performed, the function must [set the updated client and consensus states](https://github.com/cosmos/ibc-go/blob/02-client-refactor-beta1/modules/light-clients/07-tendermint/proposal_handle.go#L77) within the client store for the subject client. + +Please refer to the [Tendermint light client implementation](https://github.com/cosmos/ibc-go/blob/02-client-refactor-beta1/modules/light-clients/07-tendermint/proposal_handle.go#L27) for reference. diff --git a/docs/ibc/light-clients/setup.md b/docs/ibc/light-clients/setup.md new file mode 100644 index 00000000000..84d5a924fff --- /dev/null +++ b/docs/ibc/light-clients/setup.md @@ -0,0 +1,129 @@ + + +# Setup + +Learn how to configure light client modules and create clients using core IBC and the `02-client` submodule. {synopsis} + +A last step to finish the development of the light client, is to implement the `AppModuleBasic` interface to allow it to be added to the chain's `app.go` alongside other light client types the chain enables. + +Finally, a succinct rundown is given of the remaining steps to make the light client operational, getting the light client type passed through governance and creating the clients. + +## Configuring a light client module + +An IBC light client module must implement the [`AppModuleBasic`](https://github.com/cosmos/cosmos-sdk/blob/main/types/module/module.go#L50) interface in order to register its concrete types against the core IBC interfaces defined in `modules/core/exported`. This is accomplished via the `RegisterInterfaces` method which provides the light client module with the opportunity to register codec types using the chain's `InterfaceRegistry`. Please refer to the [`07-tendermint` codec registration](https://github.com/cosmos/ibc-go/blob/02-client-refactor-beta1/modules/light-clients/07-tendermint/codec.go#L11). + +The `AppModuleBasic` interface may also be leveraged to install custom CLI handlers for light client module users. Light client modules can safely no-op for interface methods which it does not wish to implement. + +Please refer to the [core IBC documentation](../integration.md#integrating-light-clients) for how to configure additional light client modules alongside `07-tendermint` in `app.go`. + +See below for an example of the `07-tendermint` implementation of `AppModuleBasic`. + +```go +var _ module.AppModuleBasic = AppModuleBasic{} + +// AppModuleBasic defines the basic application module used by the tendermint light client. +// Only the RegisterInterfaces function needs to be implemented. All other function perform +// a no-op. +type AppModuleBasic struct{} + +// Name returns the tendermint module name. +func (AppModuleBasic) Name() string { + return ModuleName +} + +// RegisterLegacyAminoCodec performs a no-op. The Tendermint client does not support amino. +func (AppModuleBasic) RegisterLegacyAminoCodec(*codec.LegacyAmino) {} + +// RegisterInterfaces registers module concrete types into protobuf Any. This allows core IBC +// to unmarshal tendermint light client types. +func (AppModuleBasic) RegisterInterfaces(registry codectypes.InterfaceRegistry) { + RegisterInterfaces(registry) +} + +// DefaultGenesis performs a no-op. Genesis is not supported for the tendermint light client. +func (AppModuleBasic) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage { + return nil +} + +// ValidateGenesis performs a no-op. Genesis is not supported for the tendermint light cilent. +func (AppModuleBasic) ValidateGenesis(cdc codec.JSONCodec, config client.TxEncodingConfig, bz json.RawMessage) error { + return nil +} + +// RegisterGRPCGatewayRoutes performs a no-op. +func (AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *runtime.ServeMux) {} + +// GetTxCmd performs a no-op. Please see the 02-client cli commands. +func (AppModuleBasic) GetTxCmd() *cobra.Command { + return nil +} + +// GetQueryCmd performs a no-op. Please see the 02-client cli commands. +func (AppModuleBasic) GetQueryCmd() *cobra.Command { + return nil +} +``` + +## Creating clients + +A client is created by executing a new `MsgCreateClient` transaction composed with a valid `ClientState` and initial `ConsensusState` encoded as protobuf `Any`s. +Generally, this is performed by an off-chain process known as an [IBC relayer](https://github.com/cosmos/ibc/tree/main/spec/relayer/ics-018-relayer-algorithms) however, this is not a strict requirement. + +See below for a list of IBC relayer implementations: + +- [cosmos/relayer](https://github.com/cosmos/relayer) +- [informalsystems/hermes](https://github.com/informalsystems/hermes) +- [confio/ts-relayer](https://github.com/confio/ts-relayer) + +Stateless checks are performed within the [`ValidateBasic`](https://github.com/cosmos/ibc-go/blob/02-client-refactor-beta1/modules/core/02-client/types/msgs.go#L48) method of `MsgCreateClient`. + +```protobuf +// MsgCreateClient defines a message to create an IBC client +message MsgCreateClient { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // light client state + google.protobuf.Any client_state = 1 [(gogoproto.moretags) = "yaml:\"client_state\""]; + // consensus state associated with the client that corresponds to a given + // height. + google.protobuf.Any consensus_state = 2 [(gogoproto.moretags) = "yaml:\"consensus_state\""]; + // signer address + string signer = 3; +} +``` + +Leveraging protobuf `Any` encoding allows core IBC to [unpack](https://github.com/cosmos/ibc-go/blob/02-client-refactor-beta1/modules/core/keeper/msg_server.go#L28-L36) both the `ClientState` and `ConsensusState` into their respective interface types registered previously using the light client module's `RegisterInterfaces` method. + +Within the `02-client` submodule, the [`ClientState` is then initialized](https://github.com/cosmos/ibc-go/blob/02-client-refactor-beta1/modules/core/02-client/keeper/client.go#L30-L34) with its own isolated key-value store, namespaced using a unique client identifier. + +In order to successfully create an IBC client using a new client type, it [must be supported](https://github.com/cosmos/ibc-go/blob/02-client-refactor-beta1/modules/core/02-client/keeper/client.go#L18-L24). Light client support in IBC is gated by on-chain governance. The allow list may be updated by submitting a new governance proposal to update the `02-client` parameter `AllowedClients`. + + +See below for example: + +```shell +%s tx gov submit-proposal param-change --from= +``` + +where `proposal.json` contains: + +```json +{ + "title": "IBC Clients Param Change", + "description": "Update allowed clients", + "changes": [ + { + "subspace": "ibc", + "key": "AllowedClients", + "value": ["06-solomachine", "07-tendermint", "0x-new-client"] + } + ], + "deposit": "1000stake" +} +``` diff --git a/modules/light-clients/06-solomachine/spec/01_concepts.md b/docs/ibc/light-clients/solomachine/concepts.md similarity index 75% rename from modules/light-clients/06-solomachine/spec/01_concepts.md rename to docs/ibc/light-clients/solomachine/concepts.md index 75d31bf5340..71cd6b6c706 100644 --- a/modules/light-clients/06-solomachine/spec/01_concepts.md +++ b/docs/ibc/light-clients/solomachine/concepts.md @@ -8,32 +8,32 @@ order: 1 The `ClientState` for a solo machine light client stores the latest sequence, the frozen sequence, the latest consensus state, and client flag indicating if the client should be allowed to be updated -after a governance proposal. +after a governance proposal. -If the client is not frozen then the frozen sequence is 0. +If the client is not frozen then the frozen sequence is 0. ## Consensus State -The consensus states stores the public key, diversifier, and timestamp of the solo machine light client. +The consensus states stores the public key, diversifier, and timestamp of the solo machine light client. The diversifier is used to prevent accidental misbehaviour if the same public key is used across different chains with the same client identifier. It should be unique to the chain the light client -is used on. +is used on. ## Public Key The public key can be a single public key or a multi-signature public key. The public key type used must fulfill the tendermint public key interface (this will become the SDK public key interface in the -near future). The public key must be registered on the application codec otherwise encoding/decoding -errors will arise. The public key stored in the consensus state is represented as a protobuf `Any`. -This allows for flexibility in what other public key types can be supported in the future. - +near future). The public key must be registered on the application codec otherwise encoding/decoding +errors will arise. The public key stored in the consensus state is represented as a protobuf `Any`. +This allows for flexibility in what other public key types can be supported in the future. + ## Counterparty Verification The solo machine light client can verify counterparty client state, consensus state, connection state, -channel state, packet commitments, packet acknowledgements, packet receipt absence, +channel state, packet commitments, packet acknowledgements, packet receipt absence, and the next sequence receive. At the end of each successful verification call the light -client sequence number will be incremented. +client sequence number will be incremented. Successful verification requires the current public key to sign over the proof. @@ -50,14 +50,14 @@ For example: ```go data := &ClientStateData{ Path: []byte(path.String()), - ClientState: any, + ClientState: protoAny, } dataBz, err := cdc.Marshal(data) ``` -The helper functions `...DataBytes()` in [proof.go](../types/proof.go) handle this -functionality. +The helper functions `...DataBytes()` in [proof.go](../../../../modules/light-clients/06-solomachine/proof.go) handle this +functionality. 2. Construct the `SignBytes` and marshal it. @@ -75,8 +75,8 @@ signBytes := &SignBytes{ signBz, err := cdc.Marshal(signBytes) ``` -The helper functions `...SignBytes()` in [proof.go](../types/proof.go) handle this functionality. -The `DataType` field is used to disambiguate what type of data was signed to prevent potential +The helper functions `...SignBytes()` in [proof.go](../../../../modules/light-clients/06-solomachine/proof.go) handle this functionality. +The `DataType` field is used to disambiguate what type of data was signed to prevent potential proto encoding overlap. 3. Sign the sign bytes. Embed the signatures into either `SingleSignatureData` or `MultiSignatureData`. @@ -94,13 +94,13 @@ protoSigData := signing.SignatureDataToProto(sigData) bz, err := cdc.Marshal(protoSigData) ``` -4. Construct a `TimestampedSignatureData` and marshal it. The marshaled result can be passed in +4. Construct a `TimestampedSignatureData` and marshal it. The marshaled result can be passed in as the proof parameter to the verification functions. For example: ```go -timestampedSignatureData := &types.TimestampedSignatureData{ +timestampedSignatureData := &solomachine.TimestampedSignatureData{ SignatureData: sigData, Timestamp: solomachine.Time, } @@ -108,8 +108,8 @@ timestampedSignatureData := &types.TimestampedSignatureData{ proof, err := cdc.Marshal(timestampedSignatureData) ``` -NOTE: At the end of this process, the sequence associated with the key needs to be updated. -The sequence must be incremented each time proof is generated. +NOTE: At the end of this process, the sequence associated with the key needs to be updated. +The sequence must be incremented each time proof is generated. ## Updates By Header @@ -126,14 +126,13 @@ If the update is successful: - the diversifier is updated - the timestamp is updated - the sequence is incremented by 1 -- the new consensus state is set in the client state +- the new consensus state is set in the client state ## Updates By Proposal An update by a governance proposal will only succeed if: - the substitute provided is parseable to solo machine client state -- the `AllowUpdateAfterProposal` client parameter is set to `true` - the new consensus state public key does not equal the current consensus state public key If the update is successful: @@ -142,22 +141,24 @@ If the update is successful: - the subject consensus state is updated to the substitute consensus state - the client is unfrozen (if it was previously frozen) +NOTE: Previously, `AllowUpdateAfterProposal` was used to signal the update/recovery options for the solo machine client. However, this has now been deprecated because a code migration can overwrite the client and consensus states regardless of the value of this parameter. 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. + ## Misbehaviour Misbehaviour handling will only succeed if: - the misbehaviour provided is parseable to solo machine misbehaviour - the client is not already frozen -- the current public key signed over two unique data messages at the same sequence and diversifier. +- the current public key signed over two unique data messages at the same sequence and diversifier. If the misbehaviour is successfully processed: - the client is frozen by setting the frozen sequence to the misbehaviour sequence NOTE: Misbehaviour processing is data processing order dependent. A misbehaving solo machine -could update to a new public key to prevent being frozen before misbehaviour is submitted. +could update to a new public key to prevent being frozen before misbehaviour is submitted. ## Upgrades -Upgrades to solo machine light clients are not supported since an entirely different type of +Upgrades to solo machine light clients are not supported since an entirely different type of public key can be set using normal client updates. diff --git a/modules/light-clients/06-solomachine/spec/README.md b/docs/ibc/light-clients/solomachine/solomachine.md similarity index 83% rename from modules/light-clients/06-solomachine/spec/README.md rename to docs/ibc/light-clients/solomachine/solomachine.md index 0879f1bd2be..bd9e852a2e6 100644 --- a/modules/light-clients/06-solomachine/spec/README.md +++ b/docs/ibc/light-clients/solomachine/solomachine.md @@ -16,10 +16,10 @@ This implementation of a solo machine light client supports single and multi-sig keys. The client is capable of handling public key updates by header and governance proposals. The light client is capable of processing client misbehaviour. Proofs of the counterparty state are generated by the solo machine client by signing over the desired state with a certain sequence, -diversifier, and timestamp. +diversifier, and timestamp. ## Contents -1. **[Concepts](01_concepts.md)** -2. **[State](02_state.md)** -3. **[State Transitions](03_state_transitions.md)** +1. **[Concepts](./concepts.md)** +2. **[State](./state.md)** +3. **[State Transitions](./state_transitions.md)** diff --git a/modules/light-clients/06-solomachine/spec/02_state.md b/docs/ibc/light-clients/solomachine/state.md similarity index 99% rename from modules/light-clients/06-solomachine/spec/02_state.md rename to docs/ibc/light-clients/solomachine/state.md index 51cb1f058c6..a4b9f87cf8c 100644 --- a/modules/light-clients/06-solomachine/spec/02_state.md +++ b/docs/ibc/light-clients/solomachine/state.md @@ -6,4 +6,3 @@ order: 2 The solo machine light client will only store consensus states for each update by a header or a governance proposal. The latest client state is also maintained in the store. - diff --git a/modules/light-clients/06-solomachine/spec/03_state_transitions.md b/docs/ibc/light-clients/solomachine/state_transitions.md similarity index 98% rename from modules/light-clients/06-solomachine/spec/03_state_transitions.md rename to docs/ibc/light-clients/solomachine/state_transitions.md index 48a1e18f1c9..387111a572c 100644 --- a/modules/light-clients/06-solomachine/spec/03_state_transitions.md +++ b/docs/ibc/light-clients/solomachine/state_transitions.md @@ -14,7 +14,7 @@ Successful state verification by a solo machine light client will result in: A successful update of a solo machine light client by a header will result in: -- the public key being updated to the new public key provided by the header. +- the public key being updated to the new public key provided by the header. - the diversifier being updated to the new diviersifier provided by the header. - the timestamp being updated to the new timestamp provided by the header. - the sequence being incremented by 1 diff --git a/docs/ibc/light-clients/updates-and-misbehaviour.md b/docs/ibc/light-clients/updates-and-misbehaviour.md new file mode 100644 index 00000000000..35e5cd43d4c --- /dev/null +++ b/docs/ibc/light-clients/updates-and-misbehaviour.md @@ -0,0 +1,95 @@ + + +# Handling `ClientMessage`s: updates and misbehaviour + +As mentioned before in the documentation about [implementing the `ConsensusState` interface](./consensus-state.md), [`ClientMessage`](https://github.com/cosmos/ibc-go/blob/main/modules/core/exported/client.go#L145) is an interface used to update an IBC client. This update may be performed by: + ++ a single header ++ a batch of headers ++ evidence of misbehaviour, ++ or any type which when verified produces a change to the consensus state of the IBC client. + +This interface has been purposefully kept generic in order to give the maximum amount of flexibility to the light client implementer. + +## Implementing the `ClientMessage` interface + +Find the `ClientMessage`interface in `modules/core/exported`: + +```go +type ClientMessage interface { + proto.Message + + ClientType() string + ValidateBasic() error +} +``` + +The `ClientMessage` will be passed to the client to be used in [`UpdateClient`](https://github.com/cosmos/ibc-go/blob/57da75a70145409247e85365b64a4b2fc6ddad2f/modules/core/02-client/keeper/client.go#L53), which retrieves the `ClientState` by client ID (available in `MsgUpdateClient`). This `ClientState` implements the [`ClientState` interface](./client-state.md) for its specific consenus type (e.g. Tendermint). + +`UpdateClient` will then handle a number of cases including misbehaviour and/or updating the consensus state, utilizing the specific methods defined in the relevant `ClientState`. + +```go +VerifyClientMessage(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, clientMsg ClientMessage) error +CheckForMisbehaviour(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, clientMsg ClientMessage) bool +UpdateStateOnMisbehaviour(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, clientMsg ClientMessage) +UpdateState(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, clientMsg ClientMessage) []Height +``` + +## Handling updates and misbehaviour + +The functions for handling updates to a light client and evidence of misbehaviour are all found in the [`ClientState`](https://github.com/cosmos/ibc-go/blob/v6.0.0/modules/core/exported/client.go#L40) interface, and will be discussed below. + +> It is important to note that `Misbehaviour` in this particular context is referring to misbehaviour on the chain level intended to fool the light client. This will be defined by each light client. + +## `VerifyClientMessage` + +`VerifyClientMessage` must verify a `ClientMessage`. A `ClientMessage` could be a `Header`, `Misbehaviour`, or batch update. To understand how to implement a `ClientMessage`, please refer to the [Implementing the `ClientMessage` interface](#implementing-the-clientmessage-interface) section. + +It must handle each type of `ClientMessage` appropriately. Calls to `CheckForMisbehaviour`, `UpdateState`, and `UpdateStateOnMisbehaviour` will assume that the content of the `ClientMessage` has been verified and can be trusted. An error should be returned if the `ClientMessage` fails to verify. + +For an example of a `VerifyClientMessage` implementation, please check the [Tendermint light client](https://github.com/cosmos/ibc-go/blob/main/modules/light-clients/07-tendermint/update.go#L20). + +## `CheckForMisbehaviour` + +Checks for evidence of a misbehaviour in `Header` or `Misbehaviour` type. It assumes the `ClientMessage` has already been verified. + +For an example of a `CheckForMisbehaviour` implementation, please check the [Tendermint light client](https://github.com/cosmos/ibc-go/blob/main/modules/light-clients/07-tendermint/misbehaviour_handle.go#L18). + +> The Tendermint light client [defines `Misbehaviour`](https://github.com/cosmos/ibc-go/blob/main/modules/light-clients/07-tendermint/misbehaviour.go) as two different types of situations: a situation where two conflicting `Header`s with the same height have been submitted to update a client's `ConsensusState` within the same trusting period, or that the two conflicting `Header`s have been submitted at different heights but the consensus states are not in the correct monotonic time ordering (BFT time violation). More explicitly, updating to a new height must have a timestamp greater than the previous consensus state, or, if inserting a consensus at a past height, then time must be less than those heights which come after and greater than heights which come before. + +## `UpdateStateOnMisbehaviour` + +`UpdateStateOnMisbehaviour` should perform appropriate state changes on a client state given that misbehaviour has been detected and verified. This method should only be called when misbehaviour is detected, as it does not perform any misbehaviour checks. Notably, it should freeze the client so that calling the `Status` function on the associated client state no longer returns `Active`. + +For an example of a `UpdateStateOnMisbehaviour` implementation, please check the [Tendermint light client](https://github.com/cosmos/ibc-go/blob/main/modules/light-clients/07-tendermint/update.go#L197). + +## `UpdateState` + +`UpdateState` updates and stores as necessary any associated information for an IBC client, such as the `ClientState` and corresponding `ConsensusState`. It should perform a no-op on duplicate updates. + +It assumes the `ClientMessage` has already been verified. + +For an example of a `UpdateState` implementation, please check the [Tendermint light client](https://github.com/cosmos/ibc-go/blob/main/modules/light-clients/07-tendermint/update.go#L131). + +## Putting it all together + +The `02-client` `Keeper` module in ibc-go offers a reference as to how these functions will be used to [update the client](https://github.com/cosmos/ibc-go/blob/main/modules/core/02-client/keeper/client.go#L48). + +```go +if err := clientState.VerifyClientMessage(clientMessage); err != nil { + return err +} + +foundMisbehaviour := clientState.CheckForMisbehaviour(clientMessage) +if foundMisbehaviour { + clientState.UpdateStateOnMisbehaviour(clientMessage) + // emit misbehaviour event + return +} + +clientState.UpdateState(clientMessage) // expects no-op on duplicate header + // emit update event + return +} diff --git a/docs/ibc/light-clients/upgrades.md b/docs/ibc/light-clients/upgrades.md new file mode 100644 index 00000000000..dc1553bf0c4 --- /dev/null +++ b/docs/ibc/light-clients/upgrades.md @@ -0,0 +1,62 @@ + + +# Handling upgrades + +It is vital that high-value IBC clients can upgrade along with their underlying chains to avoid disruption to the IBC ecosystem. Thus, IBC client developers will want to implement upgrade functionality to enable clients to maintain connections and channels even across chain upgrades. + +## Implementing `VerifyUpgradeAndUpdateState` + +The IBC protocol allows client implementations to provide a path to upgrading clients given the upgraded `ClientState`, upgraded `ConsensusState` and proofs for each. This path is provided in the `VerifyUpgradeAndUpdateState` method: + +```go +// NOTE: proof heights are not included as upgrade to a new revision is expected to pass only on the last height committed by the current revision. Clients are responsible for ensuring that the planned last height of the current revision is somehow encoded in the proof verification process. +// This is to ensure that no premature upgrades occur, since upgrade plans committed to by the counterparty may be cancelled or modified before the last planned height. +// If the upgrade is verified, the upgraded client and consensus states must be set in the client store. +func (cs ClientState) VerifyUpgradeAndUpdateState( + ctx sdk.Context, + cdc codec.BinaryCodec, + store sdk.KVStore, + newClient ClientState, + newConsState ConsensusState, + proofUpgradeClient, + proofUpgradeConsState []byte, +) error +``` + +> Please refer to the [Tendermint light client implementation](https://github.com/cosmos/ibc-go/blob/02-client-refactor-beta1/modules/light-clients/07-tendermint/upgrade.go#L27) as an example for implementation. + +It is important to note that light clients **must** handle all management of client and consensus states including the setting of updated `ClientState` and `ConsensusState` in the client store. This can include verifying that the submitted upgraded `ClientState` is of a valid `ClientState` type, that the height of the upgraded client is not greater than the height of the current client (in order to preserve BFT monotonic time), or that certain parameters which should not be changed have not been altered in the upgraded `ClientState`. + +Developers must ensure that the `MsgUpgradeClient` does not pass until the last height of the old chain has been committed, and after the chain upgrades, the `MsgUpgradeClient` should pass once and only once on all counterparty clients. + +### Upgrade path + +Clients should have **prior knowledge of the merkle path** that the upgraded client and upgraded consensus states will use. The height at which the upgrade has occurred should also be encoded in the proof. +> The Tendermint client implementation accomplishes this by including an `UpgradePath` in the `ClientState` itself, which is used along with the upgrade height to construct the merkle path under which the client state and consensus state are committed. + +## Chain specific vs client specific client parameters + +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](./client-state.md): + +```go +// Utility function that zeroes out any client customizable fields in client state +// Ledger enforced fields are maintained while all custom fields are zero values +// Used to verify upgrades +func (cs ClientState) ZeroCustomFields() ClientState +``` + +Developers must ensure that the new client adopts all of the new client parameters that must be uniform across every valid light client of a chain (chain-chosen parameters), while maintaining the client parameters that are customizable by each individual client (client-chosen parameters) from the previous version of the client. `ZeroCustomFields` is a useful utility function to ensure only chain specific fields are updated during upgrades. + +## Security + +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`, `TrustingPeriod`, `ChainID`, `UpgradePath`, etc), while ensuring that the relayer submitting the `MsgUpgradeClient` cannot alter the client-chosen parameters that the users are relying upon (examples in Tendermint client include `TrustLevel`, `MaxClockDrift`, etc). The previous paragraph discusses how `ZeroCustomFields` helps achieve this. + +### Document potential client parameter conflicts during upgrades + +Counterparty clients can upgrade securely by using all of the chain-chosen parameters from the chain-committed `UpgradedClient` and preserving all of the old client-chosen parameters. This enables chains to securely upgrade without relying on an honest relayer, however it can in some cases lead to an invalid final `ClientState` if the new chain-chosen parameters clash with the old client-chosen parameter. This can happen in the Tendermint client case if the upgrading chain lowers the `UnbondingPeriod` (chain-chosen) to a duration below that of a counterparty client's `TrustingPeriod` (client-chosen). Such cases should be clearly documented by developers, so that chains know which upgrades should be avoided to prevent this problem. The final upgraded client should also be validated in `VerifyUpgradeAndUpdateState` before returning to ensure that the client does not upgrade to an invalid `ClientState`. diff --git a/docs/ibc/middleware/develop.md b/docs/ibc/middleware/develop.md index 3c0c74a0186..96748c7acf0 100644 --- a/docs/ibc/middleware/develop.md +++ b/docs/ibc/middleware/develop.md @@ -32,13 +32,13 @@ Middleware allows developers to define the extensions as separate modules that c 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 +### Interfaces ```go // Middleware implements the ICS26 Module interface type Middleware interface { - porttypes.IBCModule // middleware has acccess to an underlying application which may be wrapped by more middleware - ics4Wrapper: ICS4Wrapper // middleware has access to ICS4Wrapper which may be core IBC Channel Handler or a higher-level middleware that wraps this middleware. + porttypes.IBCModule // middleware has acccess to an underlying application which may be wrapped by more middleware + ics4Wrapper: ICS4Wrapper // middleware has access to ICS4Wrapper which may be core IBC Channel Handler or a higher-level middleware that wraps this middleware. } ``` @@ -47,9 +47,28 @@ type Middleware interface { // The base application will call `sendPacket` or `writeAcknowledgement` of the middleware directly above them // 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 exported.Acknowledgement) error - GetAppVersion(ctx sdk.Context, portID, channelID string) (string, bool) + SendPacket( + ctx sdk.Context, + chanCap *capabilitytypes.Capability, + sourcePort string, + sourceChannel string, + timeoutHeight clienttypes.Height, + timeoutTimestamp uint64, + data []byte, + ) (sequence uint64, err error) + + WriteAcknowledgement( + ctx sdk.Context, + chanCap *capabilitytypes.Capability, + packet exported.PacketI, + ack exported.Acknowledgement, + ) error + + GetAppVersion( + ctx sdk.Context, + portID, + channelID string, + ) (string, bool) } ``` @@ -84,65 +103,65 @@ In the case where the middleware wishes to send a packet or acknowledgment witho ```go func (im IBCModule) OnChanOpenInit( - ctx sdk.Context, - order channeltypes.Order, - connectionHops []string, - portID string, - channelID string, - channelCap *capabilitytypes.Capability, - counterparty channeltypes.Counterparty, - version string, + ctx sdk.Context, + order channeltypes.Order, + connectionHops []string, + portID string, + channelID string, + channelCap *capabilitytypes.Capability, + counterparty channeltypes.Counterparty, + version string, ) (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() - - // 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, - portID, - channelID, - channelCap, - counterparty, - metadata.AppVersion, // note we only pass app version here - ) + 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 { - return "", err + // 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: "", } + } - version := constructVersion(metadata.MiddlewareVersion, appVersion) + doCustomLogic() - return version, nil + // 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, + portID, + channelID, + channelCap, + counterparty, + metadata.AppVersion, // note we only pass app version here + ) + if err != nil { + return "", err + } + + version := constructVersion(metadata.MiddlewareVersion, appVersion) + + return version, nil } ``` @@ -152,55 +171,55 @@ See [here](https://github.com/cosmos/ibc-go/blob/48a6ae512b4ea42c29fdf6c6f5363f5 ```go func OnChanOpenTry( - ctx sdk.Context, - order channeltypes.Order, - connectionHops []string, - portID, - channelID string, - channelCap *capabilitytypes.Capability, - counterparty channeltypes.Counterparty, - counterpartyVersion string, + ctx sdk.Context, + order channeltypes.Order, + connectionHops []string, + portID, + channelID string, + channelCap *capabilitytypes.Capability, + counterparty channeltypes.Counterparty, + counterpartyVersion string, ) (string, error) { - // 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, - ) - } - - doCustomLogic() - - // 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, - connectionHops, - portID, - channelID, - channelCap, - counterparty, - cpMetadata.AppVersion, // note we only pass counterparty app version here + // 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, ) - if err != nil { - return "", err - } + } - // negotiate final middleware version - middlewareVersion := negotiateMiddlewareVersion(cpMetadata.MiddlewareVersion) - version := constructVersion(middlewareVersion, appVersion) + doCustomLogic() - return version, nil + // 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, + connectionHops, + portID, + channelID, + channelCap, + counterparty, + cpMetadata.AppVersion, // note we only pass counterparty app version here + ) + if err != nil { + return "", err + } + + // negotiate final middleware version + middlewareVersion := negotiateMiddlewareVersion(cpMetadata.MiddlewareVersion) + version := constructVersion(middlewareVersion, appVersion) + + return version, nil } ``` @@ -210,27 +229,27 @@ See [here](https://github.com/cosmos/ibc-go/blob/48a6ae512b4ea42c29fdf6c6f5363f5 ```go func OnChanOpenAck( - ctx sdk.Context, - portID, - channelID string, - counterpartyChannelID string, - counterpartyVersion string, + ctx sdk.Context, + portID, + channelID string, + counterpartyChannelID string, + counterpartyVersion string, ) error { - // 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 application's OnChanOpenTry callback - return app.OnChanOpenAck(ctx, portID, channelID, counterpartyChannelID, cpMetadata.AppVersion) + // 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 application's OnChanOpenTry callback + return app.OnChanOpenAck(ctx, portID, channelID, counterpartyChannelID, cpMetadata.AppVersion) } ``` @@ -240,13 +259,13 @@ See [here](https://github.com/cosmos/ibc-go/blob/48a6ae512b4ea42c29fdf6c6f5363f5 ```go func OnChanOpenConfirm( - ctx sdk.Context, - portID, - channelID string, + ctx sdk.Context, + portID, + channelID string, ) error { - doCustomLogic() + doCustomLogic() - return app.OnChanOpenConfirm(ctx, portID, channelID) + return app.OnChanOpenConfirm(ctx, portID, channelID) } ``` @@ -256,13 +275,13 @@ See [here](https://github.com/cosmos/ibc-go/blob/48a6ae512b4ea42c29fdf6c6f5363f5 ```go func OnChanCloseInit( - ctx sdk.Context, - portID, - channelID string, + ctx sdk.Context, + portID, + channelID string, ) error { - doCustomLogic() + doCustomLogic() - return app.OnChanCloseInit(ctx, portID, channelID) + return app.OnChanCloseInit(ctx, portID, channelID) } ``` @@ -272,13 +291,13 @@ See [here](https://github.com/cosmos/ibc-go/blob/48a6ae512b4ea42c29fdf6c6f5363f5 ```go func OnChanCloseConfirm( - ctx sdk.Context, - portID, - channelID string, + ctx sdk.Context, + portID, + channelID string, ) error { - doCustomLogic() + doCustomLogic() - return app.OnChanCloseConfirm(ctx, portID, channelID) + return app.OnChanCloseConfirm(ctx, portID, channelID) } ``` @@ -294,16 +313,16 @@ The packet callbacks just like the handshake callbacks wrap the application's pa ```go func OnRecvPacket( - ctx sdk.Context, - packet channeltypes.Packet, - relayer sdk.AccAddress, + ctx sdk.Context, + packet channeltypes.Packet, + relayer sdk.AccAddress, ) ibcexported.Acknowledgement { - doCustomLogic(packet) + doCustomLogic(packet) - ack := app.OnRecvPacket(ctx, packet, relayer) + ack := app.OnRecvPacket(ctx, packet, relayer) - doCustomLogic(ack) // middleware may modify outgoing ack - return ack + doCustomLogic(ack) // middleware may modify outgoing ack + return ack } ``` @@ -313,14 +332,14 @@ See [here](https://github.com/cosmos/ibc-go/blob/48a6ae512b4ea42c29fdf6c6f5363f5 ```go func OnAcknowledgementPacket( - ctx sdk.Context, - packet channeltypes.Packet, - acknowledgement []byte, - relayer sdk.AccAddress, + ctx sdk.Context, + packet channeltypes.Packet, + acknowledgement []byte, + relayer sdk.AccAddress, ) error { - doCustomLogic(packet, ack) + doCustomLogic(packet, ack) - return app.OnAcknowledgementPacket(ctx, packet, ack, relayer) + return app.OnAcknowledgementPacket(ctx, packet, ack, relayer) } ``` @@ -330,13 +349,13 @@ See [here](https://github.com/cosmos/ibc-go/blob/48a6ae512b4ea42c29fdf6c6f5363f5 ```go func OnTimeoutPacket( - ctx sdk.Context, - packet channeltypes.Packet, - relayer sdk.AccAddress, + ctx sdk.Context, + packet channeltypes.Packet, + relayer sdk.AccAddress, ) error { - doCustomLogic(packet) + doCustomLogic(packet) - return app.OnTimeoutPacket(ctx, packet, relayer) + return app.OnTimeoutPacket(ctx, packet, relayer) } ``` @@ -350,14 +369,26 @@ Middleware must also wrap ICS-4 so that any communication from the application t ```go func SendPacket( - ctx sdk.Context, - chanCap *capabilitytypes.Capability, - appPacket exported.PacketI, + ctx sdk.Context, + chanCap *capabilitytypes.Capability, + sourcePort string, + sourceChannel string, + timeoutHeight clienttypes.Height, + timeoutTimestamp uint64, + appData []byte, ) { - // middleware may modify packet - packet = doCustomLogic(appPacket) - - return ics4Keeper.SendPacket(ctx, chanCap, packet) + // middleware may modify data + data = doCustomLogic(appData) + + return ics4Keeper.SendPacket( + ctx, + chanCap, + sourcePort, + sourceChannel, + timeoutHeight, + timeoutTimestamp, + data, + ) } ``` @@ -368,15 +399,15 @@ See [here](https://github.com/cosmos/ibc-go/blob/48a6ae512b4ea42c29fdf6c6f5363f5 ```go // only called for async acks func WriteAcknowledgement( - ctx sdk.Context, - chanCap *capabilitytypes.Capability, - packet exported.PacketI, - ack exported.Acknowledgement, + ctx sdk.Context, + chanCap *capabilitytypes.Capability, + packet exported.PacketI, + ack exported.Acknowledgement, ) { - // middleware may modify acknowledgement - ack_bytes = doCustomLogic(ack) + // middleware may modify acknowledgement + ack_bytes = doCustomLogic(ack) - return ics4Keeper.WriteAcknowledgement(packet, ack_bytes) + return ics4Keeper.WriteAcknowledgement(packet, ack_bytes) } ``` @@ -387,26 +418,46 @@ See [here](https://github.com/cosmos/ibc-go/blob/48a6ae512b4ea42c29fdf6c6f5363f5 ```go // middleware must return the underlying application version func GetAppVersion( - ctx sdk.Context, - portID, - channelID string, + ctx sdk.Context, + portID, + channelID string, ) (string, bool) { - version, found := ics4Keeper.GetAppVersion(ctx, portID, channelID) - if !found { - return "", false - } + version, found := ics4Keeper.GetAppVersion(ctx, portID, channelID) + if !found { + return "", false + } + + if !MiddlewareEnabled { + return version, true + } + + // unwrap channel version + metadata, err := Unmarshal(version) + if err != nil { + panic(fmt.Errof("unable to unmarshal version: %w", err)) + } + + return metadata.AppVersion, true +} - if !MiddlewareEnabled { - return version, true - } +// 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 + } - // unwrap channel version - metadata, err := Unmarshal(version) - if err != nil { - panic(fmt.Errof("unable to unmarshal version: %w", err)) - } + if !MiddlewareEnabled { + return version, true + } + + // unwrap channel version + metadata, err := Unmarshal(version) + if err != nil { + panic(fmt.Errof("unable to unmarshal version: %w", err)) + } - return metadata.AppVersion, true + return metadata.AppVersion, true } ``` diff --git a/docs/ibc/middleware/integration.md b/docs/ibc/middleware/integration.md index 12eb447f8ea..3da9fb78b5f 100644 --- a/docs/ibc/middleware/integration.md +++ b/docs/ibc/middleware/integration.md @@ -12,7 +12,7 @@ All middleware must be connected to the IBC router and wrap over an underlying b The order of middleware **matters**, function calls from IBC to the application travel from top-level middleware to the bottom middleware and then to the application. Function calls from the application to IBC goes through the bottom middleware in order to the top middleware and then to core IBC handlers. Thus the same set of middleware put in different orders may produce different effects. -### Example integration +## Example integration ```go // app.go @@ -25,11 +25,11 @@ mw3Keeper := mw3.NewKeeper(storeKey3) // Only create App Module **once** and register in app module // if the module maintains independent state and/or processes sdk.Msgs app.moduleManager = module.NewManager( - ... - mw1.NewAppModule(mw1Keeper), - mw3.NewAppModule(mw3Keeper), - transfer.NewAppModule(transferKeeper), - custom.NewAppModule(customKeeper) + ... + mw1.NewAppModule(mw1Keeper), + mw3.NewAppModule(mw3Keeper), + transfer.NewAppModule(transferKeeper), + custom.NewAppModule(customKeeper) ) mw1IBCModule := mw1.NewIBCModule(mw1Keeper) @@ -66,4 +66,3 @@ ibcRouter.AddRoute("custom1", stack2) ibcRouter.AddRoute("custom2", stack3) app.IBCKeeper.SetRouter(ibcRouter) ``` - diff --git a/docs/ibc/overview.md b/docs/ibc/overview.md index f36b366a5e0..1b1b5213a36 100644 --- a/docs/ibc/overview.md +++ b/docs/ibc/overview.md @@ -13,17 +13,17 @@ Learn about IBC, its components, and IBC use cases. {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. -> IBC applications must be written as self-contained modules. +> IBC applications must be written as self-contained modules. Due to the modular design of the IBC protocol, IBC application developers do not need to be concerned with the low-level details of clients, -connections, and proof verification. +connections, and proof verification. This brief explanation of the lower levels of the stack gives application developers a broad understanding of the IBC protocol. Abstraction layer details for channels and ports are most relevant for application developers and describe how to define custom packets and `IBCModule` callbacks. -The requirements to have your module interact over IBC are: +The requirements to have your module interact over IBC are: - Bind to a port or ports. - Define your packet data. @@ -37,25 +37,25 @@ Read on for a detailed explanation of how to write a self-contained IBC applicat ### [Clients](https://github.com/cosmos/ibc-go/blob/main/modules/core/02-client) -IBC clients are on-chain light clients. Each light client is identified by a unique client-id. -IBC clients track the consensus states of other blockchains, along with the proof spec necessary to -properly verify proofs against the client's consensus state. A client can be associated with any number -of connections to the counterparty chain. The client identifier is auto generated using the client type -and the global client counter appended in the format: `{client-type}-{N}`. +IBC clients are on-chain light clients. Each light client is identified by a unique client-id. +IBC clients track the consensus states of other blockchains, along with the proof spec necessary to +properly verify proofs against the client's consensus state. A client can be associated with any number +of connections to the counterparty chain. The client identifier is auto generated using the client type +and the global client counter appended in the format: `{client-type}-{N}`. A `ClientState` should contain chain specific and light client specific information necessary for verifying updates -and upgrades to the IBC client. The `ClientState` may contain information such as chain-id, latest height, proof specs, +and upgrades to the IBC client. The `ClientState` may contain information such as chain-id, latest height, proof specs, unbonding periods or the status of the light client. The `ClientState` should not contain information that is specific to a given block at a certain height, this is the function of the `ConsensusState`. Each `ConsensusState` -should be associated with a unique block and should be referenced using a height. IBC clients are given a -client identifier prefixed store to store their associated client state and consensus states along with -any metadata associated with the consensus states. Consensus states are stored using their associated height. +should be associated with a unique block and should be referenced using a height. IBC clients are given a +client identifier prefixed store to store their associated client state and consensus states along with +any metadata associated with the consensus states. Consensus states are stored using their associated height. The supported IBC clients are: -* [Solo Machine light client](https://github.com/cosmos/ibc-go/blob/main/modules/light-clients/06-solomachine): Devices such as phones, browsers, or laptops. -* [Tendermint light client](https://github.com/cosmos/ibc-go/blob/main/modules/light-clients/07-tendermint): The default for Cosmos SDK-based chains. -* [Localhost (loopback) client](https://github.com/cosmos/ibc-go/blob/main/modules/light-clients/09-localhost): Useful for +- [Solo Machine light client](https://github.com/cosmos/ibc-go/blob/main/modules/light-clients/06-solomachine): Devices such as phones, browsers, or laptops. +- [Tendermint light client](https://github.com/cosmos/ibc-go/blob/main/modules/light-clients/07-tendermint): The default for Cosmos SDK-based chains. +- [Localhost (loopback) client](https://github.com/cosmos/ibc-go/blob/main/modules/light-clients/09-localhost): Useful for testing, simulation, and relaying packets to modules on the same application. ### IBC Client Heights @@ -64,8 +64,8 @@ IBC Client Heights are represented by the struct: ```go type Height struct { - RevisionNumber uint64 - RevisionHeight uint64 + RevisionNumber uint64 + RevisionHeight uint64 } ``` @@ -90,7 +90,7 @@ Height{RevisionNumber: 3, RevisionHeight: 0} > Height{RevisionNumber: 2, Revisio ``` When a Tendermint chain is running a particular revision, relayers can simply submit headers and proofs with the revision number -given by the chain's `chainID`, and the revision height given by the Tendermint block height. When a chain updates using a hard-fork +given by the chain's `chainID`, and the revision height given by the Tendermint block height. When a chain updates using a hard-fork and resets its block-height, it is responsible for updating its `chainID` to increment the revision number. IBC Tendermint clients then verifies the revision number against their `chainID` and treat the `RevisionHeight` as the Tendermint block-height. @@ -128,19 +128,19 @@ communicate, a blockchain commits some state to a specifically defined path that specific message type and a specific counterparty. For example, for storing a specific connectionEnd as part of a handshake or a packet intended to be relayed to a module on the counterparty chain. A relayer process monitors for updates to these paths and relays messages by submitting the data stored -under the path and a proof to the counterparty chain. +under the path and a proof to the counterparty chain. Proofs are passed from core IBC to light-clients as bytes. It is up to light client implementation to interpret these bytes appropriately. - The paths that all IBC implementations must use for committing IBC messages is defined in -[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. +[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/cosmos/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 -appropriate permissions can use them. +appropriate permissions can use them. This module authentication is accomplished using a [dynamic capability store](https://github.com/cosmos/cosmos-sdk/blob/master/docs/architecture/adr-003-dynamic-capability-store.md). Upon binding to a port or @@ -150,11 +150,11 @@ they do not own the appropriate capability. While this background information is useful, IBC modules do not need to interact at all with these lower-level abstractions. The relevant abstraction layer for IBC application developers is -that of channels and ports. IBC applications must be written as self-contained **modules**. +that of channels and ports. IBC applications must be written as self-contained **modules**. A module on one blockchain can communicate with other modules on other blockchains by sending, receiving, and acknowledging packets through channels that are uniquely identified by the -`(channelID, portID)` tuple. +`(channelID, portID)` tuple. A useful analogy is to consider IBC modules as internet applications on a computer. A channel can then be conceptualized as an IP connection, with the IBC portID being @@ -203,7 +203,7 @@ If all handshake steps are successful, the channel is opened on both sides. At e associated with the `ChannelEnd` executes its callback. So on `ChanOpenInit`, the module on chain A executes its callback `OnChanOpenInit`. -The channel identifier is auto derived in the format: `channel-{N}` where N is the next sequence to be used. +The channel identifier is auto derived in the format: `channel-{N}` where N is the next sequence to be used. Just as ports came with dynamic capabilities, channel initialization returns a dynamic capability that the module **must** claim so that they can pass in a capability to authenticate channel actions @@ -214,24 +214,23 @@ handshake; either `OnChanOpenInit` on the initializing chain or `OnChanOpenTry` Closing a channel occurs in 2 handshake steps as defined in [ICS 04](https://github.com/cosmos/ibc/tree/master/spec/core/ics-004-channel-and-packet-semantics). -`ChanCloseInit` closes a channel on the executing chain if the channel exists, it is not -already closed and the connection it exists upon is OPEN. Channels can only be closed by a +`ChanCloseInit` closes a channel on the executing chain if the channel exists, it is not +already closed and the connection it exists upon is OPEN. Channels can only be closed by a calling module or in the case of a packet timeout on an ORDERED channel. `ChanCloseConfirm` is a response to a counterparty channel executing `ChanCloseInit`. The channel -on the executing chain closes if the channel exists, the channel is not already closed, +on the executing chain closes if the channel exists, the channel is not already closed, the connection the channel exists upon is OPEN and the executing chain successfully verifies that the counterparty channel has been closed. - ### [Packets](https://github.com/cosmos/ibc-go/blob/main/modules/core/04-channel) Modules communicate with each other by sending packets over IBC channels. All IBC packets contain the destination `portID` and `channelID` along with the source `portID` and -`channelID`. This packet structure allows modules to know the sender module of a given packet. IBC packets -contain a sequence to optionally enforce ordering. +`channelID`. This packet structure allows modules to know the sender module of a given packet. IBC packets +contain a sequence to optionally enforce ordering. -IBC packets also contain a `TimeoutHeight` and a `TimeoutTimestamp` that determine the deadline before the receiving module must process a packet. +IBC packets also contain a `TimeoutHeight` and a `TimeoutTimestamp` that determine the deadline before the receiving module must process a packet. Modules send custom application data to each other inside the `Data []byte` field of the IBC packet. Thus, packet data is opaque to IBC handlers. It is incumbent on a sender module to encode @@ -240,8 +239,8 @@ module must decode that `Data` back to the original application data. ### [Receipts and Timeouts](https://github.com/cosmos/ibc-go/blob/main/modules/core/04-channel) -Since IBC works over a distributed network and relies on potentially faulty relayers to relay messages between ledgers, -IBC must handle the case where a packet does not get sent to its destination in a timely manner or at all. Packets must +Since IBC works over a distributed network and relies on potentially faulty relayers to relay messages between ledgers, +IBC must handle the case where a packet does not get sent to its destination in a timely manner or at all. Packets must specify a non-zero value for timeout height (`TimeoutHeight`) or timeout timestamp (`TimeoutTimestamp` ) after which a packet can no longer be successfully received on the destination chain. - The `timeoutHeight` indicates a consensus height on the destination chain after which the packet is no longer be processed, and instead counts as having timed-out. @@ -250,21 +249,21 @@ specify a non-zero value for timeout height (`TimeoutHeight`) or timeout timesta If the timeout passes without the packet being successfully received, the packet can no longer be received on the destination chain. The sending module can timeout the packet and take appropriate actions. -If the timeout is reached, then a proof of packet timeout can be submitted to the original chain. The original chain can then perform +If the timeout is reached, then a proof of packet timeout can be submitted to the original chain. The original chain can then perform application-specific logic to timeout the packet, perhaps by rolling back the packet send changes (refunding senders any locked funds, etc.). -- In ORDERED channels, a timeout of a single packet in the channel causes the channel to close. +- In ORDERED channels, a timeout of a single packet in the channel causes the channel to close. - - If packet sequence `n` times out, then a packet at sequence `k > n` cannot be received without violating the contract of ORDERED channels that packets are processed in the order that they are sent. - - Since ORDERED channels enforce this invariant, a proof that sequence `n` has not been received on the destination chain by the specified timeout of packet `n` is sufficient to timeout packet `n` and close the channel. + - If packet sequence `n` times out, then a packet at sequence `k > n` cannot be received without violating the contract of ORDERED channels that packets are processed in the order that they are sent. + - Since ORDERED channels enforce this invariant, a proof that sequence `n` has not been received on the destination chain by the specified timeout of packet `n` is sufficient to timeout packet `n` and close the channel. - In UNORDERED channels, the application-specific timeout logic for that packet is applied and the channel is not closed. - - Packets can be received in any order. + - Packets can be received in any order. - - IBC writes a packet receipt for each sequence receives in the UNORDERED channel. This receipt does not contain information; it is simply a marker intended to signify that the UNORDERED channel has received a packet at the specified sequence. + - IBC writes a packet receipt for each sequence receives in the UNORDERED channel. This receipt does not contain information; it is simply a marker intended to signify that the UNORDERED channel has received a packet at the specified sequence. - - To timeout a packet on an UNORDERED channel, a proof is required that a packet receipt **does not exist** for the packet's sequence by the specified timeout. + - To timeout a packet on an UNORDERED channel, a proof is required that a packet receipt **does not exist** for the packet's sequence by the specified timeout. For this reason, most modules should use UNORDERED channels as they require fewer liveness guarantees to function effectively for users of that channel. @@ -272,16 +271,16 @@ For this reason, most modules should use UNORDERED channels as they require fewe Modules can also choose to write application-specific acknowledgments upon processing a packet. Acknowledgments can be done: -- Synchronously on `OnRecvPacket` if the module processes packets as soon as they are received from IBC module. +- Synchronously on `OnRecvPacket` if the module processes packets as soon as they are received from IBC module. - Asynchronously if module processes packets at some later point after receiving the packet. -This acknowledgment data is opaque to IBC much like the packet `Data` and is treated by IBC as a simple byte string `[]byte`. Receiver modules must encode their acknowledgment so that the sender module can decode it correctly. The encoding must be negotiated between the two parties during version negotiation in the channel handshake. +This acknowledgment data is opaque to IBC much like the packet `Data` and is treated by IBC as a simple byte string `[]byte`. Receiver modules must encode their acknowledgment so that the sender module can decode it correctly. The encoding must be negotiated between the two parties during version negotiation in the channel handshake. The acknowledgment can encode whether the packet processing succeeded or failed, along with additional information that allows the sender module to take appropriate action. After the acknowledgment has been written by the receiving chain, a relayer relays the acknowledgment back to the original sender module. -The original sender module then executes application-specific acknowledgment logic using the contents of the acknowledgment. +The original sender module then executes application-specific acknowledgment logic using the contents of the acknowledgment. - After an acknowledgement fails, packet-send changes can be rolled back (for example, refunding senders in ICS20). @@ -291,7 +290,7 @@ The original sender module then executes application-specific acknowledgment log If you want to learn more about IBC, check the following specifications: -* [IBC specification overview](https://github.com/cosmos/ibc/blob/master/README.md) +- [IBC specification overview](https://github.com/cosmos/ibc/blob/master/README.md) ## Next {hide} diff --git a/docs/ibc/proposals.md b/docs/ibc/proposals.md index f6bf351a705..dc22eb441af 100644 --- a/docs/ibc/proposals.md +++ b/docs/ibc/proposals.md @@ -4,7 +4,7 @@ order: 5 # Governance Proposals -In uncommon situations, a highly valued client may become frozen due to uncontrollable +In uncommon situations, a highly valued client may become frozen due to uncontrollable circumstances. A highly valued client might have hundreds of channels being actively used. Some of those channels might have a significant amount of locked tokens used for ICS 20. @@ -12,41 +12,47 @@ If the one third of the validator set of the chain the client represents decides they can sign off on two valid but conflicting headers each signed by the other one third of the honest validator set. The light client can now be updated with two valid, but conflicting headers at the same height. The light client cannot know which header is trustworthy and therefore -evidence of such misbehaviour is likely to be submitted resulting in a frozen light client. +evidence of such misbehaviour is likely to be submitted resulting in a frozen light client. Frozen light clients cannot be updated under any circumstance except via a governance proposal. -Since a quorum of validators can sign arbitrary state roots which may not be valid executions +Since a quorum of validators can sign arbitrary state roots which may not be valid executions of the state machine, a governance proposal has been added to ease the complexity of unfreezing or updating clients which have become "stuck". Without this mechanism, validator sets would need -to construct a state root to unfreeze the client. Unfreezing clients, re-enables all of the channels -built upon that client. This may result in recovery of otherwise lost funds. +to construct a state root to unfreeze the client. Unfreezing clients, re-enables all of the channels +built upon that client. This may result in recovery of otherwise lost funds. -Tendermint light clients may become expired if the trusting period has passed since their +Tendermint light clients may become expired if the trusting period has passed since their last update. This may occur if relayers stop submitting headers to update the clients. -An unplanned upgrade by the counterparty chain may also result in expired clients. If the counterparty -chain undergoes an unplanned upgrade, there may be no commitment to that upgrade signed by the validator -set before the chain-id changes. In this situation, the validator set of the last valid update for the -light client is never expected to produce another valid header since the chain-id has changed, which will -ultimately lead the on-chain light client to become expired. +An unplanned upgrade by the counterparty chain may also result in expired clients. If the counterparty +chain undergoes an unplanned upgrade, there may be no commitment to that upgrade signed by the validator +set before the chain-id changes. In this situation, the validator set of the last valid update for the +light client is never expected to produce another valid header since the chain-id has changed, which will +ultimately lead the on-chain light client to become expired. In the case that a highly valued light client is frozen, expired, or rendered non-updateable, a -governance proposal may be submitted to update this client, known as the subject client. The +governance proposal may be submitted to update this client, known as the subject client. The proposal includes the client identifier for the subject and the client identifier for a substitute -client. Light client implementations may implement custom updating logic, but in most cases, +client. Light client implementations may implement custom updating logic, but in most cases, the subject will be updated to the latest consensus state of the substitute client, if the proposal passes. -The substitute client is used as a "stand in" while the subject is on trial. It is best practice to create -a substitute client *after* the subject has become frozen to avoid the substitute from also becoming frozen. -An active substitute client allows headers to be submitted during the voting period to prevent accidental expiry -once the proposal passes. +The substitute client is used as a "stand in" while the subject is on trial. It is best practice to create +a substitute client _after_ the subject has become frozen to avoid the substitute from also becoming frozen. +An active substitute client allows headers to be submitted during the voting period to prevent accidental expiry +once the proposal passes. + +_note_ two of these parameters: `AllowUpdateAfterExpiry` and `AllowUpdateAfterMisbehavior` have been deprecated, and will both be set to `false` upon upgrades even if they were previously set to `true`. These parameters will no longer play a role in restricting a client upgrade. Please see ADR026 for more details. # How to recover an expired client with a governance proposal See also the relevant documentation: [ADR-026, IBC client recovery mechanisms](../architecture/adr-026-ibc-client-recovery-mechanisms.md) -### Preconditions +> **Who is this information for?** +> Although technically anyone can submit the governance proposal to recover an expired client, often it will be **relayer operators** (at least coordinating the submission). + +## Preconditions + - The chain is updated with ibc-go >= v1.1.0. -- The client identifier of an active client for the same counterparty chain. +- There exists an active client (with a known client identifier) for the same counterparty chain as the expired client. - The governance deposit. ## Steps @@ -55,7 +61,7 @@ See also the relevant documentation: [ADR-026, IBC client recovery mechanisms](. Check if the client is attached to the expected `chain-id`. For example, for an expired Tendermint client representing the Akash chain the client state looks like this on querying the client state: -``` +```text { client_id: 07-tendermint-146 client_state: @@ -70,18 +76,54 @@ The client is attached to the expected Akash `chain-id`. Note that although the ### Step 2 -If the chain has been updated to ibc-go >= v1.1.0, anyone can submit the governance proposal to recover the client by executing this via cli: +If the chain has been updated to ibc-go >= v1.1.0, anyone can submit the governance proposal to recover the client by executing this via CLI. -``` - tx gov submit-proposal update-client -``` +> Note that the Cosmos SDK has updated how governance proposals are submitted in SDK v0.46, now requiring to pass a .json proposal file + +- From SDK v0.46.x onwards + + ```shell + tx gov submit-proposal [path-to-proposal-json] + ``` + + where `proposal.json` contains: + + ```json + { + "messages": [ + { + "@type": "/ibc.core.client.v1.ClientUpdateProposal", + "title": "title_string", + "description": "description_string", + "subject_client_id": "expired_client_id_string", + "substitute_client_id": "active_client_id_string" + } + ], + "metadata": "", + "deposit": "10stake" + } + ``` + + Alternatively there's a legacy command (that is no longer recommended though): + + ```shell + tx gov submit-legacy-proposal update-client + ``` + +- Until SDK v0.45.x + + ```shell + tx gov submit-proposal update-client + ``` + +The `` identifier is the proposed client to be updated. This client must be either frozen or expired. -The `` should be a client identifier on the same chain as the expired or frozen client. This client identifier should connect to the same chain as the expired or frozen client. This means: use the active client that is currently being used to relay packets between the two chains as the replacement client. +The `` represents a substitute client. It carries all the state for the client which may be updated. It must have identical client and chain parameters to the client which may be updated (except for latest height, frozen height, and chain ID). It should be continually updated during the voting period. -After this, it is just a question of who funds the governance deposit and if the chain in question votes yes. +After this, all that remains is deciding who funds the governance deposit and ensuring the governance proposal passes. If it does, the client on trial will be updated to the latest state of the substitute. -## Important considerations +## Important considerations Please note that from v1.0.0 of ibc-go it will not be allowed for transactions to go to expired clients anymore, so please update to at least this version to prevent similar issues in the future. -Please also note that if the client on the other end of the transaction is also expired, that client will also need to update. This process updates only one client. \ No newline at end of file +Please also note that if the client on the other end of the transaction is also expired, that client will also need to update. This process updates only one client. diff --git a/docs/ibc/proto-docs.md b/docs/ibc/proto-docs.md index 5eaf8aaedb3..5edd28bbf0c 100644 --- a/docs/ibc/proto-docs.md +++ b/docs/ibc/proto-docs.md @@ -1,5406 +1,7 @@ - -# Protobuf Documentation - + -## Table of Contents - -- [ibc/applications/fee/v1/ack.proto](#ibc/applications/fee/v1/ack.proto) - - [IncentivizedAcknowledgement](#ibc.applications.fee.v1.IncentivizedAcknowledgement) - -- [ibc/core/client/v1/client.proto](#ibc/core/client/v1/client.proto) - - [ClientConsensusStates](#ibc.core.client.v1.ClientConsensusStates) - - [ClientUpdateProposal](#ibc.core.client.v1.ClientUpdateProposal) - - [ConsensusStateWithHeight](#ibc.core.client.v1.ConsensusStateWithHeight) - - [Height](#ibc.core.client.v1.Height) - - [IdentifiedClientState](#ibc.core.client.v1.IdentifiedClientState) - - [Params](#ibc.core.client.v1.Params) - - [UpgradeProposal](#ibc.core.client.v1.UpgradeProposal) - -- [ibc/core/channel/v1/channel.proto](#ibc/core/channel/v1/channel.proto) - - [Acknowledgement](#ibc.core.channel.v1.Acknowledgement) - - [Channel](#ibc.core.channel.v1.Channel) - - [Counterparty](#ibc.core.channel.v1.Counterparty) - - [IdentifiedChannel](#ibc.core.channel.v1.IdentifiedChannel) - - [Packet](#ibc.core.channel.v1.Packet) - - [PacketId](#ibc.core.channel.v1.PacketId) - - [PacketState](#ibc.core.channel.v1.PacketState) - - - [Order](#ibc.core.channel.v1.Order) - - [State](#ibc.core.channel.v1.State) - -- [ibc/applications/fee/v1/fee.proto](#ibc/applications/fee/v1/fee.proto) - - [Fee](#ibc.applications.fee.v1.Fee) - - [IdentifiedPacketFees](#ibc.applications.fee.v1.IdentifiedPacketFees) - - [PacketFee](#ibc.applications.fee.v1.PacketFee) - - [PacketFees](#ibc.applications.fee.v1.PacketFees) - -- [ibc/applications/fee/v1/genesis.proto](#ibc/applications/fee/v1/genesis.proto) - - [FeeEnabledChannel](#ibc.applications.fee.v1.FeeEnabledChannel) - - [ForwardRelayerAddress](#ibc.applications.fee.v1.ForwardRelayerAddress) - - [GenesisState](#ibc.applications.fee.v1.GenesisState) - - [RegisteredCounterpartyPayee](#ibc.applications.fee.v1.RegisteredCounterpartyPayee) - - [RegisteredPayee](#ibc.applications.fee.v1.RegisteredPayee) - -- [ibc/applications/fee/v1/metadata.proto](#ibc/applications/fee/v1/metadata.proto) - - [Metadata](#ibc.applications.fee.v1.Metadata) - -- [ibc/applications/fee/v1/query.proto](#ibc/applications/fee/v1/query.proto) - - [QueryCounterpartyPayeeRequest](#ibc.applications.fee.v1.QueryCounterpartyPayeeRequest) - - [QueryCounterpartyPayeeResponse](#ibc.applications.fee.v1.QueryCounterpartyPayeeResponse) - - [QueryFeeEnabledChannelRequest](#ibc.applications.fee.v1.QueryFeeEnabledChannelRequest) - - [QueryFeeEnabledChannelResponse](#ibc.applications.fee.v1.QueryFeeEnabledChannelResponse) - - [QueryFeeEnabledChannelsRequest](#ibc.applications.fee.v1.QueryFeeEnabledChannelsRequest) - - [QueryFeeEnabledChannelsResponse](#ibc.applications.fee.v1.QueryFeeEnabledChannelsResponse) - - [QueryIncentivizedPacketRequest](#ibc.applications.fee.v1.QueryIncentivizedPacketRequest) - - [QueryIncentivizedPacketResponse](#ibc.applications.fee.v1.QueryIncentivizedPacketResponse) - - [QueryIncentivizedPacketsForChannelRequest](#ibc.applications.fee.v1.QueryIncentivizedPacketsForChannelRequest) - - [QueryIncentivizedPacketsForChannelResponse](#ibc.applications.fee.v1.QueryIncentivizedPacketsForChannelResponse) - - [QueryIncentivizedPacketsRequest](#ibc.applications.fee.v1.QueryIncentivizedPacketsRequest) - - [QueryIncentivizedPacketsResponse](#ibc.applications.fee.v1.QueryIncentivizedPacketsResponse) - - [QueryPayeeRequest](#ibc.applications.fee.v1.QueryPayeeRequest) - - [QueryPayeeResponse](#ibc.applications.fee.v1.QueryPayeeResponse) - - [QueryTotalAckFeesRequest](#ibc.applications.fee.v1.QueryTotalAckFeesRequest) - - [QueryTotalAckFeesResponse](#ibc.applications.fee.v1.QueryTotalAckFeesResponse) - - [QueryTotalRecvFeesRequest](#ibc.applications.fee.v1.QueryTotalRecvFeesRequest) - - [QueryTotalRecvFeesResponse](#ibc.applications.fee.v1.QueryTotalRecvFeesResponse) - - [QueryTotalTimeoutFeesRequest](#ibc.applications.fee.v1.QueryTotalTimeoutFeesRequest) - - [QueryTotalTimeoutFeesResponse](#ibc.applications.fee.v1.QueryTotalTimeoutFeesResponse) - - - [Query](#ibc.applications.fee.v1.Query) - -- [ibc/applications/fee/v1/tx.proto](#ibc/applications/fee/v1/tx.proto) - - [MsgPayPacketFee](#ibc.applications.fee.v1.MsgPayPacketFee) - - [MsgPayPacketFeeAsync](#ibc.applications.fee.v1.MsgPayPacketFeeAsync) - - [MsgPayPacketFeeAsyncResponse](#ibc.applications.fee.v1.MsgPayPacketFeeAsyncResponse) - - [MsgPayPacketFeeResponse](#ibc.applications.fee.v1.MsgPayPacketFeeResponse) - - [MsgRegisterCounterpartyPayee](#ibc.applications.fee.v1.MsgRegisterCounterpartyPayee) - - [MsgRegisterCounterpartyPayeeResponse](#ibc.applications.fee.v1.MsgRegisterCounterpartyPayeeResponse) - - [MsgRegisterPayee](#ibc.applications.fee.v1.MsgRegisterPayee) - - [MsgRegisterPayeeResponse](#ibc.applications.fee.v1.MsgRegisterPayeeResponse) - - - [Msg](#ibc.applications.fee.v1.Msg) - -- [ibc/applications/interchain_accounts/controller/v1/controller.proto](#ibc/applications/interchain_accounts/controller/v1/controller.proto) - - [Params](#ibc.applications.interchain_accounts.controller.v1.Params) - -- [ibc/applications/interchain_accounts/controller/v1/query.proto](#ibc/applications/interchain_accounts/controller/v1/query.proto) - - [QueryParamsRequest](#ibc.applications.interchain_accounts.controller.v1.QueryParamsRequest) - - [QueryParamsResponse](#ibc.applications.interchain_accounts.controller.v1.QueryParamsResponse) - - - [Query](#ibc.applications.interchain_accounts.controller.v1.Query) - -- [ibc/applications/interchain_accounts/host/v1/host.proto](#ibc/applications/interchain_accounts/host/v1/host.proto) - - [Params](#ibc.applications.interchain_accounts.host.v1.Params) - -- [ibc/applications/interchain_accounts/host/v1/query.proto](#ibc/applications/interchain_accounts/host/v1/query.proto) - - [QueryParamsRequest](#ibc.applications.interchain_accounts.host.v1.QueryParamsRequest) - - [QueryParamsResponse](#ibc.applications.interchain_accounts.host.v1.QueryParamsResponse) - - - [Query](#ibc.applications.interchain_accounts.host.v1.Query) - -- [ibc/applications/interchain_accounts/v1/account.proto](#ibc/applications/interchain_accounts/v1/account.proto) - - [InterchainAccount](#ibc.applications.interchain_accounts.v1.InterchainAccount) - -- [ibc/applications/interchain_accounts/v1/genesis.proto](#ibc/applications/interchain_accounts/v1/genesis.proto) - - [ActiveChannel](#ibc.applications.interchain_accounts.v1.ActiveChannel) - - [ControllerGenesisState](#ibc.applications.interchain_accounts.v1.ControllerGenesisState) - - [GenesisState](#ibc.applications.interchain_accounts.v1.GenesisState) - - [HostGenesisState](#ibc.applications.interchain_accounts.v1.HostGenesisState) - - [RegisteredInterchainAccount](#ibc.applications.interchain_accounts.v1.RegisteredInterchainAccount) - -- [ibc/applications/interchain_accounts/v1/metadata.proto](#ibc/applications/interchain_accounts/v1/metadata.proto) - - [Metadata](#ibc.applications.interchain_accounts.v1.Metadata) - -- [ibc/applications/interchain_accounts/v1/packet.proto](#ibc/applications/interchain_accounts/v1/packet.proto) - - [CosmosTx](#ibc.applications.interchain_accounts.v1.CosmosTx) - - [InterchainAccountPacketData](#ibc.applications.interchain_accounts.v1.InterchainAccountPacketData) - - - [Type](#ibc.applications.interchain_accounts.v1.Type) - -- [ibc/applications/transfer/v1/transfer.proto](#ibc/applications/transfer/v1/transfer.proto) - - [DenomTrace](#ibc.applications.transfer.v1.DenomTrace) - - [Params](#ibc.applications.transfer.v1.Params) - -- [ibc/applications/transfer/v1/genesis.proto](#ibc/applications/transfer/v1/genesis.proto) - - [GenesisState](#ibc.applications.transfer.v1.GenesisState) - -- [ibc/applications/transfer/v1/query.proto](#ibc/applications/transfer/v1/query.proto) - - [QueryDenomHashRequest](#ibc.applications.transfer.v1.QueryDenomHashRequest) - - [QueryDenomHashResponse](#ibc.applications.transfer.v1.QueryDenomHashResponse) - - [QueryDenomTraceRequest](#ibc.applications.transfer.v1.QueryDenomTraceRequest) - - [QueryDenomTraceResponse](#ibc.applications.transfer.v1.QueryDenomTraceResponse) - - [QueryDenomTracesRequest](#ibc.applications.transfer.v1.QueryDenomTracesRequest) - - [QueryDenomTracesResponse](#ibc.applications.transfer.v1.QueryDenomTracesResponse) - - [QueryEscrowAddressRequest](#ibc.applications.transfer.v1.QueryEscrowAddressRequest) - - [QueryEscrowAddressResponse](#ibc.applications.transfer.v1.QueryEscrowAddressResponse) - - [QueryParamsRequest](#ibc.applications.transfer.v1.QueryParamsRequest) - - [QueryParamsResponse](#ibc.applications.transfer.v1.QueryParamsResponse) - - - [Query](#ibc.applications.transfer.v1.Query) - -- [ibc/applications/transfer/v1/tx.proto](#ibc/applications/transfer/v1/tx.proto) - - [MsgTransfer](#ibc.applications.transfer.v1.MsgTransfer) - - [MsgTransferResponse](#ibc.applications.transfer.v1.MsgTransferResponse) - - - [Msg](#ibc.applications.transfer.v1.Msg) - -- [ibc/applications/transfer/v2/packet.proto](#ibc/applications/transfer/v2/packet.proto) - - [FungibleTokenPacketData](#ibc.applications.transfer.v2.FungibleTokenPacketData) - -- [ibc/core/channel/v1/genesis.proto](#ibc/core/channel/v1/genesis.proto) - - [GenesisState](#ibc.core.channel.v1.GenesisState) - - [PacketSequence](#ibc.core.channel.v1.PacketSequence) - -- [ibc/core/channel/v1/query.proto](#ibc/core/channel/v1/query.proto) - - [QueryChannelClientStateRequest](#ibc.core.channel.v1.QueryChannelClientStateRequest) - - [QueryChannelClientStateResponse](#ibc.core.channel.v1.QueryChannelClientStateResponse) - - [QueryChannelConsensusStateRequest](#ibc.core.channel.v1.QueryChannelConsensusStateRequest) - - [QueryChannelConsensusStateResponse](#ibc.core.channel.v1.QueryChannelConsensusStateResponse) - - [QueryChannelRequest](#ibc.core.channel.v1.QueryChannelRequest) - - [QueryChannelResponse](#ibc.core.channel.v1.QueryChannelResponse) - - [QueryChannelsRequest](#ibc.core.channel.v1.QueryChannelsRequest) - - [QueryChannelsResponse](#ibc.core.channel.v1.QueryChannelsResponse) - - [QueryConnectionChannelsRequest](#ibc.core.channel.v1.QueryConnectionChannelsRequest) - - [QueryConnectionChannelsResponse](#ibc.core.channel.v1.QueryConnectionChannelsResponse) - - [QueryNextSequenceReceiveRequest](#ibc.core.channel.v1.QueryNextSequenceReceiveRequest) - - [QueryNextSequenceReceiveResponse](#ibc.core.channel.v1.QueryNextSequenceReceiveResponse) - - [QueryPacketAcknowledgementRequest](#ibc.core.channel.v1.QueryPacketAcknowledgementRequest) - - [QueryPacketAcknowledgementResponse](#ibc.core.channel.v1.QueryPacketAcknowledgementResponse) - - [QueryPacketAcknowledgementsRequest](#ibc.core.channel.v1.QueryPacketAcknowledgementsRequest) - - [QueryPacketAcknowledgementsResponse](#ibc.core.channel.v1.QueryPacketAcknowledgementsResponse) - - [QueryPacketCommitmentRequest](#ibc.core.channel.v1.QueryPacketCommitmentRequest) - - [QueryPacketCommitmentResponse](#ibc.core.channel.v1.QueryPacketCommitmentResponse) - - [QueryPacketCommitmentsRequest](#ibc.core.channel.v1.QueryPacketCommitmentsRequest) - - [QueryPacketCommitmentsResponse](#ibc.core.channel.v1.QueryPacketCommitmentsResponse) - - [QueryPacketReceiptRequest](#ibc.core.channel.v1.QueryPacketReceiptRequest) - - [QueryPacketReceiptResponse](#ibc.core.channel.v1.QueryPacketReceiptResponse) - - [QueryUnreceivedAcksRequest](#ibc.core.channel.v1.QueryUnreceivedAcksRequest) - - [QueryUnreceivedAcksResponse](#ibc.core.channel.v1.QueryUnreceivedAcksResponse) - - [QueryUnreceivedPacketsRequest](#ibc.core.channel.v1.QueryUnreceivedPacketsRequest) - - [QueryUnreceivedPacketsResponse](#ibc.core.channel.v1.QueryUnreceivedPacketsResponse) - - - [Query](#ibc.core.channel.v1.Query) - -- [ibc/core/channel/v1/upgrade.proto](#ibc/core/channel/v1/upgrade.proto) - - [ErrorReceipt](#ibc.core.channel.v1.ErrorReceipt) - - [UpgradeTimeout](#ibc.core.channel.v1.UpgradeTimeout) - -- [ibc/core/channel/v1/tx.proto](#ibc/core/channel/v1/tx.proto) - - [MsgAcknowledgement](#ibc.core.channel.v1.MsgAcknowledgement) - - [MsgAcknowledgementResponse](#ibc.core.channel.v1.MsgAcknowledgementResponse) - - [MsgChannelCloseConfirm](#ibc.core.channel.v1.MsgChannelCloseConfirm) - - [MsgChannelCloseConfirmResponse](#ibc.core.channel.v1.MsgChannelCloseConfirmResponse) - - [MsgChannelCloseInit](#ibc.core.channel.v1.MsgChannelCloseInit) - - [MsgChannelCloseInitResponse](#ibc.core.channel.v1.MsgChannelCloseInitResponse) - - [MsgChannelOpenAck](#ibc.core.channel.v1.MsgChannelOpenAck) - - [MsgChannelOpenAckResponse](#ibc.core.channel.v1.MsgChannelOpenAckResponse) - - [MsgChannelOpenConfirm](#ibc.core.channel.v1.MsgChannelOpenConfirm) - - [MsgChannelOpenConfirmResponse](#ibc.core.channel.v1.MsgChannelOpenConfirmResponse) - - [MsgChannelOpenInit](#ibc.core.channel.v1.MsgChannelOpenInit) - - [MsgChannelOpenInitResponse](#ibc.core.channel.v1.MsgChannelOpenInitResponse) - - [MsgChannelOpenTry](#ibc.core.channel.v1.MsgChannelOpenTry) - - [MsgChannelOpenTryResponse](#ibc.core.channel.v1.MsgChannelOpenTryResponse) - - [MsgChannelUpgradeAck](#ibc.core.channel.v1.MsgChannelUpgradeAck) - - [MsgChannelUpgradeAckResponse](#ibc.core.channel.v1.MsgChannelUpgradeAckResponse) - - [MsgChannelUpgradeCancel](#ibc.core.channel.v1.MsgChannelUpgradeCancel) - - [MsgChannelUpgradeCancelResponse](#ibc.core.channel.v1.MsgChannelUpgradeCancelResponse) - - [MsgChannelUpgradeConfirm](#ibc.core.channel.v1.MsgChannelUpgradeConfirm) - - [MsgChannelUpgradeConfirmResponse](#ibc.core.channel.v1.MsgChannelUpgradeConfirmResponse) - - [MsgChannelUpgradeInit](#ibc.core.channel.v1.MsgChannelUpgradeInit) - - [MsgChannelUpgradeInitResponse](#ibc.core.channel.v1.MsgChannelUpgradeInitResponse) - - [MsgChannelUpgradeTimeout](#ibc.core.channel.v1.MsgChannelUpgradeTimeout) - - [MsgChannelUpgradeTimeoutResponse](#ibc.core.channel.v1.MsgChannelUpgradeTimeoutResponse) - - [MsgChannelUpgradeTry](#ibc.core.channel.v1.MsgChannelUpgradeTry) - - [MsgChannelUpgradeTryResponse](#ibc.core.channel.v1.MsgChannelUpgradeTryResponse) - - [MsgRecvPacket](#ibc.core.channel.v1.MsgRecvPacket) - - [MsgRecvPacketResponse](#ibc.core.channel.v1.MsgRecvPacketResponse) - - [MsgTimeout](#ibc.core.channel.v1.MsgTimeout) - - [MsgTimeoutOnClose](#ibc.core.channel.v1.MsgTimeoutOnClose) - - [MsgTimeoutOnCloseResponse](#ibc.core.channel.v1.MsgTimeoutOnCloseResponse) - - [MsgTimeoutResponse](#ibc.core.channel.v1.MsgTimeoutResponse) - - - [ResponseResultType](#ibc.core.channel.v1.ResponseResultType) - - - [Msg](#ibc.core.channel.v1.Msg) - -- [ibc/core/client/v1/genesis.proto](#ibc/core/client/v1/genesis.proto) - - [GenesisMetadata](#ibc.core.client.v1.GenesisMetadata) - - [GenesisState](#ibc.core.client.v1.GenesisState) - - [IdentifiedGenesisMetadata](#ibc.core.client.v1.IdentifiedGenesisMetadata) - -- [ibc/core/client/v1/query.proto](#ibc/core/client/v1/query.proto) - - [QueryClientParamsRequest](#ibc.core.client.v1.QueryClientParamsRequest) - - [QueryClientParamsResponse](#ibc.core.client.v1.QueryClientParamsResponse) - - [QueryClientStateRequest](#ibc.core.client.v1.QueryClientStateRequest) - - [QueryClientStateResponse](#ibc.core.client.v1.QueryClientStateResponse) - - [QueryClientStatesRequest](#ibc.core.client.v1.QueryClientStatesRequest) - - [QueryClientStatesResponse](#ibc.core.client.v1.QueryClientStatesResponse) - - [QueryClientStatusRequest](#ibc.core.client.v1.QueryClientStatusRequest) - - [QueryClientStatusResponse](#ibc.core.client.v1.QueryClientStatusResponse) - - [QueryConsensusStateHeightsRequest](#ibc.core.client.v1.QueryConsensusStateHeightsRequest) - - [QueryConsensusStateHeightsResponse](#ibc.core.client.v1.QueryConsensusStateHeightsResponse) - - [QueryConsensusStateRequest](#ibc.core.client.v1.QueryConsensusStateRequest) - - [QueryConsensusStateResponse](#ibc.core.client.v1.QueryConsensusStateResponse) - - [QueryConsensusStatesRequest](#ibc.core.client.v1.QueryConsensusStatesRequest) - - [QueryConsensusStatesResponse](#ibc.core.client.v1.QueryConsensusStatesResponse) - - [QueryUpgradedClientStateRequest](#ibc.core.client.v1.QueryUpgradedClientStateRequest) - - [QueryUpgradedClientStateResponse](#ibc.core.client.v1.QueryUpgradedClientStateResponse) - - [QueryUpgradedConsensusStateRequest](#ibc.core.client.v1.QueryUpgradedConsensusStateRequest) - - [QueryUpgradedConsensusStateResponse](#ibc.core.client.v1.QueryUpgradedConsensusStateResponse) - - - [Query](#ibc.core.client.v1.Query) - -- [ibc/core/client/v1/tx.proto](#ibc/core/client/v1/tx.proto) - - [MsgCreateClient](#ibc.core.client.v1.MsgCreateClient) - - [MsgCreateClientResponse](#ibc.core.client.v1.MsgCreateClientResponse) - - [MsgSubmitMisbehaviour](#ibc.core.client.v1.MsgSubmitMisbehaviour) - - [MsgSubmitMisbehaviourResponse](#ibc.core.client.v1.MsgSubmitMisbehaviourResponse) - - [MsgUpdateClient](#ibc.core.client.v1.MsgUpdateClient) - - [MsgUpdateClientResponse](#ibc.core.client.v1.MsgUpdateClientResponse) - - [MsgUpgradeClient](#ibc.core.client.v1.MsgUpgradeClient) - - [MsgUpgradeClientResponse](#ibc.core.client.v1.MsgUpgradeClientResponse) - - - [Msg](#ibc.core.client.v1.Msg) - -- [ibc/core/commitment/v1/commitment.proto](#ibc/core/commitment/v1/commitment.proto) - - [MerklePath](#ibc.core.commitment.v1.MerklePath) - - [MerklePrefix](#ibc.core.commitment.v1.MerklePrefix) - - [MerkleProof](#ibc.core.commitment.v1.MerkleProof) - - [MerkleRoot](#ibc.core.commitment.v1.MerkleRoot) - -- [ibc/core/connection/v1/connection.proto](#ibc/core/connection/v1/connection.proto) - - [ClientPaths](#ibc.core.connection.v1.ClientPaths) - - [ConnectionEnd](#ibc.core.connection.v1.ConnectionEnd) - - [ConnectionPaths](#ibc.core.connection.v1.ConnectionPaths) - - [Counterparty](#ibc.core.connection.v1.Counterparty) - - [IdentifiedConnection](#ibc.core.connection.v1.IdentifiedConnection) - - [Params](#ibc.core.connection.v1.Params) - - [Version](#ibc.core.connection.v1.Version) - - - [State](#ibc.core.connection.v1.State) - -- [ibc/core/connection/v1/genesis.proto](#ibc/core/connection/v1/genesis.proto) - - [GenesisState](#ibc.core.connection.v1.GenesisState) - -- [ibc/core/connection/v1/query.proto](#ibc/core/connection/v1/query.proto) - - [QueryClientConnectionsRequest](#ibc.core.connection.v1.QueryClientConnectionsRequest) - - [QueryClientConnectionsResponse](#ibc.core.connection.v1.QueryClientConnectionsResponse) - - [QueryConnectionClientStateRequest](#ibc.core.connection.v1.QueryConnectionClientStateRequest) - - [QueryConnectionClientStateResponse](#ibc.core.connection.v1.QueryConnectionClientStateResponse) - - [QueryConnectionConsensusStateRequest](#ibc.core.connection.v1.QueryConnectionConsensusStateRequest) - - [QueryConnectionConsensusStateResponse](#ibc.core.connection.v1.QueryConnectionConsensusStateResponse) - - [QueryConnectionRequest](#ibc.core.connection.v1.QueryConnectionRequest) - - [QueryConnectionResponse](#ibc.core.connection.v1.QueryConnectionResponse) - - [QueryConnectionsRequest](#ibc.core.connection.v1.QueryConnectionsRequest) - - [QueryConnectionsResponse](#ibc.core.connection.v1.QueryConnectionsResponse) - - - [Query](#ibc.core.connection.v1.Query) - -- [ibc/core/connection/v1/tx.proto](#ibc/core/connection/v1/tx.proto) - - [MsgConnectionOpenAck](#ibc.core.connection.v1.MsgConnectionOpenAck) - - [MsgConnectionOpenAckResponse](#ibc.core.connection.v1.MsgConnectionOpenAckResponse) - - [MsgConnectionOpenConfirm](#ibc.core.connection.v1.MsgConnectionOpenConfirm) - - [MsgConnectionOpenConfirmResponse](#ibc.core.connection.v1.MsgConnectionOpenConfirmResponse) - - [MsgConnectionOpenInit](#ibc.core.connection.v1.MsgConnectionOpenInit) - - [MsgConnectionOpenInitResponse](#ibc.core.connection.v1.MsgConnectionOpenInitResponse) - - [MsgConnectionOpenTry](#ibc.core.connection.v1.MsgConnectionOpenTry) - - [MsgConnectionOpenTryResponse](#ibc.core.connection.v1.MsgConnectionOpenTryResponse) - - - [Msg](#ibc.core.connection.v1.Msg) - -- [ibc/core/types/v1/genesis.proto](#ibc/core/types/v1/genesis.proto) - - [GenesisState](#ibc.core.types.v1.GenesisState) - -- [ibc/lightclients/localhost/v1/localhost.proto](#ibc/lightclients/localhost/v1/localhost.proto) - - [ClientState](#ibc.lightclients.localhost.v1.ClientState) - -- [ibc/lightclients/solomachine/v1/solomachine.proto](#ibc/lightclients/solomachine/v1/solomachine.proto) - - [ChannelStateData](#ibc.lightclients.solomachine.v1.ChannelStateData) - - [ClientState](#ibc.lightclients.solomachine.v1.ClientState) - - [ClientStateData](#ibc.lightclients.solomachine.v1.ClientStateData) - - [ConnectionStateData](#ibc.lightclients.solomachine.v1.ConnectionStateData) - - [ConsensusState](#ibc.lightclients.solomachine.v1.ConsensusState) - - [ConsensusStateData](#ibc.lightclients.solomachine.v1.ConsensusStateData) - - [Header](#ibc.lightclients.solomachine.v1.Header) - - [HeaderData](#ibc.lightclients.solomachine.v1.HeaderData) - - [Misbehaviour](#ibc.lightclients.solomachine.v1.Misbehaviour) - - [NextSequenceRecvData](#ibc.lightclients.solomachine.v1.NextSequenceRecvData) - - [PacketAcknowledgementData](#ibc.lightclients.solomachine.v1.PacketAcknowledgementData) - - [PacketCommitmentData](#ibc.lightclients.solomachine.v1.PacketCommitmentData) - - [PacketReceiptAbsenceData](#ibc.lightclients.solomachine.v1.PacketReceiptAbsenceData) - - [SignBytes](#ibc.lightclients.solomachine.v1.SignBytes) - - [SignatureAndData](#ibc.lightclients.solomachine.v1.SignatureAndData) - - [TimestampedSignatureData](#ibc.lightclients.solomachine.v1.TimestampedSignatureData) - - - [DataType](#ibc.lightclients.solomachine.v1.DataType) - -- [ibc/lightclients/solomachine/v2/solomachine.proto](#ibc/lightclients/solomachine/v2/solomachine.proto) - - [ChannelStateData](#ibc.lightclients.solomachine.v2.ChannelStateData) - - [ClientState](#ibc.lightclients.solomachine.v2.ClientState) - - [ClientStateData](#ibc.lightclients.solomachine.v2.ClientStateData) - - [ConnectionStateData](#ibc.lightclients.solomachine.v2.ConnectionStateData) - - [ConsensusState](#ibc.lightclients.solomachine.v2.ConsensusState) - - [ConsensusStateData](#ibc.lightclients.solomachine.v2.ConsensusStateData) - - [Header](#ibc.lightclients.solomachine.v2.Header) - - [HeaderData](#ibc.lightclients.solomachine.v2.HeaderData) - - [Misbehaviour](#ibc.lightclients.solomachine.v2.Misbehaviour) - - [NextSequenceRecvData](#ibc.lightclients.solomachine.v2.NextSequenceRecvData) - - [PacketAcknowledgementData](#ibc.lightclients.solomachine.v2.PacketAcknowledgementData) - - [PacketCommitmentData](#ibc.lightclients.solomachine.v2.PacketCommitmentData) - - [PacketReceiptAbsenceData](#ibc.lightclients.solomachine.v2.PacketReceiptAbsenceData) - - [SignBytes](#ibc.lightclients.solomachine.v2.SignBytes) - - [SignatureAndData](#ibc.lightclients.solomachine.v2.SignatureAndData) - - [TimestampedSignatureData](#ibc.lightclients.solomachine.v2.TimestampedSignatureData) - - - [DataType](#ibc.lightclients.solomachine.v2.DataType) - -- [ibc/lightclients/tendermint/v1/tendermint.proto](#ibc/lightclients/tendermint/v1/tendermint.proto) - - [ClientState](#ibc.lightclients.tendermint.v1.ClientState) - - [ConsensusState](#ibc.lightclients.tendermint.v1.ConsensusState) - - [Fraction](#ibc.lightclients.tendermint.v1.Fraction) - - [Header](#ibc.lightclients.tendermint.v1.Header) - - [Misbehaviour](#ibc.lightclients.tendermint.v1.Misbehaviour) - -- [Scalar Value Types](#scalar-value-types) - - - - -

    Top

    - -## ibc/applications/fee/v1/ack.proto - - - - - -### IncentivizedAcknowledgement -IncentivizedAcknowledgement is the acknowledgement format to be used by applications wrapped in the fee middleware - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `app_acknowledgement` | [bytes](#bytes) | | the underlying app acknowledgement bytes | -| `forward_relayer_address` | [string](#string) | | the relayer address which submits the recv packet message | -| `underlying_app_success` | [bool](#bool) | | success flag of the base application callback | - - - - - - - - - - - - - - - - -

    Top

    - -## ibc/core/client/v1/client.proto - - - - - -### ClientConsensusStates -ClientConsensusStates defines all the stored consensus states for a given -client. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `client_id` | [string](#string) | | client identifier | -| `consensus_states` | [ConsensusStateWithHeight](#ibc.core.client.v1.ConsensusStateWithHeight) | repeated | consensus states and their heights associated with the client | - - - - - - - - -### ClientUpdateProposal -ClientUpdateProposal is a governance proposal. If it passes, the substitute -client's latest consensus state is copied over to the subject client. The proposal -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). - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `title` | [string](#string) | | the title of the update proposal | -| `description` | [string](#string) | | the description of the proposal | -| `subject_client_id` | [string](#string) | | the client identifier for the client to be updated if the proposal passes | -| `substitute_client_id` | [string](#string) | | the substitute client identifier for the client standing in for the subject client | - - - - - - - - -### ConsensusStateWithHeight -ConsensusStateWithHeight defines a consensus state with an additional height -field. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `height` | [Height](#ibc.core.client.v1.Height) | | consensus state height | -| `consensus_state` | [google.protobuf.Any](#google.protobuf.Any) | | consensus state | - - - - - - - - -### Height -Height is a monotonically increasing data type -that can be compared against another Height for the purposes of updating and -freezing clients - -Normally the RevisionHeight is incremented at each height while keeping -RevisionNumber the same. However some consensus algorithms may choose to -reset the height in certain conditions e.g. hard forks, state-machine -breaking changes In these cases, the RevisionNumber is incremented so that -height continues to be monitonically increasing even as the RevisionHeight -gets reset - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `revision_number` | [uint64](#uint64) | | the revision that the client is currently on | -| `revision_height` | [uint64](#uint64) | | the height within the given revision | - - - - - - - - -### IdentifiedClientState -IdentifiedClientState defines a client state with an additional client -identifier field. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `client_id` | [string](#string) | | client identifier | -| `client_state` | [google.protobuf.Any](#google.protobuf.Any) | | client state | - - - - - - - - -### Params -Params defines the set of IBC light client parameters. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `allowed_clients` | [string](#string) | repeated | allowed_clients defines the list of allowed client state types. | - - - - - - - - -### UpgradeProposal -UpgradeProposal is a gov Content type for initiating an IBC breaking -upgrade. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `title` | [string](#string) | | | -| `description` | [string](#string) | | | -| `plan` | [cosmos.upgrade.v1beta1.Plan](#cosmos.upgrade.v1beta1.Plan) | | | -| `upgraded_client_state` | [google.protobuf.Any](#google.protobuf.Any) | | An UpgradedClientState must be provided to perform an IBC breaking upgrade. This will make the chain commit to the correct upgraded (self) client state before the upgrade occurs, so that connecting chains can verify that the new upgraded client is valid by verifying a proof on the previous version of the chain. This will allow IBC connections to persist smoothly across planned chain upgrades | - - - - - - - - - - - - - - - - -

    Top

    - -## ibc/core/channel/v1/channel.proto - - - - - -### Acknowledgement -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 - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `result` | [bytes](#bytes) | | | -| `error` | [string](#string) | | | - - - - - - - - -### Channel -Channel defines pipeline for exactly-once packet delivery between specific -modules on separate blockchains, which has at least one end capable of -sending packets and one end capable of receiving packets. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `state` | [State](#ibc.core.channel.v1.State) | | current state of the channel end | -| `ordering` | [Order](#ibc.core.channel.v1.Order) | | whether the channel is ordered or unordered | -| `counterparty` | [Counterparty](#ibc.core.channel.v1.Counterparty) | | counterparty channel end | -| `connection_hops` | [string](#string) | repeated | list of connection identifiers, in order, along which packets sent on this channel will travel | -| `version` | [string](#string) | | opaque channel version, which is agreed upon during the handshake | - - - - - - - - -### Counterparty -Counterparty defines a channel end counterparty - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `port_id` | [string](#string) | | port on the counterparty chain which owns the other end of the channel. | -| `channel_id` | [string](#string) | | channel end on the counterparty chain | - - - - - - - - -### IdentifiedChannel -IdentifiedChannel defines a channel with additional port and channel -identifier fields. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `state` | [State](#ibc.core.channel.v1.State) | | current state of the channel end | -| `ordering` | [Order](#ibc.core.channel.v1.Order) | | whether the channel is ordered or unordered | -| `counterparty` | [Counterparty](#ibc.core.channel.v1.Counterparty) | | counterparty channel end | -| `connection_hops` | [string](#string) | repeated | list of connection identifiers, in order, along which packets sent on this channel will travel | -| `version` | [string](#string) | | opaque channel version, which is agreed upon during the handshake | -| `port_id` | [string](#string) | | port identifier | -| `channel_id` | [string](#string) | | channel identifier | - - - - - - - - -### Packet -Packet defines a type that carries data across different chains through IBC - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `sequence` | [uint64](#uint64) | | number corresponds to the order of sends and receives, where a Packet with an earlier sequence number must be sent and received before a Packet with a later sequence number. | -| `source_port` | [string](#string) | | identifies the port on the sending chain. | -| `source_channel` | [string](#string) | | identifies the channel end on the sending chain. | -| `destination_port` | [string](#string) | | identifies the port on the receiving chain. | -| `destination_channel` | [string](#string) | | identifies the channel end on the receiving chain. | -| `data` | [bytes](#bytes) | | actual opaque bytes transferred directly to the application module | -| `timeout_height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | block height after which the packet times out | -| `timeout_timestamp` | [uint64](#uint64) | | block timestamp (in nanoseconds) after which the packet times out | - - - - - - - - -### PacketId -PacketId is an identifer for a unique Packet -Source chains refer to packets by source port/channel -Destination chains refer to packets by destination port/channel - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `port_id` | [string](#string) | | channel port identifier | -| `channel_id` | [string](#string) | | channel unique identifier | -| `sequence` | [uint64](#uint64) | | packet sequence | - - - - - - - - -### PacketState -PacketState defines the generic type necessary to retrieve and store -packet commitments, acknowledgements, and receipts. -Caller is responsible for knowing the context necessary to interpret this -state as a commitment, acknowledgement, or a receipt. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `port_id` | [string](#string) | | channel port identifier. | -| `channel_id` | [string](#string) | | channel unique identifier. | -| `sequence` | [uint64](#uint64) | | packet sequence. | -| `data` | [bytes](#bytes) | | embedded data that represents packet state. | - - - - - - - - - - -### Order -Order defines if a channel is ORDERED or UNORDERED - -| Name | Number | Description | -| ---- | ------ | ----------- | -| ORDER_NONE_UNSPECIFIED | 0 | zero-value for channel ordering | -| ORDER_UNORDERED | 1 | packets can be delivered in any order, which may differ from the order in which they were sent. | -| ORDER_ORDERED | 2 | packets are delivered exactly in the order which they were sent | - - - - - -### State -State defines if a channel is in one of the following states: -CLOSED, INIT, TRYOPEN, OPEN, INITUPGRADE, TRYUPGRADE or UNINITIALIZED. - -| Name | Number | Description | -| ---- | ------ | ----------- | -| STATE_UNINITIALIZED_UNSPECIFIED | 0 | Default State | -| STATE_INIT | 1 | A channel has just started the opening handshake. | -| STATE_TRYOPEN | 2 | A channel has acknowledged the handshake step on the counterparty chain. | -| STATE_OPEN | 3 | A channel has completed the handshake. Open channels are ready to send and receive packets. | -| STATE_CLOSED | 4 | A channel has been closed and can no longer be used to send or receive packets. | -| STATE_INITUPGRADE | 5 | A channel has just started the channel upgrade handshake. The chain that is proposing the upgrade should set the channel state from OPEN to UPGRADEINIT. | -| STATE_TRYUPGRADE | 6 | A channel has acknowledged the upgrade handshake step on the counterparty chain. The counterparty chain that accepts the upgrade should set the channel state from OPEN to UPGRADETRY. | - - - - - - - - - - - -

    Top

    - -## ibc/applications/fee/v1/fee.proto - - - - - -### Fee -Fee defines the ICS29 receive, acknowledgement and timeout fees - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `recv_fee` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | the packet receive fee | -| `ack_fee` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | the packet acknowledgement fee | -| `timeout_fee` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | the packet timeout fee | - - - - - - - - -### IdentifiedPacketFees -IdentifiedPacketFees contains a list of type PacketFee and associated PacketId - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `packet_id` | [ibc.core.channel.v1.PacketId](#ibc.core.channel.v1.PacketId) | | unique packet identifier comprised of the channel ID, port ID and sequence | -| `packet_fees` | [PacketFee](#ibc.applications.fee.v1.PacketFee) | repeated | list of packet fees | - - - - - - - - -### PacketFee -PacketFee contains ICS29 relayer fees, refund address and optional list of permitted relayers - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `fee` | [Fee](#ibc.applications.fee.v1.Fee) | | fee encapsulates the recv, ack and timeout fees associated with an IBC packet | -| `refund_address` | [string](#string) | | the refund address for unspent fees | -| `relayers` | [string](#string) | repeated | optional list of relayers permitted to receive fees | - - - - - - - - -### PacketFees -PacketFees contains a list of type PacketFee - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `packet_fees` | [PacketFee](#ibc.applications.fee.v1.PacketFee) | repeated | list of packet fees | - - - - - - - - - - - - - - - - -

    Top

    - -## ibc/applications/fee/v1/genesis.proto - - - - - -### FeeEnabledChannel -FeeEnabledChannel contains the PortID & ChannelID for a fee enabled channel - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `port_id` | [string](#string) | | unique port identifier | -| `channel_id` | [string](#string) | | unique channel identifier | - - - - - - - - -### ForwardRelayerAddress -ForwardRelayerAddress contains the forward relayer address and PacketId used for async acknowledgements - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `address` | [string](#string) | | the forward relayer address | -| `packet_id` | [ibc.core.channel.v1.PacketId](#ibc.core.channel.v1.PacketId) | | unique packet identifer comprised of the channel ID, port ID and sequence | - - - - - - - - -### GenesisState -GenesisState defines the ICS29 fee middleware genesis state - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `identified_fees` | [IdentifiedPacketFees](#ibc.applications.fee.v1.IdentifiedPacketFees) | repeated | list of identified packet fees | -| `fee_enabled_channels` | [FeeEnabledChannel](#ibc.applications.fee.v1.FeeEnabledChannel) | repeated | list of fee enabled channels | -| `registered_payees` | [RegisteredPayee](#ibc.applications.fee.v1.RegisteredPayee) | repeated | list of registered payees | -| `registered_counterparty_payees` | [RegisteredCounterpartyPayee](#ibc.applications.fee.v1.RegisteredCounterpartyPayee) | repeated | list of registered counterparty payees | -| `forward_relayers` | [ForwardRelayerAddress](#ibc.applications.fee.v1.ForwardRelayerAddress) | repeated | list of forward relayer addresses | - - - - - - - - -### RegisteredCounterpartyPayee -RegisteredCounterpartyPayee contains the relayer address and counterparty payee address for a specific channel (used -for recv fee distribution) - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `channel_id` | [string](#string) | | unique channel identifier | -| `relayer` | [string](#string) | | the relayer address | -| `counterparty_payee` | [string](#string) | | the counterparty payee address | - - - - - - - - -### RegisteredPayee -RegisteredPayee contains the relayer address and payee address for a specific channel - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `channel_id` | [string](#string) | | unique channel identifier | -| `relayer` | [string](#string) | | the relayer address | -| `payee` | [string](#string) | | the payee address | - - - - - - - - - - - - - - - - -

    Top

    - -## ibc/applications/fee/v1/metadata.proto - - - - - -### Metadata -Metadata defines the ICS29 channel specific metadata encoded into the channel version bytestring -See ICS004: https://github.com/cosmos/ibc/tree/master/spec/core/ics-004-channel-and-packet-semantics#Versioning - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `fee_version` | [string](#string) | | fee_version defines the ICS29 fee version | -| `app_version` | [string](#string) | | app_version defines the underlying application version, which may or may not be a JSON encoded bytestring | - - - - - - - - - - - - - - - - -

    Top

    - -## ibc/applications/fee/v1/query.proto - - - - - -### QueryCounterpartyPayeeRequest -QueryCounterpartyPayeeRequest defines the request type for the CounterpartyPayee rpc - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `channel_id` | [string](#string) | | unique channel identifier | -| `relayer` | [string](#string) | | the relayer address to which the counterparty is registered | - - - - - - - - -### QueryCounterpartyPayeeResponse -QueryCounterpartyPayeeResponse defines the response type for the CounterpartyPayee rpc - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `counterparty_payee` | [string](#string) | | the counterparty payee address used to compensate forward relaying | - - - - - - - - -### QueryFeeEnabledChannelRequest -QueryFeeEnabledChannelRequest defines the request type for the FeeEnabledChannel rpc - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `port_id` | [string](#string) | | unique port identifier | -| `channel_id` | [string](#string) | | unique channel identifier | - - - - - - - - -### QueryFeeEnabledChannelResponse -QueryFeeEnabledChannelResponse defines the response type for the FeeEnabledChannel rpc - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `fee_enabled` | [bool](#bool) | | boolean flag representing the fee enabled channel status | - - - - - - - - -### QueryFeeEnabledChannelsRequest -QueryFeeEnabledChannelsRequest defines the request type for the FeeEnabledChannels rpc - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | pagination defines an optional pagination for the request. | -| `query_height` | [uint64](#uint64) | | block height at which to query | - - - - - - - - -### QueryFeeEnabledChannelsResponse -QueryFeeEnabledChannelsResponse defines the response type for the FeeEnabledChannels rpc - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `fee_enabled_channels` | [FeeEnabledChannel](#ibc.applications.fee.v1.FeeEnabledChannel) | repeated | list of fee enabled channels | - - - - - - - - -### QueryIncentivizedPacketRequest -QueryIncentivizedPacketRequest defines the request type for the IncentivizedPacket rpc - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `packet_id` | [ibc.core.channel.v1.PacketId](#ibc.core.channel.v1.PacketId) | | unique packet identifier comprised of channel ID, port ID and sequence | -| `query_height` | [uint64](#uint64) | | block height at which to query | - - - - - - - - -### QueryIncentivizedPacketResponse -QueryIncentivizedPacketsResponse defines the response type for the IncentivizedPacket rpc - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `incentivized_packet` | [IdentifiedPacketFees](#ibc.applications.fee.v1.IdentifiedPacketFees) | | the identified fees for the incentivized packet | - - - - - - - - -### QueryIncentivizedPacketsForChannelRequest -QueryIncentivizedPacketsForChannelRequest defines the request type for querying for all incentivized packets -for a specific channel - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | pagination defines an optional pagination for the request. | -| `port_id` | [string](#string) | | | -| `channel_id` | [string](#string) | | | -| `query_height` | [uint64](#uint64) | | Height to query at | - - - - - - - - -### QueryIncentivizedPacketsForChannelResponse -QueryIncentivizedPacketsResponse defines the response type for the incentivized packets RPC - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `incentivized_packets` | [IdentifiedPacketFees](#ibc.applications.fee.v1.IdentifiedPacketFees) | repeated | Map of all incentivized_packets | - - - - - - - - -### QueryIncentivizedPacketsRequest -QueryIncentivizedPacketsRequest defines the request type for the IncentivizedPackets rpc - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | pagination defines an optional pagination for the request. | -| `query_height` | [uint64](#uint64) | | block height at which to query | - - - - - - - - -### QueryIncentivizedPacketsResponse -QueryIncentivizedPacketsResponse defines the response type for the IncentivizedPackets rpc - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `incentivized_packets` | [IdentifiedPacketFees](#ibc.applications.fee.v1.IdentifiedPacketFees) | repeated | list of identified fees for incentivized packets | - - - - - - - - -### QueryPayeeRequest -QueryPayeeRequest defines the request type for the Payee rpc - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `channel_id` | [string](#string) | | unique channel identifier | -| `relayer` | [string](#string) | | the relayer address to which the distribution address is registered | - - - - - - - - -### QueryPayeeResponse -QueryPayeeResponse defines the response type for the Payee rpc - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `payee_address` | [string](#string) | | the payee address to which packet fees are paid out | - - - - - - - - -### QueryTotalAckFeesRequest -QueryTotalAckFeesRequest defines the request type for the TotalAckFees rpc - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `packet_id` | [ibc.core.channel.v1.PacketId](#ibc.core.channel.v1.PacketId) | | the packet identifier for the associated fees | - - - - - - - - -### QueryTotalAckFeesResponse -QueryTotalAckFeesResponse defines the response type for the TotalAckFees rpc - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `ack_fees` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | the total packet acknowledgement fees | - - - - - - - - -### QueryTotalRecvFeesRequest -QueryTotalRecvFeesRequest defines the request type for the TotalRecvFees rpc - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `packet_id` | [ibc.core.channel.v1.PacketId](#ibc.core.channel.v1.PacketId) | | the packet identifier for the associated fees | - - - - - - - - -### QueryTotalRecvFeesResponse -QueryTotalRecvFeesResponse defines the response type for the TotalRecvFees rpc - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `recv_fees` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | the total packet receive fees | - - - - - - - - -### QueryTotalTimeoutFeesRequest -QueryTotalTimeoutFeesRequest defines the request type for the TotalTimeoutFees rpc - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `packet_id` | [ibc.core.channel.v1.PacketId](#ibc.core.channel.v1.PacketId) | | the packet identifier for the associated fees | - - - - - - - - -### QueryTotalTimeoutFeesResponse -QueryTotalTimeoutFeesResponse defines the response type for the TotalTimeoutFees rpc - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `timeout_fees` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | the total packet timeout fees | - - - - - - - - - - - - - - -### Query -Query defines the ICS29 gRPC querier service. - -| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint | -| ----------- | ------------ | ------------- | ------------| ------- | -------- | -| `IncentivizedPackets` | [QueryIncentivizedPacketsRequest](#ibc.applications.fee.v1.QueryIncentivizedPacketsRequest) | [QueryIncentivizedPacketsResponse](#ibc.applications.fee.v1.QueryIncentivizedPacketsResponse) | IncentivizedPackets returns all incentivized packets and their associated fees | GET|/ibc/apps/fee/v1/incentivized_packets| -| `IncentivizedPacket` | [QueryIncentivizedPacketRequest](#ibc.applications.fee.v1.QueryIncentivizedPacketRequest) | [QueryIncentivizedPacketResponse](#ibc.applications.fee.v1.QueryIncentivizedPacketResponse) | IncentivizedPacket returns all packet fees for a packet given its identifier | GET|/ibc/apps/fee/v1/channels/{packet_id.channel_id}/ports/{packet_id.port_id}/sequences/{packet_id.sequence}/incentivized_packet| -| `IncentivizedPacketsForChannel` | [QueryIncentivizedPacketsForChannelRequest](#ibc.applications.fee.v1.QueryIncentivizedPacketsForChannelRequest) | [QueryIncentivizedPacketsForChannelResponse](#ibc.applications.fee.v1.QueryIncentivizedPacketsForChannelResponse) | Gets all incentivized packets for a specific channel | GET|/ibc/apps/fee/v1/channels/{channel_id}/ports/{port_id}/incentivized_packets| -| `TotalRecvFees` | [QueryTotalRecvFeesRequest](#ibc.applications.fee.v1.QueryTotalRecvFeesRequest) | [QueryTotalRecvFeesResponse](#ibc.applications.fee.v1.QueryTotalRecvFeesResponse) | TotalRecvFees returns the total receive fees for a packet given its identifier | GET|/ibc/apps/fee/v1/channels/{packet_id.channel_id}/ports/{packet_id.port_id}/sequences/{packet_id.sequence}/total_recv_fees| -| `TotalAckFees` | [QueryTotalAckFeesRequest](#ibc.applications.fee.v1.QueryTotalAckFeesRequest) | [QueryTotalAckFeesResponse](#ibc.applications.fee.v1.QueryTotalAckFeesResponse) | TotalAckFees returns the total acknowledgement fees for a packet given its identifier | GET|/ibc/apps/fee/v1/channels/{packet_id.channel_id}/ports/{packet_id.port_id}/sequences/{packet_id.sequence}/total_ack_fees| -| `TotalTimeoutFees` | [QueryTotalTimeoutFeesRequest](#ibc.applications.fee.v1.QueryTotalTimeoutFeesRequest) | [QueryTotalTimeoutFeesResponse](#ibc.applications.fee.v1.QueryTotalTimeoutFeesResponse) | TotalTimeoutFees returns the total timeout fees for a packet given its identifier | GET|/ibc/apps/fee/v1/channels/{packet_id.channel_id}/ports/{packet_id.port_id}/sequences/{packet_id.sequence}/total_timeout_fees| -| `Payee` | [QueryPayeeRequest](#ibc.applications.fee.v1.QueryPayeeRequest) | [QueryPayeeResponse](#ibc.applications.fee.v1.QueryPayeeResponse) | Payee returns the registered payee address for a specific channel given the relayer address | GET|/ibc/apps/fee/v1/channels/{channel_id}/relayers/{relayer}/payee| -| `CounterpartyPayee` | [QueryCounterpartyPayeeRequest](#ibc.applications.fee.v1.QueryCounterpartyPayeeRequest) | [QueryCounterpartyPayeeResponse](#ibc.applications.fee.v1.QueryCounterpartyPayeeResponse) | CounterpartyPayee returns the registered counterparty payee for forward relaying | GET|/ibc/apps/fee/v1/channels/{channel_id}/relayers/{relayer}/counterparty_payee| -| `FeeEnabledChannels` | [QueryFeeEnabledChannelsRequest](#ibc.applications.fee.v1.QueryFeeEnabledChannelsRequest) | [QueryFeeEnabledChannelsResponse](#ibc.applications.fee.v1.QueryFeeEnabledChannelsResponse) | FeeEnabledChannels returns a list of all fee enabled channels | GET|/ibc/apps/fee/v1/fee_enabled| -| `FeeEnabledChannel` | [QueryFeeEnabledChannelRequest](#ibc.applications.fee.v1.QueryFeeEnabledChannelRequest) | [QueryFeeEnabledChannelResponse](#ibc.applications.fee.v1.QueryFeeEnabledChannelResponse) | FeeEnabledChannel returns true if the provided port and channel identifiers belong to a fee enabled channel | GET|/ibc/apps/fee/v1/channels/{channel_id}/ports/{port_id}/fee_enabled| - - - - - - -

    Top

    - -## ibc/applications/fee/v1/tx.proto - - - - - -### MsgPayPacketFee -MsgPayPacketFee defines the request type for the PayPacketFee rpc -This Msg can be used to pay for a packet at the next sequence send & should be combined with the Msg that will be -paid for - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `fee` | [Fee](#ibc.applications.fee.v1.Fee) | | fee encapsulates the recv, ack and timeout fees associated with an IBC packet | -| `source_port_id` | [string](#string) | | the source port unique identifier | -| `source_channel_id` | [string](#string) | | the source channel unique identifer | -| `signer` | [string](#string) | | account address to refund fee if necessary | -| `relayers` | [string](#string) | repeated | optional list of relayers permitted to the receive packet fees | - - - - - - - - -### MsgPayPacketFeeAsync -MsgPayPacketFeeAsync defines the request type for the PayPacketFeeAsync rpc -This Msg can be used to pay for a packet at a specified sequence (instead of the next sequence send) - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `packet_id` | [ibc.core.channel.v1.PacketId](#ibc.core.channel.v1.PacketId) | | unique packet identifier comprised of the channel ID, port ID and sequence | -| `packet_fee` | [PacketFee](#ibc.applications.fee.v1.PacketFee) | | the packet fee associated with a particular IBC packet | - - - - - - - - -### MsgPayPacketFeeAsyncResponse -MsgPayPacketFeeAsyncResponse defines the response type for the PayPacketFeeAsync rpc - - - - - - - - -### MsgPayPacketFeeResponse -MsgPayPacketFeeResponse defines the response type for the PayPacketFee rpc - - - - - - - - -### MsgRegisterCounterpartyPayee -MsgRegisterCounterpartyPayee defines the request type for the RegisterCounterpartyPayee rpc - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `port_id` | [string](#string) | | unique port identifier | -| `channel_id` | [string](#string) | | unique channel identifier | -| `relayer` | [string](#string) | | the relayer address | -| `counterparty_payee` | [string](#string) | | the counterparty payee address | - - - - - - - - -### MsgRegisterCounterpartyPayeeResponse -MsgRegisterCounterpartyPayeeResponse defines the response type for the RegisterCounterpartyPayee rpc - - - - - - - - -### MsgRegisterPayee -MsgRegisterPayee defines the request type for the RegisterPayee rpc - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `port_id` | [string](#string) | | unique port identifier | -| `channel_id` | [string](#string) | | unique channel identifier | -| `relayer` | [string](#string) | | the relayer address | -| `payee` | [string](#string) | | the payee address | - - - - - - - - -### MsgRegisterPayeeResponse -MsgRegisterPayeeResponse defines the response type for the RegisterPayee rpc - - - - - - - - - - - - - - -### Msg -Msg defines the ICS29 Msg service. - -| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint | -| ----------- | ------------ | ------------- | ------------| ------- | -------- | -| `RegisterPayee` | [MsgRegisterPayee](#ibc.applications.fee.v1.MsgRegisterPayee) | [MsgRegisterPayeeResponse](#ibc.applications.fee.v1.MsgRegisterPayeeResponse) | RegisterPayee defines a rpc handler method for MsgRegisterPayee RegisterPayee is called by the relayer on each channelEnd and allows them to set an optional payee to which reverse and timeout relayer packet fees will be paid out. The payee should be registered on the source chain from which packets originate as this is where fee distribution takes place. This function may be called more than once by a relayer, in which case, the latest payee is always used. | | -| `RegisterCounterpartyPayee` | [MsgRegisterCounterpartyPayee](#ibc.applications.fee.v1.MsgRegisterCounterpartyPayee) | [MsgRegisterCounterpartyPayeeResponse](#ibc.applications.fee.v1.MsgRegisterCounterpartyPayeeResponse) | RegisterCounterpartyPayee defines a rpc handler method for MsgRegisterCounterpartyPayee RegisterCounterpartyPayee is called by the relayer on each channelEnd and allows them to specify the counterparty payee address before relaying. This ensures they will be properly compensated for forward relaying since the destination chain must include the registered counterparty payee address in the acknowledgement. This function may be called more than once by a relayer, in which case, the latest counterparty payee address is always used. | | -| `PayPacketFee` | [MsgPayPacketFee](#ibc.applications.fee.v1.MsgPayPacketFee) | [MsgPayPacketFeeResponse](#ibc.applications.fee.v1.MsgPayPacketFeeResponse) | PayPacketFee defines a rpc handler method for MsgPayPacketFee PayPacketFee is an open callback that may be called by any module/user that wishes to escrow funds in order to incentivize the relaying of the packet at the next sequence NOTE: This method is intended to be used within a multi msg transaction, where the subsequent msg that follows initiates the lifecycle of the incentivized packet | | -| `PayPacketFeeAsync` | [MsgPayPacketFeeAsync](#ibc.applications.fee.v1.MsgPayPacketFeeAsync) | [MsgPayPacketFeeAsyncResponse](#ibc.applications.fee.v1.MsgPayPacketFeeAsyncResponse) | PayPacketFeeAsync defines a rpc handler method for MsgPayPacketFeeAsync PayPacketFeeAsync is an open callback that may be called by any module/user that wishes to escrow funds in order to incentivize the relaying of a known packet (i.e. at a particular sequence) | | - - - - - - -

    Top

    - -## ibc/applications/interchain_accounts/controller/v1/controller.proto - - - - - -### Params -Params defines the set of on-chain interchain accounts parameters. -The following parameters may be used to disable the controller submodule. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `controller_enabled` | [bool](#bool) | | controller_enabled enables or disables the controller submodule. | - - - - - - - - - - - - - - - - -

    Top

    - -## ibc/applications/interchain_accounts/controller/v1/query.proto - - - - - -### QueryParamsRequest -QueryParamsRequest is the request type for the Query/Params RPC method. - - - - - - - - -### QueryParamsResponse -QueryParamsResponse is the response type for the Query/Params RPC method. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `params` | [Params](#ibc.applications.interchain_accounts.controller.v1.Params) | | params defines the parameters of the module. | - - - - - - - - - - - - - - -### Query -Query provides defines the gRPC querier service. - -| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint | -| ----------- | ------------ | ------------- | ------------| ------- | -------- | -| `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| - - - - - - -

    Top

    - -## ibc/applications/interchain_accounts/host/v1/host.proto - - - - - -### Params -Params defines the set of on-chain interchain accounts parameters. -The following parameters may be used to disable the host submodule. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `host_enabled` | [bool](#bool) | | host_enabled enables or disables the host submodule. | -| `allow_messages` | [string](#string) | repeated | allow_messages defines a list of sdk message typeURLs allowed to be executed on a host chain. | - - - - - - - - - - - - - - - - -

    Top

    - -## ibc/applications/interchain_accounts/host/v1/query.proto - - - - - -### QueryParamsRequest -QueryParamsRequest is the request type for the Query/Params RPC method. - - - - - - - - -### QueryParamsResponse -QueryParamsResponse is the response type for the Query/Params RPC method. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `params` | [Params](#ibc.applications.interchain_accounts.host.v1.Params) | | params defines the parameters of the module. | - - - - - - - - - - - - - - -### Query -Query provides defines the gRPC querier service. - -| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint | -| ----------- | ------------ | ------------- | ------------| ------- | -------- | -| `Params` | [QueryParamsRequest](#ibc.applications.interchain_accounts.host.v1.QueryParamsRequest) | [QueryParamsResponse](#ibc.applications.interchain_accounts.host.v1.QueryParamsResponse) | Params queries all parameters of the ICA host submodule. | GET|/ibc/apps/interchain_accounts/host/v1/params| - - - - - - -

    Top

    - -## ibc/applications/interchain_accounts/v1/account.proto - - - - - -### InterchainAccount -An InterchainAccount is defined as a BaseAccount & the address of the account owner on the controller chain - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `base_account` | [cosmos.auth.v1beta1.BaseAccount](#cosmos.auth.v1beta1.BaseAccount) | | | -| `account_owner` | [string](#string) | | | - - - - - - - - - - - - - - - - -

    Top

    - -## ibc/applications/interchain_accounts/v1/genesis.proto - - - - - -### ActiveChannel -ActiveChannel contains a connection ID, port ID and associated active channel ID - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `connection_id` | [string](#string) | | | -| `port_id` | [string](#string) | | | -| `channel_id` | [string](#string) | | | - - - - - - - - -### ControllerGenesisState -ControllerGenesisState defines the interchain accounts controller genesis state - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `active_channels` | [ActiveChannel](#ibc.applications.interchain_accounts.v1.ActiveChannel) | repeated | | -| `interchain_accounts` | [RegisteredInterchainAccount](#ibc.applications.interchain_accounts.v1.RegisteredInterchainAccount) | repeated | | -| `ports` | [string](#string) | repeated | | -| `params` | [ibc.applications.interchain_accounts.controller.v1.Params](#ibc.applications.interchain_accounts.controller.v1.Params) | | | - - - - - - - - -### GenesisState -GenesisState defines the interchain accounts genesis state - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `controller_genesis_state` | [ControllerGenesisState](#ibc.applications.interchain_accounts.v1.ControllerGenesisState) | | | -| `host_genesis_state` | [HostGenesisState](#ibc.applications.interchain_accounts.v1.HostGenesisState) | | | - - - - - - - - -### HostGenesisState -HostGenesisState defines the interchain accounts host genesis state - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `active_channels` | [ActiveChannel](#ibc.applications.interchain_accounts.v1.ActiveChannel) | repeated | | -| `interchain_accounts` | [RegisteredInterchainAccount](#ibc.applications.interchain_accounts.v1.RegisteredInterchainAccount) | repeated | | -| `port` | [string](#string) | | | -| `params` | [ibc.applications.interchain_accounts.host.v1.Params](#ibc.applications.interchain_accounts.host.v1.Params) | | | - - - - - - - - -### RegisteredInterchainAccount -RegisteredInterchainAccount contains a connection ID, port ID and associated interchain account address - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `connection_id` | [string](#string) | | | -| `port_id` | [string](#string) | | | -| `account_address` | [string](#string) | | | - - - - - - - - - - - - - - - - -

    Top

    - -## ibc/applications/interchain_accounts/v1/metadata.proto - - - - - -### Metadata -Metadata defines a set of protocol specific data encoded into the ICS27 channel version bytestring -See ICS004: https://github.com/cosmos/ibc/tree/master/spec/core/ics-004-channel-and-packet-semantics#Versioning - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `version` | [string](#string) | | version defines the ICS27 protocol version | -| `controller_connection_id` | [string](#string) | | controller_connection_id is the connection identifier associated with the controller chain | -| `host_connection_id` | [string](#string) | | host_connection_id is the connection identifier associated with the host chain | -| `address` | [string](#string) | | address defines the interchain account address to be fulfilled upon the OnChanOpenTry handshake step NOTE: the address field is empty on the OnChanOpenInit handshake step | -| `encoding` | [string](#string) | | encoding defines the supported codec format | -| `tx_type` | [string](#string) | | tx_type defines the type of transactions the interchain account can execute | - - - - - - - - - - - - - - - - -

    Top

    - -## ibc/applications/interchain_accounts/v1/packet.proto - - - - - -### CosmosTx -CosmosTx contains a list of sdk.Msg's. It should be used when sending transactions to an SDK host chain. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `messages` | [google.protobuf.Any](#google.protobuf.Any) | repeated | | - - - - - - - - -### InterchainAccountPacketData -InterchainAccountPacketData is comprised of a raw transaction, type of transaction and optional memo field. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `type` | [Type](#ibc.applications.interchain_accounts.v1.Type) | | | -| `data` | [bytes](#bytes) | | | -| `memo` | [string](#string) | | | - - - - - - - - - - -### Type -Type defines a classification of message issued from a controller chain to its associated interchain accounts -host - -| Name | Number | Description | -| ---- | ------ | ----------- | -| TYPE_UNSPECIFIED | 0 | Default zero value enumeration | -| TYPE_EXECUTE_TX | 1 | Execute a transaction on an interchain accounts host chain | - - - - - - - - - - - -

    Top

    - -## ibc/applications/transfer/v1/transfer.proto - - - - - -### DenomTrace -DenomTrace contains the base denomination for ICS20 fungible tokens and the -source tracing information path. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `path` | [string](#string) | | path defines the chain of port/channel identifiers used for tracing the source of the fungible token. | -| `base_denom` | [string](#string) | | base denomination of the relayed fungible token. | - - - - - - - - -### Params -Params defines the set of IBC transfer parameters. -NOTE: To prevent a single token from being transferred, set the -TransfersEnabled parameter to true and then set the bank module's SendEnabled -parameter for the denomination to false. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `send_enabled` | [bool](#bool) | | send_enabled enables or disables all cross-chain token transfers from this chain. | -| `receive_enabled` | [bool](#bool) | | receive_enabled enables or disables all cross-chain token transfers to this chain. | - - - - - - - - - - - - - - - - -

    Top

    - -## ibc/applications/transfer/v1/genesis.proto - - - - - -### GenesisState -GenesisState defines the ibc-transfer genesis state - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `port_id` | [string](#string) | | | -| `denom_traces` | [DenomTrace](#ibc.applications.transfer.v1.DenomTrace) | repeated | | -| `params` | [Params](#ibc.applications.transfer.v1.Params) | | | - - - - - - - - - - - - - - - - -

    Top

    - -## ibc/applications/transfer/v1/query.proto - - - - - -### QueryDenomHashRequest -QueryDenomHashRequest is the request type for the Query/DenomHash RPC -method - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `trace` | [string](#string) | | The denomination trace ([port_id]/[channel_id])+/[denom] | - - - - - - - - -### QueryDenomHashResponse -QueryDenomHashResponse is the response type for the Query/DenomHash RPC -method. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `hash` | [string](#string) | | hash (in hex format) of the denomination trace information. | - - - - - - - - -### QueryDenomTraceRequest -QueryDenomTraceRequest is the request type for the Query/DenomTrace RPC -method - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `hash` | [string](#string) | | hash (in hex format) or denom (full denom with ibc prefix) of the denomination trace information. | - - - - - - - - -### QueryDenomTraceResponse -QueryDenomTraceResponse is the response type for the Query/DenomTrace RPC -method. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `denom_trace` | [DenomTrace](#ibc.applications.transfer.v1.DenomTrace) | | denom_trace returns the requested denomination trace information. | - - - - - - - - -### QueryDenomTracesRequest -QueryConnectionsRequest is the request type for the Query/DenomTraces RPC -method - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | pagination defines an optional pagination for the request. | - - - - - - - - -### QueryDenomTracesResponse -QueryConnectionsResponse is the response type for the Query/DenomTraces RPC -method. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `denom_traces` | [DenomTrace](#ibc.applications.transfer.v1.DenomTrace) | repeated | denom_traces returns all denominations trace information. | -| `pagination` | [cosmos.base.query.v1beta1.PageResponse](#cosmos.base.query.v1beta1.PageResponse) | | pagination defines the pagination in the response. | - - - - - - - - -### QueryEscrowAddressRequest -QueryEscrowAddressRequest is the request type for the EscrowAddress RPC method. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `port_id` | [string](#string) | | unique port identifier | -| `channel_id` | [string](#string) | | unique channel identifier | - - - - - - - - -### QueryEscrowAddressResponse -QueryEscrowAddressResponse is the response type of the EscrowAddress RPC method. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `escrow_address` | [string](#string) | | the escrow account address | - - - - - - - - -### QueryParamsRequest -QueryParamsRequest is the request type for the Query/Params RPC method. - - - - - - - - -### QueryParamsResponse -QueryParamsResponse is the response type for the Query/Params RPC method. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `params` | [Params](#ibc.applications.transfer.v1.Params) | | params defines the parameters of the module. | - - - - - - - - - - - - - - -### Query -Query provides defines the gRPC querier service. - -| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint | -| ----------- | ------------ | ------------- | ------------| ------- | -------- | -| `DenomTrace` | [QueryDenomTraceRequest](#ibc.applications.transfer.v1.QueryDenomTraceRequest) | [QueryDenomTraceResponse](#ibc.applications.transfer.v1.QueryDenomTraceResponse) | DenomTrace queries a denomination trace information. | GET|/ibc/apps/transfer/v1/denom_traces/{hash}| -| `DenomTraces` | [QueryDenomTracesRequest](#ibc.applications.transfer.v1.QueryDenomTracesRequest) | [QueryDenomTracesResponse](#ibc.applications.transfer.v1.QueryDenomTracesResponse) | DenomTraces queries all denomination traces. | GET|/ibc/apps/transfer/v1/denom_traces| -| `Params` | [QueryParamsRequest](#ibc.applications.transfer.v1.QueryParamsRequest) | [QueryParamsResponse](#ibc.applications.transfer.v1.QueryParamsResponse) | Params queries all parameters of the ibc-transfer module. | GET|/ibc/apps/transfer/v1/params| -| `DenomHash` | [QueryDenomHashRequest](#ibc.applications.transfer.v1.QueryDenomHashRequest) | [QueryDenomHashResponse](#ibc.applications.transfer.v1.QueryDenomHashResponse) | DenomHash queries a denomination hash information. | GET|/ibc/apps/transfer/v1/denom_hashes/{trace}| -| `EscrowAddress` | [QueryEscrowAddressRequest](#ibc.applications.transfer.v1.QueryEscrowAddressRequest) | [QueryEscrowAddressResponse](#ibc.applications.transfer.v1.QueryEscrowAddressResponse) | EscrowAddress returns the escrow address for a particular port and channel id. | GET|/ibc/apps/transfer/v1/channels/{channel_id}/ports/{port_id}/escrow_address| - - - - - - -

    Top

    - -## ibc/applications/transfer/v1/tx.proto - - - - - -### MsgTransfer -MsgTransfer defines a msg to transfer fungible tokens (i.e Coins) between -ICS20 enabled chains. See ICS Spec here: -https://github.com/cosmos/ibc/tree/master/spec/app/ics-020-fungible-token-transfer#data-structures - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `source_port` | [string](#string) | | the port on which the packet will be sent | -| `source_channel` | [string](#string) | | the channel by which the packet will be sent | -| `token` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | | the tokens to be transferred | -| `sender` | [string](#string) | | the sender address | -| `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. | - - - - - - - - -### MsgTransferResponse -MsgTransferResponse defines the Msg/Transfer response type. - - - - - - - - - - - - - - -### Msg -Msg defines the ibc/transfer Msg service. - -| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint | -| ----------- | ------------ | ------------- | ------------| ------- | -------- | -| `Transfer` | [MsgTransfer](#ibc.applications.transfer.v1.MsgTransfer) | [MsgTransferResponse](#ibc.applications.transfer.v1.MsgTransferResponse) | Transfer defines a rpc handler method for MsgTransfer. | | - - - - - - -

    Top

    - -## ibc/applications/transfer/v2/packet.proto - - - - - -### FungibleTokenPacketData -FungibleTokenPacketData defines a struct for the packet payload -See FungibleTokenPacketData spec: -https://github.com/cosmos/ibc/tree/master/spec/app/ics-020-fungible-token-transfer#data-structures - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `denom` | [string](#string) | | the token denomination to be transferred | -| `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 | - - - - - - - - - - - - - - - - -

    Top

    - -## ibc/core/channel/v1/genesis.proto - - - - - -### GenesisState -GenesisState defines the ibc channel submodule's genesis state. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `channels` | [IdentifiedChannel](#ibc.core.channel.v1.IdentifiedChannel) | repeated | | -| `acknowledgements` | [PacketState](#ibc.core.channel.v1.PacketState) | repeated | | -| `commitments` | [PacketState](#ibc.core.channel.v1.PacketState) | repeated | | -| `receipts` | [PacketState](#ibc.core.channel.v1.PacketState) | repeated | | -| `send_sequences` | [PacketSequence](#ibc.core.channel.v1.PacketSequence) | repeated | | -| `recv_sequences` | [PacketSequence](#ibc.core.channel.v1.PacketSequence) | repeated | | -| `ack_sequences` | [PacketSequence](#ibc.core.channel.v1.PacketSequence) | repeated | | -| `next_channel_sequence` | [uint64](#uint64) | | the sequence for the next generated channel identifier | - - - - - - - - -### PacketSequence -PacketSequence defines the genesis type necessary to retrieve and store -next send and receive sequences. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `port_id` | [string](#string) | | | -| `channel_id` | [string](#string) | | | -| `sequence` | [uint64](#uint64) | | | - - - - - - - - - - - - - - - - -

    Top

    - -## ibc/core/channel/v1/query.proto - - - - - -### QueryChannelClientStateRequest -QueryChannelClientStateRequest is the request type for the Query/ClientState -RPC method - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `port_id` | [string](#string) | | port unique identifier | -| `channel_id` | [string](#string) | | channel unique identifier | - - - - - - - - -### QueryChannelClientStateResponse -QueryChannelClientStateResponse is the Response type for the -Query/QueryChannelClientState RPC method - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `identified_client_state` | [ibc.core.client.v1.IdentifiedClientState](#ibc.core.client.v1.IdentifiedClientState) | | client state associated with the channel | -| `proof` | [bytes](#bytes) | | merkle proof of existence | -| `proof_height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | height at which the proof was retrieved | - - - - - - - - -### QueryChannelConsensusStateRequest -QueryChannelConsensusStateRequest is the request type for the -Query/ConsensusState RPC method - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `port_id` | [string](#string) | | port unique identifier | -| `channel_id` | [string](#string) | | channel unique identifier | -| `revision_number` | [uint64](#uint64) | | revision number of the consensus state | -| `revision_height` | [uint64](#uint64) | | revision height of the consensus state | - - - - - - - - -### QueryChannelConsensusStateResponse -QueryChannelClientStateResponse is the Response type for the -Query/QueryChannelClientState RPC method - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `consensus_state` | [google.protobuf.Any](#google.protobuf.Any) | | consensus state associated with the channel | -| `client_id` | [string](#string) | | client ID associated with the consensus state | -| `proof` | [bytes](#bytes) | | merkle proof of existence | -| `proof_height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | height at which the proof was retrieved | - - - - - - - - -### QueryChannelRequest -QueryChannelRequest is the request type for the Query/Channel RPC method - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `port_id` | [string](#string) | | port unique identifier | -| `channel_id` | [string](#string) | | channel unique identifier | - - - - - - - - -### QueryChannelResponse -QueryChannelResponse is the response type for the Query/Channel RPC method. -Besides the Channel end, it includes a proof and the height from which the -proof was retrieved. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `channel` | [Channel](#ibc.core.channel.v1.Channel) | | channel associated with the request identifiers | -| `proof` | [bytes](#bytes) | | merkle proof of existence | -| `proof_height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | height at which the proof was retrieved | - - - - - - - - -### QueryChannelsRequest -QueryChannelsRequest is the request type for the Query/Channels RPC method - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | pagination request | - - - - - - - - -### QueryChannelsResponse -QueryChannelsResponse is the response type for the Query/Channels RPC method. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `channels` | [IdentifiedChannel](#ibc.core.channel.v1.IdentifiedChannel) | repeated | list of stored channels of the chain. | -| `pagination` | [cosmos.base.query.v1beta1.PageResponse](#cosmos.base.query.v1beta1.PageResponse) | | pagination response | -| `height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | query block height | - - - - - - - - -### QueryConnectionChannelsRequest -QueryConnectionChannelsRequest is the request type for the -Query/QueryConnectionChannels RPC method - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `connection` | [string](#string) | | connection unique identifier | -| `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | pagination request | - - - - - - - - -### QueryConnectionChannelsResponse -QueryConnectionChannelsResponse is the Response type for the -Query/QueryConnectionChannels RPC method - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `channels` | [IdentifiedChannel](#ibc.core.channel.v1.IdentifiedChannel) | repeated | list of channels associated with a connection. | -| `pagination` | [cosmos.base.query.v1beta1.PageResponse](#cosmos.base.query.v1beta1.PageResponse) | | pagination response | -| `height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | query block height | - - - - - - - - -### QueryNextSequenceReceiveRequest -QueryNextSequenceReceiveRequest is the request type for the -Query/QueryNextSequenceReceiveRequest RPC method - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `port_id` | [string](#string) | | port unique identifier | -| `channel_id` | [string](#string) | | channel unique identifier | - - - - - - - - -### QueryNextSequenceReceiveResponse -QuerySequenceResponse is the request type for the -Query/QueryNextSequenceReceiveResponse RPC method - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `next_sequence_receive` | [uint64](#uint64) | | next sequence receive number | -| `proof` | [bytes](#bytes) | | merkle proof of existence | -| `proof_height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | height at which the proof was retrieved | - - - - - - - - -### QueryPacketAcknowledgementRequest -QueryPacketAcknowledgementRequest is the request type for the -Query/PacketAcknowledgement RPC method - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `port_id` | [string](#string) | | port unique identifier | -| `channel_id` | [string](#string) | | channel unique identifier | -| `sequence` | [uint64](#uint64) | | packet sequence | - - - - - - - - -### QueryPacketAcknowledgementResponse -QueryPacketAcknowledgementResponse defines the client query response for a -packet which also includes a proof and the height from which the -proof was retrieved - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `acknowledgement` | [bytes](#bytes) | | packet associated with the request fields | -| `proof` | [bytes](#bytes) | | merkle proof of existence | -| `proof_height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | height at which the proof was retrieved | - - - - - - - - -### QueryPacketAcknowledgementsRequest -QueryPacketAcknowledgementsRequest is the request type for the -Query/QueryPacketCommitments RPC method - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `port_id` | [string](#string) | | port unique identifier | -| `channel_id` | [string](#string) | | channel unique identifier | -| `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | pagination request | -| `packet_commitment_sequences` | [uint64](#uint64) | repeated | list of packet sequences | - - - - - - - - -### QueryPacketAcknowledgementsResponse -QueryPacketAcknowledgemetsResponse is the request type for the -Query/QueryPacketAcknowledgements RPC method - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `acknowledgements` | [PacketState](#ibc.core.channel.v1.PacketState) | repeated | | -| `pagination` | [cosmos.base.query.v1beta1.PageResponse](#cosmos.base.query.v1beta1.PageResponse) | | pagination response | -| `height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | query block height | - - - - - - - - -### QueryPacketCommitmentRequest -QueryPacketCommitmentRequest is the request type for the -Query/PacketCommitment RPC method - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `port_id` | [string](#string) | | port unique identifier | -| `channel_id` | [string](#string) | | channel unique identifier | -| `sequence` | [uint64](#uint64) | | packet sequence | - - - - - - - - -### QueryPacketCommitmentResponse -QueryPacketCommitmentResponse defines the client query response for a packet -which also includes a proof and the height from which the proof was -retrieved - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `commitment` | [bytes](#bytes) | | packet associated with the request fields | -| `proof` | [bytes](#bytes) | | merkle proof of existence | -| `proof_height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | height at which the proof was retrieved | - - - - - - - - -### QueryPacketCommitmentsRequest -QueryPacketCommitmentsRequest is the request type for the -Query/QueryPacketCommitments RPC method - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `port_id` | [string](#string) | | port unique identifier | -| `channel_id` | [string](#string) | | channel unique identifier | -| `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | pagination request | - - - - - - - - -### QueryPacketCommitmentsResponse -QueryPacketCommitmentsResponse is the request type for the -Query/QueryPacketCommitments RPC method - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `commitments` | [PacketState](#ibc.core.channel.v1.PacketState) | repeated | | -| `pagination` | [cosmos.base.query.v1beta1.PageResponse](#cosmos.base.query.v1beta1.PageResponse) | | pagination response | -| `height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | query block height | - - - - - - - - -### QueryPacketReceiptRequest -QueryPacketReceiptRequest is the request type for the -Query/PacketReceipt RPC method - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `port_id` | [string](#string) | | port unique identifier | -| `channel_id` | [string](#string) | | channel unique identifier | -| `sequence` | [uint64](#uint64) | | packet sequence | - - - - - - - - -### QueryPacketReceiptResponse -QueryPacketReceiptResponse defines the client query response for a packet -receipt which also includes a proof, and the height from which the proof was -retrieved - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `received` | [bool](#bool) | | success flag for if receipt exists | -| `proof` | [bytes](#bytes) | | merkle proof of existence | -| `proof_height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | height at which the proof was retrieved | - - - - - - - - -### QueryUnreceivedAcksRequest -QueryUnreceivedAcks is the request type for the -Query/UnreceivedAcks RPC method - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `port_id` | [string](#string) | | port unique identifier | -| `channel_id` | [string](#string) | | channel unique identifier | -| `packet_ack_sequences` | [uint64](#uint64) | repeated | list of acknowledgement sequences | - - - - - - - - -### QueryUnreceivedAcksResponse -QueryUnreceivedAcksResponse is the response type for the -Query/UnreceivedAcks RPC method - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `sequences` | [uint64](#uint64) | repeated | list of unreceived acknowledgement sequences | -| `height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | query block height | - - - - - - - - -### QueryUnreceivedPacketsRequest -QueryUnreceivedPacketsRequest is the request type for the -Query/UnreceivedPackets RPC method - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `port_id` | [string](#string) | | port unique identifier | -| `channel_id` | [string](#string) | | channel unique identifier | -| `packet_commitment_sequences` | [uint64](#uint64) | repeated | list of packet sequences | - - - - - - - - -### QueryUnreceivedPacketsResponse -QueryUnreceivedPacketsResponse is the response type for the -Query/UnreceivedPacketCommitments RPC method - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `sequences` | [uint64](#uint64) | repeated | list of unreceived packet sequences | -| `height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | query block height | - - - - - - - - - - - - - - -### Query -Query provides defines the gRPC querier service - -| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint | -| ----------- | ------------ | ------------- | ------------| ------- | -------- | -| `Channel` | [QueryChannelRequest](#ibc.core.channel.v1.QueryChannelRequest) | [QueryChannelResponse](#ibc.core.channel.v1.QueryChannelResponse) | Channel queries an IBC Channel. | GET|/ibc/core/channel/v1/channels/{channel_id}/ports/{port_id}| -| `Channels` | [QueryChannelsRequest](#ibc.core.channel.v1.QueryChannelsRequest) | [QueryChannelsResponse](#ibc.core.channel.v1.QueryChannelsResponse) | Channels queries all the IBC channels of a chain. | GET|/ibc/core/channel/v1/channels| -| `ConnectionChannels` | [QueryConnectionChannelsRequest](#ibc.core.channel.v1.QueryConnectionChannelsRequest) | [QueryConnectionChannelsResponse](#ibc.core.channel.v1.QueryConnectionChannelsResponse) | ConnectionChannels queries all the channels associated with a connection end. | GET|/ibc/core/channel/v1/connections/{connection}/channels| -| `ChannelClientState` | [QueryChannelClientStateRequest](#ibc.core.channel.v1.QueryChannelClientStateRequest) | [QueryChannelClientStateResponse](#ibc.core.channel.v1.QueryChannelClientStateResponse) | ChannelClientState queries for the client state for the channel associated with the provided channel identifiers. | GET|/ibc/core/channel/v1/channels/{channel_id}/ports/{port_id}/client_state| -| `ChannelConsensusState` | [QueryChannelConsensusStateRequest](#ibc.core.channel.v1.QueryChannelConsensusStateRequest) | [QueryChannelConsensusStateResponse](#ibc.core.channel.v1.QueryChannelConsensusStateResponse) | ChannelConsensusState queries for the consensus state for the channel associated with the provided channel identifiers. | GET|/ibc/core/channel/v1/channels/{channel_id}/ports/{port_id}/consensus_state/revision/{revision_number}/height/{revision_height}| -| `PacketCommitment` | [QueryPacketCommitmentRequest](#ibc.core.channel.v1.QueryPacketCommitmentRequest) | [QueryPacketCommitmentResponse](#ibc.core.channel.v1.QueryPacketCommitmentResponse) | PacketCommitment queries a stored packet commitment hash. | GET|/ibc/core/channel/v1/channels/{channel_id}/ports/{port_id}/packet_commitments/{sequence}| -| `PacketCommitments` | [QueryPacketCommitmentsRequest](#ibc.core.channel.v1.QueryPacketCommitmentsRequest) | [QueryPacketCommitmentsResponse](#ibc.core.channel.v1.QueryPacketCommitmentsResponse) | PacketCommitments returns all the packet commitments hashes associated with a channel. | GET|/ibc/core/channel/v1/channels/{channel_id}/ports/{port_id}/packet_commitments| -| `PacketReceipt` | [QueryPacketReceiptRequest](#ibc.core.channel.v1.QueryPacketReceiptRequest) | [QueryPacketReceiptResponse](#ibc.core.channel.v1.QueryPacketReceiptResponse) | PacketReceipt queries if a given packet sequence has been received on the queried chain | GET|/ibc/core/channel/v1/channels/{channel_id}/ports/{port_id}/packet_receipts/{sequence}| -| `PacketAcknowledgement` | [QueryPacketAcknowledgementRequest](#ibc.core.channel.v1.QueryPacketAcknowledgementRequest) | [QueryPacketAcknowledgementResponse](#ibc.core.channel.v1.QueryPacketAcknowledgementResponse) | PacketAcknowledgement queries a stored packet acknowledgement hash. | GET|/ibc/core/channel/v1/channels/{channel_id}/ports/{port_id}/packet_acks/{sequence}| -| `PacketAcknowledgements` | [QueryPacketAcknowledgementsRequest](#ibc.core.channel.v1.QueryPacketAcknowledgementsRequest) | [QueryPacketAcknowledgementsResponse](#ibc.core.channel.v1.QueryPacketAcknowledgementsResponse) | PacketAcknowledgements returns all the packet acknowledgements associated with a channel. | GET|/ibc/core/channel/v1/channels/{channel_id}/ports/{port_id}/packet_acknowledgements| -| `UnreceivedPackets` | [QueryUnreceivedPacketsRequest](#ibc.core.channel.v1.QueryUnreceivedPacketsRequest) | [QueryUnreceivedPacketsResponse](#ibc.core.channel.v1.QueryUnreceivedPacketsResponse) | UnreceivedPackets returns all the unreceived IBC packets associated with a channel and sequences. | GET|/ibc/core/channel/v1/channels/{channel_id}/ports/{port_id}/packet_commitments/{packet_commitment_sequences}/unreceived_packets| -| `UnreceivedAcks` | [QueryUnreceivedAcksRequest](#ibc.core.channel.v1.QueryUnreceivedAcksRequest) | [QueryUnreceivedAcksResponse](#ibc.core.channel.v1.QueryUnreceivedAcksResponse) | UnreceivedAcks returns all the unreceived IBC acknowledgements associated with a channel and sequences. | GET|/ibc/core/channel/v1/channels/{channel_id}/ports/{port_id}/packet_commitments/{packet_ack_sequences}/unreceived_acks| -| `NextSequenceReceive` | [QueryNextSequenceReceiveRequest](#ibc.core.channel.v1.QueryNextSequenceReceiveRequest) | [QueryNextSequenceReceiveResponse](#ibc.core.channel.v1.QueryNextSequenceReceiveResponse) | NextSequenceReceive returns the next receive sequence for a given channel. | GET|/ibc/core/channel/v1/channels/{channel_id}/ports/{port_id}/next_sequence| - - - - - - -

    Top

    - -## ibc/core/channel/v1/upgrade.proto - - - - - -### ErrorReceipt -ErrorReceipt defines a type which encapsulates the upgrade sequence and error associated with the -upgrade handshake failure. When a channel upgrade handshake is aborted both chains are expected to increment to the -next sequence. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `sequence` | [uint64](#uint64) | | the channel upgrade sequence | -| `error` | [string](#string) | | the error message detailing the cause of failure | - - - - - - - - -### UpgradeTimeout -UpgradeTimeout defines a type which encapsulates the upgrade timeout values at which the counterparty -must no longer proceed with the upgrade handshake. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `timeout_height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | block height after which the upgrade times out | -| `timeout_timestamp` | [uint64](#uint64) | | block timestamp (in nanoseconds) after which the upgrade times out | - - - - - - - - - - - - - - - - -

    Top

    - -## ibc/core/channel/v1/tx.proto - - - - - -### MsgAcknowledgement -MsgAcknowledgement receives incoming IBC acknowledgement - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `packet` | [Packet](#ibc.core.channel.v1.Packet) | | | -| `acknowledgement` | [bytes](#bytes) | | | -| `proof_acked` | [bytes](#bytes) | | | -| `proof_height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | | -| `signer` | [string](#string) | | | - - - - - - - - -### MsgAcknowledgementResponse -MsgAcknowledgementResponse defines the Msg/Acknowledgement response type. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `result` | [ResponseResultType](#ibc.core.channel.v1.ResponseResultType) | | | - - - - - - - - -### MsgChannelCloseConfirm -MsgChannelCloseConfirm defines a msg sent by a Relayer to Chain B -to acknowledge the change of channel state to CLOSED on Chain A. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `port_id` | [string](#string) | | | -| `channel_id` | [string](#string) | | | -| `proof_init` | [bytes](#bytes) | | | -| `proof_height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | | -| `signer` | [string](#string) | | | - - - - - - - - -### MsgChannelCloseConfirmResponse -MsgChannelCloseConfirmResponse defines the Msg/ChannelCloseConfirm response -type. - - - - - - - - -### MsgChannelCloseInit -MsgChannelCloseInit defines a msg sent by a Relayer to Chain A -to close a channel with Chain B. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `port_id` | [string](#string) | | | -| `channel_id` | [string](#string) | | | -| `signer` | [string](#string) | | | - - - - - - - - -### MsgChannelCloseInitResponse -MsgChannelCloseInitResponse defines the Msg/ChannelCloseInit response type. - - - - - - - - -### MsgChannelOpenAck -MsgChannelOpenAck defines a msg sent by a Relayer to Chain A to acknowledge -the change of channel state to TRYOPEN on Chain B. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `port_id` | [string](#string) | | | -| `channel_id` | [string](#string) | | | -| `counterparty_channel_id` | [string](#string) | | | -| `counterparty_version` | [string](#string) | | | -| `proof_try` | [bytes](#bytes) | | | -| `proof_height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | | -| `signer` | [string](#string) | | | - - - - - - - - -### MsgChannelOpenAckResponse -MsgChannelOpenAckResponse defines the Msg/ChannelOpenAck response type. - - - - - - - - -### MsgChannelOpenConfirm -MsgChannelOpenConfirm defines a msg sent by a Relayer to Chain B to -acknowledge the change of channel state to OPEN on Chain A. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `port_id` | [string](#string) | | | -| `channel_id` | [string](#string) | | | -| `proof_ack` | [bytes](#bytes) | | | -| `proof_height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | | -| `signer` | [string](#string) | | | - - - - - - - - -### MsgChannelOpenConfirmResponse -MsgChannelOpenConfirmResponse defines the Msg/ChannelOpenConfirm response -type. - - - - - - - - -### MsgChannelOpenInit -MsgChannelOpenInit defines an sdk.Msg to initialize a channel handshake. It -is called by a relayer on Chain A. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `port_id` | [string](#string) | | | -| `channel` | [Channel](#ibc.core.channel.v1.Channel) | | | -| `signer` | [string](#string) | | | - - - - - - - - -### MsgChannelOpenInitResponse -MsgChannelOpenInitResponse defines the Msg/ChannelOpenInit response type. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `channel_id` | [string](#string) | | | -| `version` | [string](#string) | | | - - - - - - - - -### MsgChannelOpenTry -MsgChannelOpenInit defines a msg sent by a Relayer to try to open a channel -on Chain B. The version field within the Channel field has been deprecated. Its -value will be ignored by core IBC. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `port_id` | [string](#string) | | | -| `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) | | | -| `proof_height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | | -| `signer` | [string](#string) | | | - - - - - - - - -### MsgChannelOpenTryResponse -MsgChannelOpenTryResponse defines the Msg/ChannelOpenTry response type. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `version` | [string](#string) | | | - - - - - - - - -### MsgChannelUpgradeAck -MsgChannelUpgradeAck defines the request type for the ChannelUpgradeAck rpc - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `port_id` | [string](#string) | | | -| `channel_id` | [string](#string) | | | -| `counterparty_channel` | [Channel](#ibc.core.channel.v1.Channel) | | | -| `proof_channel` | [bytes](#bytes) | | | -| `proof_upgrade_sequence` | [bytes](#bytes) | | | -| `proof_height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | | -| `signer` | [string](#string) | | | - - - - - - - - -### MsgChannelUpgradeAckResponse -MsgChannelUpgradeAckResponse defines MsgChannelUpgradeAck response type - - - - - - - - -### MsgChannelUpgradeCancel -MsgChannelUpgradeCancel defines the request type for the ChannelUpgradeCancel rpc - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `port_id` | [string](#string) | | | -| `channel_id` | [string](#string) | | | -| `error_receipt` | [ErrorReceipt](#ibc.core.channel.v1.ErrorReceipt) | | | -| `proof_error_receipt` | [bytes](#bytes) | | | -| `proof_height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | | -| `signer` | [string](#string) | | | - - - - - - - - -### MsgChannelUpgradeCancelResponse -MsgChannelUpgradeCancelResponse defines the MsgChannelUpgradeCancel response type - - - - - - - - -### MsgChannelUpgradeConfirm -MsgChannelUpgradeConfirm defines the request type for the ChannelUpgradeConfirm rpc - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `port_id` | [string](#string) | | | -| `channel_id` | [string](#string) | | | -| `counterparty_channel` | [Channel](#ibc.core.channel.v1.Channel) | | | -| `proof_channel` | [bytes](#bytes) | | | -| `proof_upgrade_error` | [bytes](#bytes) | | | -| `proof_upgrade_sequence` | [bytes](#bytes) | | | -| `proof_height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | | -| `signer` | [string](#string) | | | - - - - - - - - -### MsgChannelUpgradeConfirmResponse -MsgChannelUpgradeConfirmResponse defines the MsgChannelUpgradeConfirm response type - - - - - - - - -### MsgChannelUpgradeInit -MsgChanelUpgradeInit defines the request type for the ChannelUpgradeInit rpc - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `port_id` | [string](#string) | | | -| `channel_id` | [string](#string) | | | -| `proposed_upgrade_channel` | [Channel](#ibc.core.channel.v1.Channel) | | | -| `timeout_height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | | -| `timeout_timestamp` | [uint64](#uint64) | | | -| `signer` | [string](#string) | | | - - - - - - - - -### MsgChannelUpgradeInitResponse -MsgChannelUpgradeInitResponse defines the MsgChannelUpgradeInit response type - - - - - - - - -### MsgChannelUpgradeTimeout -MsgChannelUpgradeTimeout defines the request type for the ChannelUpgradeTimeout rpc - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `port_id` | [string](#string) | | | -| `channel_id` | [string](#string) | | | -| `counterparty_channel` | [Channel](#ibc.core.channel.v1.Channel) | | | -| `previous_error_receipt` | [ErrorReceipt](#ibc.core.channel.v1.ErrorReceipt) | | | -| `proof_channel` | [bytes](#bytes) | | | -| `proof_error_receipt` | [bytes](#bytes) | | | -| `proof_height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | | -| `signer` | [string](#string) | | | - - - - - - - - -### MsgChannelUpgradeTimeoutResponse -MsgChannelUpgradeTimeoutRepsonse defines the MsgChannelUpgradeTimeout response type - - - - - - - - -### MsgChannelUpgradeTry -MsgChannelUpgradeTry defines the request type for the ChannelUpgradeTry rpc - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `port_id` | [string](#string) | | | -| `channel_id` | [string](#string) | | | -| `counterparty_channel` | [Channel](#ibc.core.channel.v1.Channel) | | | -| `counterparty_sequence` | [uint64](#uint64) | | | -| `proposed_upgrade_channel` | [Channel](#ibc.core.channel.v1.Channel) | | | -| `timeout_height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | | -| `timeout_timestamp` | [uint64](#uint64) | | | -| `proof_channel` | [bytes](#bytes) | | | -| `proof_upgrade_timeout` | [bytes](#bytes) | | | -| `proof_upgrade_sequence` | [bytes](#bytes) | | | -| `proof_height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | | -| `signer` | [string](#string) | | | - - - - - - - - -### MsgChannelUpgradeTryResponse -MsgChannelUpgradeTryResponse defines the MsgChannelUpgradeTry response type - - - - - - - - -### MsgRecvPacket -MsgRecvPacket receives incoming IBC packet - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `packet` | [Packet](#ibc.core.channel.v1.Packet) | | | -| `proof_commitment` | [bytes](#bytes) | | | -| `proof_height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | | -| `signer` | [string](#string) | | | - - - - - - - - -### MsgRecvPacketResponse -MsgRecvPacketResponse defines the Msg/RecvPacket response type. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `result` | [ResponseResultType](#ibc.core.channel.v1.ResponseResultType) | | | - - - - - - - - -### MsgTimeout -MsgTimeout receives timed-out packet - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `packet` | [Packet](#ibc.core.channel.v1.Packet) | | | -| `proof_unreceived` | [bytes](#bytes) | | | -| `proof_height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | | -| `next_sequence_recv` | [uint64](#uint64) | | | -| `signer` | [string](#string) | | | - - - - - - - - -### MsgTimeoutOnClose -MsgTimeoutOnClose timed-out packet upon counterparty channel closure. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `packet` | [Packet](#ibc.core.channel.v1.Packet) | | | -| `proof_unreceived` | [bytes](#bytes) | | | -| `proof_close` | [bytes](#bytes) | | | -| `proof_height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | | -| `next_sequence_recv` | [uint64](#uint64) | | | -| `signer` | [string](#string) | | | - - - - - - - - -### MsgTimeoutOnCloseResponse -MsgTimeoutOnCloseResponse defines the Msg/TimeoutOnClose response type. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `result` | [ResponseResultType](#ibc.core.channel.v1.ResponseResultType) | | | - - - - - - - - -### MsgTimeoutResponse -MsgTimeoutResponse defines the Msg/Timeout response type. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `result` | [ResponseResultType](#ibc.core.channel.v1.ResponseResultType) | | | - - - - - - - - - - -### ResponseResultType -ResponseResultType defines the possible outcomes of the execution of a message - -| Name | Number | Description | -| ---- | ------ | ----------- | -| RESPONSE_RESULT_TYPE_UNSPECIFIED | 0 | Default zero value enumeration | -| RESPONSE_RESULT_TYPE_NOOP | 1 | The message did not call the IBC application callbacks (because, for example, the packet had already been relayed) | -| RESPONSE_RESULT_TYPE_SUCCESS | 2 | The message was executed successfully | - - - - - - - - - -### Msg -Msg defines the ibc/channel Msg service. - -| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint | -| ----------- | ------------ | ------------- | ------------| ------- | -------- | -| `ChannelOpenInit` | [MsgChannelOpenInit](#ibc.core.channel.v1.MsgChannelOpenInit) | [MsgChannelOpenInitResponse](#ibc.core.channel.v1.MsgChannelOpenInitResponse) | ChannelOpenInit defines a rpc handler method for MsgChannelOpenInit. | | -| `ChannelOpenTry` | [MsgChannelOpenTry](#ibc.core.channel.v1.MsgChannelOpenTry) | [MsgChannelOpenTryResponse](#ibc.core.channel.v1.MsgChannelOpenTryResponse) | ChannelOpenTry defines a rpc handler method for MsgChannelOpenTry. | | -| `ChannelOpenAck` | [MsgChannelOpenAck](#ibc.core.channel.v1.MsgChannelOpenAck) | [MsgChannelOpenAckResponse](#ibc.core.channel.v1.MsgChannelOpenAckResponse) | ChannelOpenAck defines a rpc handler method for MsgChannelOpenAck. | | -| `ChannelOpenConfirm` | [MsgChannelOpenConfirm](#ibc.core.channel.v1.MsgChannelOpenConfirm) | [MsgChannelOpenConfirmResponse](#ibc.core.channel.v1.MsgChannelOpenConfirmResponse) | ChannelOpenConfirm defines a rpc handler method for MsgChannelOpenConfirm. | | -| `ChannelCloseInit` | [MsgChannelCloseInit](#ibc.core.channel.v1.MsgChannelCloseInit) | [MsgChannelCloseInitResponse](#ibc.core.channel.v1.MsgChannelCloseInitResponse) | ChannelCloseInit defines a rpc handler method for MsgChannelCloseInit. | | -| `ChannelCloseConfirm` | [MsgChannelCloseConfirm](#ibc.core.channel.v1.MsgChannelCloseConfirm) | [MsgChannelCloseConfirmResponse](#ibc.core.channel.v1.MsgChannelCloseConfirmResponse) | ChannelCloseConfirm defines a rpc handler method for MsgChannelCloseConfirm. | | -| `RecvPacket` | [MsgRecvPacket](#ibc.core.channel.v1.MsgRecvPacket) | [MsgRecvPacketResponse](#ibc.core.channel.v1.MsgRecvPacketResponse) | RecvPacket defines a rpc handler method for MsgRecvPacket. | | -| `Timeout` | [MsgTimeout](#ibc.core.channel.v1.MsgTimeout) | [MsgTimeoutResponse](#ibc.core.channel.v1.MsgTimeoutResponse) | Timeout defines a rpc handler method for MsgTimeout. | | -| `TimeoutOnClose` | [MsgTimeoutOnClose](#ibc.core.channel.v1.MsgTimeoutOnClose) | [MsgTimeoutOnCloseResponse](#ibc.core.channel.v1.MsgTimeoutOnCloseResponse) | TimeoutOnClose defines a rpc handler method for MsgTimeoutOnClose. | | -| `Acknowledgement` | [MsgAcknowledgement](#ibc.core.channel.v1.MsgAcknowledgement) | [MsgAcknowledgementResponse](#ibc.core.channel.v1.MsgAcknowledgementResponse) | Acknowledgement defines a rpc handler method for MsgAcknowledgement. | | -| `ChannelUpgradeInit` | [MsgChannelUpgradeInit](#ibc.core.channel.v1.MsgChannelUpgradeInit) | [MsgChannelUpgradeInitResponse](#ibc.core.channel.v1.MsgChannelUpgradeInitResponse) | ChannelUpgradeInit defines a rpc handler method for MsgChannelUpgradeInit. | | -| `ChannelUpgradeTry` | [MsgChannelUpgradeTry](#ibc.core.channel.v1.MsgChannelUpgradeTry) | [MsgChannelUpgradeTryResponse](#ibc.core.channel.v1.MsgChannelUpgradeTryResponse) | ChannelUpgradeTry defines a rpc handler method for MsgChannelUpgradeTry. | | -| `ChannelUpgradeAck` | [MsgChannelUpgradeAck](#ibc.core.channel.v1.MsgChannelUpgradeAck) | [MsgChannelUpgradeAckResponse](#ibc.core.channel.v1.MsgChannelUpgradeAckResponse) | ChannelUpgradeAck defines a rpc handler method for MsgChannelUpgradeAck. | | -| `ChannelUpgradeConfirm` | [MsgChannelUpgradeConfirm](#ibc.core.channel.v1.MsgChannelUpgradeConfirm) | [MsgChannelUpgradeConfirmResponse](#ibc.core.channel.v1.MsgChannelUpgradeConfirmResponse) | ChannelUpgradeConfirm defines a rpc handler method for MsgChannelUpgradeConfirm. | | -| `ChannelUpgradeTimeout` | [MsgChannelUpgradeTimeout](#ibc.core.channel.v1.MsgChannelUpgradeTimeout) | [MsgChannelUpgradeTimeoutResponse](#ibc.core.channel.v1.MsgChannelUpgradeTimeoutResponse) | ChannelUpgradeTimeout defines a rpc handler method for MsgChannelUpgradeTimeout. | | -| `ChannelUpgradeCancel` | [MsgChannelUpgradeCancel](#ibc.core.channel.v1.MsgChannelUpgradeCancel) | [MsgChannelUpgradeCancelResponse](#ibc.core.channel.v1.MsgChannelUpgradeCancelResponse) | ChannelUpgradeCancel defines a rpc handler method for MsgChannelUpgradeCancel. | | - - - - - - -

    Top

    - -## ibc/core/client/v1/genesis.proto - - - - - -### GenesisMetadata -GenesisMetadata defines the genesis type for metadata that clients may return -with ExportMetadata - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `key` | [bytes](#bytes) | | store key of metadata without clientID-prefix | -| `value` | [bytes](#bytes) | | metadata value | - - - - - - - - -### GenesisState -GenesisState defines the ibc client submodule's genesis state. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `clients` | [IdentifiedClientState](#ibc.core.client.v1.IdentifiedClientState) | repeated | client states with their corresponding identifiers | -| `clients_consensus` | [ClientConsensusStates](#ibc.core.client.v1.ClientConsensusStates) | repeated | consensus states from each client | -| `clients_metadata` | [IdentifiedGenesisMetadata](#ibc.core.client.v1.IdentifiedGenesisMetadata) | repeated | metadata from each client | -| `params` | [Params](#ibc.core.client.v1.Params) | | | -| `create_localhost` | [bool](#bool) | | create localhost on initialization | -| `next_client_sequence` | [uint64](#uint64) | | the sequence for the next generated client identifier | - - - - - - - - -### IdentifiedGenesisMetadata -IdentifiedGenesisMetadata has the client metadata with the corresponding -client id. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `client_id` | [string](#string) | | | -| `client_metadata` | [GenesisMetadata](#ibc.core.client.v1.GenesisMetadata) | repeated | | - - - - - - - - - - - - - - - - -

    Top

    - -## ibc/core/client/v1/query.proto - - - - - -### QueryClientParamsRequest -QueryClientParamsRequest is the request type for the Query/ClientParams RPC -method. - - - - - - - - -### QueryClientParamsResponse -QueryClientParamsResponse is the response type for the Query/ClientParams RPC -method. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `params` | [Params](#ibc.core.client.v1.Params) | | params defines the parameters of the module. | - - - - - - - - -### QueryClientStateRequest -QueryClientStateRequest is the request type for the Query/ClientState RPC -method - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `client_id` | [string](#string) | | client state unique identifier | - - - - - - - - -### QueryClientStateResponse -QueryClientStateResponse is the response type for the Query/ClientState RPC -method. Besides the client state, it includes a proof and the height from -which the proof was retrieved. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `client_state` | [google.protobuf.Any](#google.protobuf.Any) | | client state associated with the request identifier | -| `proof` | [bytes](#bytes) | | merkle proof of existence | -| `proof_height` | [Height](#ibc.core.client.v1.Height) | | height at which the proof was retrieved | - - - - - - - - -### QueryClientStatesRequest -QueryClientStatesRequest is the request type for the Query/ClientStates RPC -method - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | pagination request | - - - - - - - - -### QueryClientStatesResponse -QueryClientStatesResponse is the response type for the Query/ClientStates RPC -method. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `client_states` | [IdentifiedClientState](#ibc.core.client.v1.IdentifiedClientState) | repeated | list of stored ClientStates of the chain. | -| `pagination` | [cosmos.base.query.v1beta1.PageResponse](#cosmos.base.query.v1beta1.PageResponse) | | pagination response | - - - - - - - - -### QueryClientStatusRequest -QueryClientStatusRequest is the request type for the Query/ClientStatus RPC -method - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `client_id` | [string](#string) | | client unique identifier | - - - - - - - - -### QueryClientStatusResponse -QueryClientStatusResponse is the response type for the Query/ClientStatus RPC -method. It returns the current status of the IBC client. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `status` | [string](#string) | | | - - - - - - - - -### QueryConsensusStateHeightsRequest -QueryConsensusStateHeightsRequest is the request type for Query/ConsensusStateHeights -RPC method. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `client_id` | [string](#string) | | client identifier | -| `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | pagination request | - - - - - - - - -### QueryConsensusStateHeightsResponse -QueryConsensusStateHeightsResponse is the response type for the -Query/ConsensusStateHeights RPC method - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `consensus_state_heights` | [Height](#ibc.core.client.v1.Height) | repeated | consensus state heights | -| `pagination` | [cosmos.base.query.v1beta1.PageResponse](#cosmos.base.query.v1beta1.PageResponse) | | pagination response | - - - - - - - - -### QueryConsensusStateRequest -QueryConsensusStateRequest is the request type for the Query/ConsensusState -RPC method. Besides the consensus state, it includes a proof and the height -from which the proof was retrieved. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `client_id` | [string](#string) | | client identifier | -| `revision_number` | [uint64](#uint64) | | consensus state revision number | -| `revision_height` | [uint64](#uint64) | | consensus state revision height | -| `latest_height` | [bool](#bool) | | latest_height overrrides the height field and queries the latest stored ConsensusState | - - - - - - - - -### QueryConsensusStateResponse -QueryConsensusStateResponse is the response type for the Query/ConsensusState -RPC method - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `consensus_state` | [google.protobuf.Any](#google.protobuf.Any) | | consensus state associated with the client identifier at the given height | -| `proof` | [bytes](#bytes) | | merkle proof of existence | -| `proof_height` | [Height](#ibc.core.client.v1.Height) | | height at which the proof was retrieved | - - - - - - - - -### QueryConsensusStatesRequest -QueryConsensusStatesRequest is the request type for the Query/ConsensusStates -RPC method. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `client_id` | [string](#string) | | client identifier | -| `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | pagination request | - - - - - - - - -### QueryConsensusStatesResponse -QueryConsensusStatesResponse is the response type for the -Query/ConsensusStates RPC method - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `consensus_states` | [ConsensusStateWithHeight](#ibc.core.client.v1.ConsensusStateWithHeight) | repeated | consensus states associated with the identifier | -| `pagination` | [cosmos.base.query.v1beta1.PageResponse](#cosmos.base.query.v1beta1.PageResponse) | | pagination response | - - - - - - - - -### QueryUpgradedClientStateRequest -QueryUpgradedClientStateRequest is the request type for the -Query/UpgradedClientState RPC method - - - - - - - - -### QueryUpgradedClientStateResponse -QueryUpgradedClientStateResponse is the response type for the -Query/UpgradedClientState RPC method. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `upgraded_client_state` | [google.protobuf.Any](#google.protobuf.Any) | | client state associated with the request identifier | - - - - - - - - -### QueryUpgradedConsensusStateRequest -QueryUpgradedConsensusStateRequest is the request type for the -Query/UpgradedConsensusState RPC method - - - - - - - - -### QueryUpgradedConsensusStateResponse -QueryUpgradedConsensusStateResponse is the response type for the -Query/UpgradedConsensusState RPC method. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `upgraded_consensus_state` | [google.protobuf.Any](#google.protobuf.Any) | | Consensus state associated with the request identifier | - - - - - - - - - - - - - - -### Query -Query provides defines the gRPC querier service - -| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint | -| ----------- | ------------ | ------------- | ------------| ------- | -------- | -| `ClientState` | [QueryClientStateRequest](#ibc.core.client.v1.QueryClientStateRequest) | [QueryClientStateResponse](#ibc.core.client.v1.QueryClientStateResponse) | ClientState queries an IBC light client. | GET|/ibc/core/client/v1/client_states/{client_id}| -| `ClientStates` | [QueryClientStatesRequest](#ibc.core.client.v1.QueryClientStatesRequest) | [QueryClientStatesResponse](#ibc.core.client.v1.QueryClientStatesResponse) | ClientStates queries all the IBC light clients of a chain. | GET|/ibc/core/client/v1/client_states| -| `ConsensusState` | [QueryConsensusStateRequest](#ibc.core.client.v1.QueryConsensusStateRequest) | [QueryConsensusStateResponse](#ibc.core.client.v1.QueryConsensusStateResponse) | ConsensusState queries a consensus state associated with a client state at a given height. | GET|/ibc/core/client/v1/consensus_states/{client_id}/revision/{revision_number}/height/{revision_height}| -| `ConsensusStates` | [QueryConsensusStatesRequest](#ibc.core.client.v1.QueryConsensusStatesRequest) | [QueryConsensusStatesResponse](#ibc.core.client.v1.QueryConsensusStatesResponse) | ConsensusStates queries all the consensus state associated with a given client. | GET|/ibc/core/client/v1/consensus_states/{client_id}| -| `ConsensusStateHeights` | [QueryConsensusStateHeightsRequest](#ibc.core.client.v1.QueryConsensusStateHeightsRequest) | [QueryConsensusStateHeightsResponse](#ibc.core.client.v1.QueryConsensusStateHeightsResponse) | ConsensusStateHeights queries the height of every consensus states associated with a given client. | GET|/ibc/core/client/v1/consensus_states/{client_id}/heights| -| `ClientStatus` | [QueryClientStatusRequest](#ibc.core.client.v1.QueryClientStatusRequest) | [QueryClientStatusResponse](#ibc.core.client.v1.QueryClientStatusResponse) | Status queries the status of an IBC client. | GET|/ibc/core/client/v1/client_status/{client_id}| -| `ClientParams` | [QueryClientParamsRequest](#ibc.core.client.v1.QueryClientParamsRequest) | [QueryClientParamsResponse](#ibc.core.client.v1.QueryClientParamsResponse) | ClientParams queries all parameters of the ibc client. | GET|/ibc/client/v1/params| -| `UpgradedClientState` | [QueryUpgradedClientStateRequest](#ibc.core.client.v1.QueryUpgradedClientStateRequest) | [QueryUpgradedClientStateResponse](#ibc.core.client.v1.QueryUpgradedClientStateResponse) | UpgradedClientState queries an Upgraded IBC light client. | GET|/ibc/core/client/v1/upgraded_client_states| -| `UpgradedConsensusState` | [QueryUpgradedConsensusStateRequest](#ibc.core.client.v1.QueryUpgradedConsensusStateRequest) | [QueryUpgradedConsensusStateResponse](#ibc.core.client.v1.QueryUpgradedConsensusStateResponse) | UpgradedConsensusState queries an Upgraded IBC consensus state. | GET|/ibc/core/client/v1/upgraded_consensus_states| - - - - - - -

    Top

    - -## ibc/core/client/v1/tx.proto - - - - - -### MsgCreateClient -MsgCreateClient defines a message to create an IBC client - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `client_state` | [google.protobuf.Any](#google.protobuf.Any) | | light client state | -| `consensus_state` | [google.protobuf.Any](#google.protobuf.Any) | | consensus state associated with the client that corresponds to a given height. | -| `signer` | [string](#string) | | signer address | - - - - - - - - -### MsgCreateClientResponse -MsgCreateClientResponse defines the Msg/CreateClient response type. - - - - - - - - -### MsgSubmitMisbehaviour -MsgSubmitMisbehaviour defines an sdk.Msg type that submits Evidence for -light client misbehaviour. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `client_id` | [string](#string) | | client unique identifier | -| `misbehaviour` | [google.protobuf.Any](#google.protobuf.Any) | | misbehaviour used for freezing the light client | -| `signer` | [string](#string) | | signer address | - - - - - - - - -### MsgSubmitMisbehaviourResponse -MsgSubmitMisbehaviourResponse defines the Msg/SubmitMisbehaviour response -type. - - - - - - - - -### MsgUpdateClient -MsgUpdateClient defines an sdk.Msg to update a IBC client state using -the given header. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `client_id` | [string](#string) | | client unique identifier | -| `header` | [google.protobuf.Any](#google.protobuf.Any) | | header to update the light client | -| `signer` | [string](#string) | | signer address | - - - - - - - - -### MsgUpdateClientResponse -MsgUpdateClientResponse defines the Msg/UpdateClient response type. - - - - - - - - -### MsgUpgradeClient -MsgUpgradeClient defines an sdk.Msg to upgrade an IBC client to a new client -state - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `client_id` | [string](#string) | | client unique identifier | -| `client_state` | [google.protobuf.Any](#google.protobuf.Any) | | upgraded client state | -| `consensus_state` | [google.protobuf.Any](#google.protobuf.Any) | | upgraded consensus state, only contains enough information to serve as a basis of trust in update logic | -| `proof_upgrade_client` | [bytes](#bytes) | | proof that old chain committed to new client | -| `proof_upgrade_consensus_state` | [bytes](#bytes) | | proof that old chain committed to new consensus state | -| `signer` | [string](#string) | | signer address | - - - - - - - - -### MsgUpgradeClientResponse -MsgUpgradeClientResponse defines the Msg/UpgradeClient response type. - - - - - - - - - - - - - - -### Msg -Msg defines the ibc/client Msg service. - -| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint | -| ----------- | ------------ | ------------- | ------------| ------- | -------- | -| `CreateClient` | [MsgCreateClient](#ibc.core.client.v1.MsgCreateClient) | [MsgCreateClientResponse](#ibc.core.client.v1.MsgCreateClientResponse) | CreateClient defines a rpc handler method for MsgCreateClient. | | -| `UpdateClient` | [MsgUpdateClient](#ibc.core.client.v1.MsgUpdateClient) | [MsgUpdateClientResponse](#ibc.core.client.v1.MsgUpdateClientResponse) | UpdateClient defines a rpc handler method for MsgUpdateClient. | | -| `UpgradeClient` | [MsgUpgradeClient](#ibc.core.client.v1.MsgUpgradeClient) | [MsgUpgradeClientResponse](#ibc.core.client.v1.MsgUpgradeClientResponse) | UpgradeClient defines a rpc handler method for MsgUpgradeClient. | | -| `SubmitMisbehaviour` | [MsgSubmitMisbehaviour](#ibc.core.client.v1.MsgSubmitMisbehaviour) | [MsgSubmitMisbehaviourResponse](#ibc.core.client.v1.MsgSubmitMisbehaviourResponse) | SubmitMisbehaviour defines a rpc handler method for MsgSubmitMisbehaviour. | | - - - - - - -

    Top

    - -## ibc/core/commitment/v1/commitment.proto - - - - - -### MerklePath -MerklePath is the path used to verify commitment proofs, which can be an -arbitrary structured object (defined by a commitment type). -MerklePath is represented from root-to-leaf - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `key_path` | [string](#string) | repeated | | - - - - - - - - -### MerklePrefix -MerklePrefix is merkle path prefixed to the key. -The constructed key from the Path and the key will be append(Path.KeyPath, -append(Path.KeyPrefix, key...)) - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `key_prefix` | [bytes](#bytes) | | | - - - - - - - - -### MerkleProof -MerkleProof is a wrapper type over a chain of CommitmentProofs. -It demonstrates membership or non-membership for an element or set of -elements, verifiable in conjunction with a known commitment root. Proofs -should be succinct. -MerkleProofs are ordered from leaf-to-root - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `proofs` | [ics23.CommitmentProof](#ics23.CommitmentProof) | repeated | | - - - - - - - - -### MerkleRoot -MerkleRoot defines a merkle root hash. -In the Cosmos SDK, the AppHash of a block header becomes the root. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `hash` | [bytes](#bytes) | | | - - - - - - - - - - - - - - - - -

    Top

    - -## ibc/core/connection/v1/connection.proto - - - - - -### ClientPaths -ClientPaths define all the connection paths for a client state. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `paths` | [string](#string) | repeated | list of connection paths | - - - - - - - - -### ConnectionEnd -ConnectionEnd defines a stateful object on a chain connected to another -separate one. -NOTE: there must only be 2 defined ConnectionEnds to establish -a connection between two chains. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `client_id` | [string](#string) | | client associated with this connection. | -| `versions` | [Version](#ibc.core.connection.v1.Version) | repeated | IBC version which can be utilised to determine encodings or protocols for channels or packets utilising this connection. | -| `state` | [State](#ibc.core.connection.v1.State) | | current state of the connection end. | -| `counterparty` | [Counterparty](#ibc.core.connection.v1.Counterparty) | | counterparty chain associated with this connection. | -| `delay_period` | [uint64](#uint64) | | delay period that must pass before a consensus state can be used for packet-verification NOTE: delay period logic is only implemented by some clients. | - - - - - - - - -### ConnectionPaths -ConnectionPaths define all the connection paths for a given client state. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `client_id` | [string](#string) | | client state unique identifier | -| `paths` | [string](#string) | repeated | list of connection paths | - - - - - - - - -### Counterparty -Counterparty defines the counterparty chain associated with a connection end. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `client_id` | [string](#string) | | identifies the client on the counterparty chain associated with a given connection. | -| `connection_id` | [string](#string) | | identifies the connection end on the counterparty chain associated with a given connection. | -| `prefix` | [ibc.core.commitment.v1.MerklePrefix](#ibc.core.commitment.v1.MerklePrefix) | | commitment merkle prefix of the counterparty chain. | - - - - - - - - -### IdentifiedConnection -IdentifiedConnection defines a connection with additional connection -identifier field. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `id` | [string](#string) | | connection identifier. | -| `client_id` | [string](#string) | | client associated with this connection. | -| `versions` | [Version](#ibc.core.connection.v1.Version) | repeated | IBC version which can be utilised to determine encodings or protocols for channels or packets utilising this connection | -| `state` | [State](#ibc.core.connection.v1.State) | | current state of the connection end. | -| `counterparty` | [Counterparty](#ibc.core.connection.v1.Counterparty) | | counterparty chain associated with this connection. | -| `delay_period` | [uint64](#uint64) | | delay period associated with this connection. | - - - - - - - - -### Params -Params defines the set of Connection parameters. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `max_expected_time_per_block` | [uint64](#uint64) | | maximum expected time per block (in nanoseconds), used to enforce block delay. This parameter should reflect the largest amount of time that the chain might reasonably take to produce the next block under normal operating conditions. A safe choice is 3-5x the expected time per block. | - - - - - - - - -### Version -Version defines the versioning scheme used to negotiate the IBC verison in -the connection handshake. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `identifier` | [string](#string) | | unique version identifier | -| `features` | [string](#string) | repeated | list of features compatible with the specified identifier | - - - - - - - - - - -### State -State defines if a connection is in one of the following states: -INIT, TRYOPEN, OPEN or UNINITIALIZED. - -| Name | Number | Description | -| ---- | ------ | ----------- | -| STATE_UNINITIALIZED_UNSPECIFIED | 0 | Default State | -| STATE_INIT | 1 | A connection end has just started the opening handshake. | -| STATE_TRYOPEN | 2 | A connection end has acknowledged the handshake step on the counterparty chain. | -| STATE_OPEN | 3 | A connection end has completed the handshake. | - - - - - - - - - - - -

    Top

    - -## ibc/core/connection/v1/genesis.proto - - - - - -### GenesisState -GenesisState defines the ibc connection submodule's genesis state. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `connections` | [IdentifiedConnection](#ibc.core.connection.v1.IdentifiedConnection) | repeated | | -| `client_connection_paths` | [ConnectionPaths](#ibc.core.connection.v1.ConnectionPaths) | repeated | | -| `next_connection_sequence` | [uint64](#uint64) | | the sequence for the next generated connection identifier | -| `params` | [Params](#ibc.core.connection.v1.Params) | | | - - - - - - - - - - - - - - - - -

    Top

    - -## ibc/core/connection/v1/query.proto - - - - - -### QueryClientConnectionsRequest -QueryClientConnectionsRequest is the request type for the -Query/ClientConnections RPC method - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `client_id` | [string](#string) | | client identifier associated with a connection | - - - - - - - - -### QueryClientConnectionsResponse -QueryClientConnectionsResponse is the response type for the -Query/ClientConnections RPC method - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `connection_paths` | [string](#string) | repeated | slice of all the connection paths associated with a client. | -| `proof` | [bytes](#bytes) | | merkle proof of existence | -| `proof_height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | height at which the proof was generated | - - - - - - - - -### QueryConnectionClientStateRequest -QueryConnectionClientStateRequest is the request type for the -Query/ConnectionClientState RPC method - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `connection_id` | [string](#string) | | connection identifier | - - - - - - - - -### QueryConnectionClientStateResponse -QueryConnectionClientStateResponse is the response type for the -Query/ConnectionClientState RPC method - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `identified_client_state` | [ibc.core.client.v1.IdentifiedClientState](#ibc.core.client.v1.IdentifiedClientState) | | client state associated with the channel | -| `proof` | [bytes](#bytes) | | merkle proof of existence | -| `proof_height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | height at which the proof was retrieved | - - - - - - - - -### QueryConnectionConsensusStateRequest -QueryConnectionConsensusStateRequest is the request type for the -Query/ConnectionConsensusState RPC method - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `connection_id` | [string](#string) | | connection identifier | -| `revision_number` | [uint64](#uint64) | | | -| `revision_height` | [uint64](#uint64) | | | - - - - - - - - -### QueryConnectionConsensusStateResponse -QueryConnectionConsensusStateResponse is the response type for the -Query/ConnectionConsensusState RPC method - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `consensus_state` | [google.protobuf.Any](#google.protobuf.Any) | | consensus state associated with the channel | -| `client_id` | [string](#string) | | client ID associated with the consensus state | -| `proof` | [bytes](#bytes) | | merkle proof of existence | -| `proof_height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | height at which the proof was retrieved | - - - - - - - - -### QueryConnectionRequest -QueryConnectionRequest is the request type for the Query/Connection RPC -method - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `connection_id` | [string](#string) | | connection unique identifier | - - - - - - - - -### QueryConnectionResponse -QueryConnectionResponse is the response type for the Query/Connection RPC -method. Besides the connection end, it includes a proof and the height from -which the proof was retrieved. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `connection` | [ConnectionEnd](#ibc.core.connection.v1.ConnectionEnd) | | connection associated with the request identifier | -| `proof` | [bytes](#bytes) | | merkle proof of existence | -| `proof_height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | height at which the proof was retrieved | - - - - - - - - -### QueryConnectionsRequest -QueryConnectionsRequest is the request type for the Query/Connections RPC -method - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | | - - - - - - - - -### QueryConnectionsResponse -QueryConnectionsResponse is the response type for the Query/Connections RPC -method. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `connections` | [IdentifiedConnection](#ibc.core.connection.v1.IdentifiedConnection) | repeated | list of stored connections of the chain. | -| `pagination` | [cosmos.base.query.v1beta1.PageResponse](#cosmos.base.query.v1beta1.PageResponse) | | pagination response | -| `height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | query block height | - - - - - - - - - - - - - - -### Query -Query provides defines the gRPC querier service - -| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint | -| ----------- | ------------ | ------------- | ------------| ------- | -------- | -| `Connection` | [QueryConnectionRequest](#ibc.core.connection.v1.QueryConnectionRequest) | [QueryConnectionResponse](#ibc.core.connection.v1.QueryConnectionResponse) | Connection queries an IBC connection end. | GET|/ibc/core/connection/v1/connections/{connection_id}| -| `Connections` | [QueryConnectionsRequest](#ibc.core.connection.v1.QueryConnectionsRequest) | [QueryConnectionsResponse](#ibc.core.connection.v1.QueryConnectionsResponse) | Connections queries all the IBC connections of a chain. | GET|/ibc/core/connection/v1/connections| -| `ClientConnections` | [QueryClientConnectionsRequest](#ibc.core.connection.v1.QueryClientConnectionsRequest) | [QueryClientConnectionsResponse](#ibc.core.connection.v1.QueryClientConnectionsResponse) | ClientConnections queries the connection paths associated with a client state. | GET|/ibc/core/connection/v1/client_connections/{client_id}| -| `ConnectionClientState` | [QueryConnectionClientStateRequest](#ibc.core.connection.v1.QueryConnectionClientStateRequest) | [QueryConnectionClientStateResponse](#ibc.core.connection.v1.QueryConnectionClientStateResponse) | ConnectionClientState queries the client state associated with the connection. | GET|/ibc/core/connection/v1/connections/{connection_id}/client_state| -| `ConnectionConsensusState` | [QueryConnectionConsensusStateRequest](#ibc.core.connection.v1.QueryConnectionConsensusStateRequest) | [QueryConnectionConsensusStateResponse](#ibc.core.connection.v1.QueryConnectionConsensusStateResponse) | ConnectionConsensusState queries the consensus state associated with the connection. | GET|/ibc/core/connection/v1/connections/{connection_id}/consensus_state/revision/{revision_number}/height/{revision_height}| - - - - - - -

    Top

    - -## ibc/core/connection/v1/tx.proto - - - - - -### MsgConnectionOpenAck -MsgConnectionOpenAck defines a msg sent by a Relayer to Chain A to -acknowledge the change of connection state to TRYOPEN on Chain B. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `connection_id` | [string](#string) | | | -| `counterparty_connection_id` | [string](#string) | | | -| `version` | [Version](#ibc.core.connection.v1.Version) | | | -| `client_state` | [google.protobuf.Any](#google.protobuf.Any) | | | -| `proof_height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | | -| `proof_try` | [bytes](#bytes) | | proof of the initialization the connection on Chain B: `UNITIALIZED -> TRYOPEN` | -| `proof_client` | [bytes](#bytes) | | proof of client state included in message | -| `proof_consensus` | [bytes](#bytes) | | proof of client consensus state | -| `consensus_height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | | -| `signer` | [string](#string) | | | - - - - - - - - -### MsgConnectionOpenAckResponse -MsgConnectionOpenAckResponse defines the Msg/ConnectionOpenAck response type. - - - - - - - - -### MsgConnectionOpenConfirm -MsgConnectionOpenConfirm defines a msg sent by a Relayer to Chain B to -acknowledge the change of connection state to OPEN on Chain A. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `connection_id` | [string](#string) | | | -| `proof_ack` | [bytes](#bytes) | | proof for the change of the connection state on Chain A: `INIT -> OPEN` | -| `proof_height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | | -| `signer` | [string](#string) | | | - - - - - - - - -### MsgConnectionOpenConfirmResponse -MsgConnectionOpenConfirmResponse defines the Msg/ConnectionOpenConfirm -response type. - - - - - - - - -### MsgConnectionOpenInit -MsgConnectionOpenInit defines the msg sent by an account on Chain A to -initialize a connection with Chain B. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `client_id` | [string](#string) | | | -| `counterparty` | [Counterparty](#ibc.core.connection.v1.Counterparty) | | | -| `version` | [Version](#ibc.core.connection.v1.Version) | | | -| `delay_period` | [uint64](#uint64) | | | -| `signer` | [string](#string) | | | - - - - - - - - -### MsgConnectionOpenInitResponse -MsgConnectionOpenInitResponse defines the Msg/ConnectionOpenInit response -type. - - - - - - - - -### MsgConnectionOpenTry -MsgConnectionOpenTry defines a msg sent by a Relayer to try to open a -connection on Chain B. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `client_id` | [string](#string) | | | -| `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) | | | -| `counterparty_versions` | [Version](#ibc.core.connection.v1.Version) | repeated | | -| `proof_height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | | -| `proof_init` | [bytes](#bytes) | | proof of the initialization the connection on Chain A: `UNITIALIZED -> INIT` | -| `proof_client` | [bytes](#bytes) | | proof of client state included in message | -| `proof_consensus` | [bytes](#bytes) | | proof of client consensus state | -| `consensus_height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | | -| `signer` | [string](#string) | | | - - - - - - - - -### MsgConnectionOpenTryResponse -MsgConnectionOpenTryResponse defines the Msg/ConnectionOpenTry response type. - - - - - - - - - - - - - - -### Msg -Msg defines the ibc/connection Msg service. - -| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint | -| ----------- | ------------ | ------------- | ------------| ------- | -------- | -| `ConnectionOpenInit` | [MsgConnectionOpenInit](#ibc.core.connection.v1.MsgConnectionOpenInit) | [MsgConnectionOpenInitResponse](#ibc.core.connection.v1.MsgConnectionOpenInitResponse) | ConnectionOpenInit defines a rpc handler method for MsgConnectionOpenInit. | | -| `ConnectionOpenTry` | [MsgConnectionOpenTry](#ibc.core.connection.v1.MsgConnectionOpenTry) | [MsgConnectionOpenTryResponse](#ibc.core.connection.v1.MsgConnectionOpenTryResponse) | ConnectionOpenTry defines a rpc handler method for MsgConnectionOpenTry. | | -| `ConnectionOpenAck` | [MsgConnectionOpenAck](#ibc.core.connection.v1.MsgConnectionOpenAck) | [MsgConnectionOpenAckResponse](#ibc.core.connection.v1.MsgConnectionOpenAckResponse) | ConnectionOpenAck defines a rpc handler method for MsgConnectionOpenAck. | | -| `ConnectionOpenConfirm` | [MsgConnectionOpenConfirm](#ibc.core.connection.v1.MsgConnectionOpenConfirm) | [MsgConnectionOpenConfirmResponse](#ibc.core.connection.v1.MsgConnectionOpenConfirmResponse) | ConnectionOpenConfirm defines a rpc handler method for MsgConnectionOpenConfirm. | | - - - - - - -

    Top

    - -## ibc/core/types/v1/genesis.proto - - - - - -### GenesisState -GenesisState defines the ibc module's genesis state. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `client_genesis` | [ibc.core.client.v1.GenesisState](#ibc.core.client.v1.GenesisState) | | ICS002 - Clients genesis state | -| `connection_genesis` | [ibc.core.connection.v1.GenesisState](#ibc.core.connection.v1.GenesisState) | | ICS003 - Connections genesis state | -| `channel_genesis` | [ibc.core.channel.v1.GenesisState](#ibc.core.channel.v1.GenesisState) | | ICS004 - Channel genesis state | - - - - - - - - - - - - - - - - -

    Top

    - -## ibc/lightclients/localhost/v1/localhost.proto - - - - - -### ClientState -ClientState defines a loopback (localhost) client. It requires (read-only) -access to keys outside the client prefix. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `chain_id` | [string](#string) | | self chain ID | -| `height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | self latest block height | - - - - - - - - - - - - - - - - -

    Top

    - -## ibc/lightclients/solomachine/v1/solomachine.proto - - - - - -### ChannelStateData -ChannelStateData returns the SignBytes data for channel state -verification. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `path` | [bytes](#bytes) | | | -| `channel` | [ibc.core.channel.v1.Channel](#ibc.core.channel.v1.Channel) | | | - - - - - - - - -### ClientState -ClientState defines a solo machine client that tracks the current consensus -state and if the client is frozen. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `sequence` | [uint64](#uint64) | | latest sequence of the client state | -| `frozen_sequence` | [uint64](#uint64) | | frozen sequence of the solo machine | -| `consensus_state` | [ConsensusState](#ibc.lightclients.solomachine.v1.ConsensusState) | | | -| `allow_update_after_proposal` | [bool](#bool) | | when set to true, will allow governance to update a solo machine client. The client will be unfrozen if it is frozen. | - - - - - - - - -### ClientStateData -ClientStateData returns the SignBytes data for client state verification. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `path` | [bytes](#bytes) | | | -| `client_state` | [google.protobuf.Any](#google.protobuf.Any) | | | - - - - - - - - -### ConnectionStateData -ConnectionStateData returns the SignBytes data for connection state -verification. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `path` | [bytes](#bytes) | | | -| `connection` | [ibc.core.connection.v1.ConnectionEnd](#ibc.core.connection.v1.ConnectionEnd) | | | - - - - - - - - -### ConsensusState -ConsensusState defines a solo machine consensus state. The sequence of a -consensus state is contained in the "height" key used in storing the -consensus state. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `public_key` | [google.protobuf.Any](#google.protobuf.Any) | | public key of the solo machine | -| `diversifier` | [string](#string) | | diversifier allows the same public key to be re-used across different solo machine clients (potentially on different chains) without being considered misbehaviour. | -| `timestamp` | [uint64](#uint64) | | | - - - - - - - - -### ConsensusStateData -ConsensusStateData returns the SignBytes data for consensus state -verification. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `path` | [bytes](#bytes) | | | -| `consensus_state` | [google.protobuf.Any](#google.protobuf.Any) | | | - - - - - - - - -### Header -Header defines a solo machine consensus header - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `sequence` | [uint64](#uint64) | | sequence to update solo machine public key at | -| `timestamp` | [uint64](#uint64) | | | -| `signature` | [bytes](#bytes) | | | -| `new_public_key` | [google.protobuf.Any](#google.protobuf.Any) | | | -| `new_diversifier` | [string](#string) | | | - - - - - - - - -### HeaderData -HeaderData returns the SignBytes data for update verification. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `new_pub_key` | [google.protobuf.Any](#google.protobuf.Any) | | header public key | -| `new_diversifier` | [string](#string) | | header diversifier | - - - - - - - - -### Misbehaviour -Misbehaviour defines misbehaviour for a solo machine which consists -of a sequence and two signatures over different messages at that sequence. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `client_id` | [string](#string) | | | -| `sequence` | [uint64](#uint64) | | | -| `signature_one` | [SignatureAndData](#ibc.lightclients.solomachine.v1.SignatureAndData) | | | -| `signature_two` | [SignatureAndData](#ibc.lightclients.solomachine.v1.SignatureAndData) | | | - - - - - - - - -### NextSequenceRecvData -NextSequenceRecvData returns the SignBytes data for verification of the next -sequence to be received. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `path` | [bytes](#bytes) | | | -| `next_seq_recv` | [uint64](#uint64) | | | - - - - - - - - -### PacketAcknowledgementData -PacketAcknowledgementData returns the SignBytes data for acknowledgement -verification. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `path` | [bytes](#bytes) | | | -| `acknowledgement` | [bytes](#bytes) | | | - - - - - - - - -### PacketCommitmentData -PacketCommitmentData returns the SignBytes data for packet commitment -verification. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `path` | [bytes](#bytes) | | | -| `commitment` | [bytes](#bytes) | | | - - - - - - - - -### PacketReceiptAbsenceData -PacketReceiptAbsenceData returns the SignBytes data for -packet receipt absence verification. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `path` | [bytes](#bytes) | | | - - - - - - - - -### SignBytes -SignBytes defines the signed bytes used for signature verification. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `sequence` | [uint64](#uint64) | | | -| `timestamp` | [uint64](#uint64) | | | -| `diversifier` | [string](#string) | | | -| `data_type` | [DataType](#ibc.lightclients.solomachine.v1.DataType) | | type of the data used | -| `data` | [bytes](#bytes) | | marshaled data | - - - - - - - - -### SignatureAndData -SignatureAndData contains a signature and the data signed over to create that -signature. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `signature` | [bytes](#bytes) | | | -| `data_type` | [DataType](#ibc.lightclients.solomachine.v1.DataType) | | | -| `data` | [bytes](#bytes) | | | -| `timestamp` | [uint64](#uint64) | | | - - - - - - - - -### TimestampedSignatureData -TimestampedSignatureData contains the signature data and the timestamp of the -signature. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `signature_data` | [bytes](#bytes) | | | -| `timestamp` | [uint64](#uint64) | | | - - - - - - - - - - -### DataType -DataType defines the type of solo machine proof being created. This is done -to preserve uniqueness of different data sign byte encodings. - -| Name | Number | Description | -| ---- | ------ | ----------- | -| DATA_TYPE_UNINITIALIZED_UNSPECIFIED | 0 | Default State | -| DATA_TYPE_CLIENT_STATE | 1 | Data type for client state verification | -| DATA_TYPE_CONSENSUS_STATE | 2 | Data type for consensus state verification | -| DATA_TYPE_CONNECTION_STATE | 3 | Data type for connection state verification | -| DATA_TYPE_CHANNEL_STATE | 4 | Data type for channel state verification | -| DATA_TYPE_PACKET_COMMITMENT | 5 | Data type for packet commitment verification | -| DATA_TYPE_PACKET_ACKNOWLEDGEMENT | 6 | Data type for packet acknowledgement verification | -| DATA_TYPE_PACKET_RECEIPT_ABSENCE | 7 | Data type for packet receipt absence verification | -| DATA_TYPE_NEXT_SEQUENCE_RECV | 8 | Data type for next sequence recv verification | -| DATA_TYPE_HEADER | 9 | Data type for header verification | - - - - - - - - - - - -

    Top

    - -## ibc/lightclients/solomachine/v2/solomachine.proto - - - - - -### ChannelStateData -ChannelStateData returns the SignBytes data for channel state -verification. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `path` | [bytes](#bytes) | | | -| `channel` | [ibc.core.channel.v1.Channel](#ibc.core.channel.v1.Channel) | | | - - - - - - - - -### ClientState -ClientState defines a solo machine client that tracks the current consensus -state and if the client is frozen. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `sequence` | [uint64](#uint64) | | latest sequence of the client state | -| `is_frozen` | [bool](#bool) | | frozen sequence of the solo machine | -| `consensus_state` | [ConsensusState](#ibc.lightclients.solomachine.v2.ConsensusState) | | | -| `allow_update_after_proposal` | [bool](#bool) | | when set to true, will allow governance to update a solo machine client. The client will be unfrozen if it is frozen. | - - - - - - - - -### ClientStateData -ClientStateData returns the SignBytes data for client state verification. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `path` | [bytes](#bytes) | | | -| `client_state` | [google.protobuf.Any](#google.protobuf.Any) | | | - - - - - - - - -### ConnectionStateData -ConnectionStateData returns the SignBytes data for connection state -verification. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `path` | [bytes](#bytes) | | | -| `connection` | [ibc.core.connection.v1.ConnectionEnd](#ibc.core.connection.v1.ConnectionEnd) | | | - - - - - - - - -### ConsensusState -ConsensusState defines a solo machine consensus state. The sequence of a -consensus state is contained in the "height" key used in storing the -consensus state. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `public_key` | [google.protobuf.Any](#google.protobuf.Any) | | public key of the solo machine | -| `diversifier` | [string](#string) | | diversifier allows the same public key to be re-used across different solo machine clients (potentially on different chains) without being considered misbehaviour. | -| `timestamp` | [uint64](#uint64) | | | - - - - - - - - -### ConsensusStateData -ConsensusStateData returns the SignBytes data for consensus state -verification. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `path` | [bytes](#bytes) | | | -| `consensus_state` | [google.protobuf.Any](#google.protobuf.Any) | | | - - - - - - - - -### Header -Header defines a solo machine consensus header - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `sequence` | [uint64](#uint64) | | sequence to update solo machine public key at | -| `timestamp` | [uint64](#uint64) | | | -| `signature` | [bytes](#bytes) | | | -| `new_public_key` | [google.protobuf.Any](#google.protobuf.Any) | | | -| `new_diversifier` | [string](#string) | | | - - - - - - - - -### HeaderData -HeaderData returns the SignBytes data for update verification. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `new_pub_key` | [google.protobuf.Any](#google.protobuf.Any) | | header public key | -| `new_diversifier` | [string](#string) | | header diversifier | - - - - - - - - -### Misbehaviour -Misbehaviour defines misbehaviour for a solo machine which consists -of a sequence and two signatures over different messages at that sequence. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `client_id` | [string](#string) | | | -| `sequence` | [uint64](#uint64) | | | -| `signature_one` | [SignatureAndData](#ibc.lightclients.solomachine.v2.SignatureAndData) | | | -| `signature_two` | [SignatureAndData](#ibc.lightclients.solomachine.v2.SignatureAndData) | | | - - - - - - - - -### NextSequenceRecvData -NextSequenceRecvData returns the SignBytes data for verification of the next -sequence to be received. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `path` | [bytes](#bytes) | | | -| `next_seq_recv` | [uint64](#uint64) | | | - - - - - - - - -### PacketAcknowledgementData -PacketAcknowledgementData returns the SignBytes data for acknowledgement -verification. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `path` | [bytes](#bytes) | | | -| `acknowledgement` | [bytes](#bytes) | | | - - - - - - - - -### PacketCommitmentData -PacketCommitmentData returns the SignBytes data for packet commitment -verification. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `path` | [bytes](#bytes) | | | -| `commitment` | [bytes](#bytes) | | | - - - - - - - - -### PacketReceiptAbsenceData -PacketReceiptAbsenceData returns the SignBytes data for -packet receipt absence verification. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `path` | [bytes](#bytes) | | | - - - - - - - - -### SignBytes -SignBytes defines the signed bytes used for signature verification. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `sequence` | [uint64](#uint64) | | | -| `timestamp` | [uint64](#uint64) | | | -| `diversifier` | [string](#string) | | | -| `data_type` | [DataType](#ibc.lightclients.solomachine.v2.DataType) | | type of the data used | -| `data` | [bytes](#bytes) | | marshaled data | - - - - - - - - -### SignatureAndData -SignatureAndData contains a signature and the data signed over to create that -signature. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `signature` | [bytes](#bytes) | | | -| `data_type` | [DataType](#ibc.lightclients.solomachine.v2.DataType) | | | -| `data` | [bytes](#bytes) | | | -| `timestamp` | [uint64](#uint64) | | | - - - - - - - - -### TimestampedSignatureData -TimestampedSignatureData contains the signature data and the timestamp of the -signature. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `signature_data` | [bytes](#bytes) | | | -| `timestamp` | [uint64](#uint64) | | | - - - - - - - - - - -### DataType -DataType defines the type of solo machine proof being created. This is done -to preserve uniqueness of different data sign byte encodings. - -| Name | Number | Description | -| ---- | ------ | ----------- | -| DATA_TYPE_UNINITIALIZED_UNSPECIFIED | 0 | Default State | -| DATA_TYPE_CLIENT_STATE | 1 | Data type for client state verification | -| DATA_TYPE_CONSENSUS_STATE | 2 | Data type for consensus state verification | -| DATA_TYPE_CONNECTION_STATE | 3 | Data type for connection state verification | -| DATA_TYPE_CHANNEL_STATE | 4 | Data type for channel state verification | -| DATA_TYPE_PACKET_COMMITMENT | 5 | Data type for packet commitment verification | -| DATA_TYPE_PACKET_ACKNOWLEDGEMENT | 6 | Data type for packet acknowledgement verification | -| DATA_TYPE_PACKET_RECEIPT_ABSENCE | 7 | Data type for packet receipt absence verification | -| DATA_TYPE_NEXT_SEQUENCE_RECV | 8 | Data type for next sequence recv verification | -| DATA_TYPE_HEADER | 9 | Data type for header verification | - - - - - - - - - - - -

    Top

    - -## ibc/lightclients/tendermint/v1/tendermint.proto - - - - - -### ClientState -ClientState from Tendermint tracks the current validator set, latest height, -and a possible frozen height. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `chain_id` | [string](#string) | | | -| `trust_level` | [Fraction](#ibc.lightclients.tendermint.v1.Fraction) | | | -| `trusting_period` | [google.protobuf.Duration](#google.protobuf.Duration) | | duration of the period since the LastestTimestamp during which the submitted headers are valid for upgrade | -| `unbonding_period` | [google.protobuf.Duration](#google.protobuf.Duration) | | duration of the staking unbonding period | -| `max_clock_drift` | [google.protobuf.Duration](#google.protobuf.Duration) | | defines how much new (untrusted) header's Time can drift into the future. | -| `frozen_height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | Block height when the client was frozen due to a misbehaviour | -| `latest_height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | Latest height the client was updated to | -| `proof_specs` | [ics23.ProofSpec](#ics23.ProofSpec) | repeated | Proof specifications used in verifying counterparty state | -| `upgrade_path` | [string](#string) | repeated | Path at which next upgraded client will be committed. Each element corresponds to the key for a single CommitmentProof in the chained proof. NOTE: ClientState must stored under `{upgradePath}/{upgradeHeight}/clientState` ConsensusState must be stored under `{upgradepath}/{upgradeHeight}/consensusState` For SDK chains using the default upgrade module, upgrade_path should be []string{"upgrade", "upgradedIBCState"}` | -| `allow_update_after_expiry` | [bool](#bool) | | **Deprecated.** allow_update_after_expiry is deprecated | -| `allow_update_after_misbehaviour` | [bool](#bool) | | **Deprecated.** allow_update_after_misbehaviour is deprecated | - - - - - - - - -### ConsensusState -ConsensusState defines the consensus state from Tendermint. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `timestamp` | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | | timestamp that corresponds to the block height in which the ConsensusState was stored. | -| `root` | [ibc.core.commitment.v1.MerkleRoot](#ibc.core.commitment.v1.MerkleRoot) | | commitment root (i.e app hash) | -| `next_validators_hash` | [bytes](#bytes) | | | - - - - - - - - -### Fraction -Fraction defines the protobuf message type for tmmath.Fraction that only -supports positive values. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `numerator` | [uint64](#uint64) | | | -| `denominator` | [uint64](#uint64) | | | - - - - - - - - -### Header -Header defines the Tendermint client consensus Header. -It encapsulates all the information necessary to update from a trusted -Tendermint ConsensusState. The inclusion of TrustedHeight and -TrustedValidators allows this update to process correctly, so long as the -ConsensusState for the TrustedHeight exists, this removes race conditions -among relayers The SignedHeader and ValidatorSet are the new untrusted update -fields for the client. The TrustedHeight is the height of a stored -ConsensusState on the client that will be used to verify the new untrusted -header. The Trusted ConsensusState must be within the unbonding period of -current time in order to correctly verify, and the TrustedValidators must -hash to TrustedConsensusState.NextValidatorsHash since that is the last -trusted validator set at the TrustedHeight. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `signed_header` | [tendermint.types.SignedHeader](#tendermint.types.SignedHeader) | | | -| `validator_set` | [tendermint.types.ValidatorSet](#tendermint.types.ValidatorSet) | | | -| `trusted_height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | | -| `trusted_validators` | [tendermint.types.ValidatorSet](#tendermint.types.ValidatorSet) | | | - - - - - - - - -### Misbehaviour -Misbehaviour is a wrapper over two conflicting Headers -that implements Misbehaviour interface expected by ICS-02 - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `client_id` | [string](#string) | | | -| `header_1` | [Header](#ibc.lightclients.tendermint.v1.Header) | | | -| `header_2` | [Header](#ibc.lightclients.tendermint.v1.Header) | | | - - - - - - - - - - - - - - - -## Scalar Value Types - -| .proto Type | Notes | C++ | Java | Python | Go | C# | PHP | Ruby | -| ----------- | ----- | --- | ---- | ------ | -- | -- | --- | ---- | -| double | | double | double | float | float64 | double | float | Float | -| float | | float | float | float | float32 | float | float | Float | -| int32 | Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint32 instead. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) | -| int64 | Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint64 instead. | int64 | long | int/long | int64 | long | integer/string | Bignum | -| uint32 | Uses variable-length encoding. | uint32 | int | int/long | uint32 | uint | integer | Bignum or Fixnum (as required) | -| uint64 | Uses variable-length encoding. | uint64 | long | int/long | uint64 | ulong | integer/string | Bignum or Fixnum (as required) | -| sint32 | Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int32s. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) | -| sint64 | Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int64s. | int64 | long | int/long | int64 | long | integer/string | Bignum | -| fixed32 | Always four bytes. More efficient than uint32 if values are often greater than 2^28. | uint32 | int | int | uint32 | uint | integer | Bignum or Fixnum (as required) | -| fixed64 | Always eight bytes. More efficient than uint64 if values are often greater than 2^56. | uint64 | long | int/long | uint64 | ulong | integer/string | Bignum | -| sfixed32 | Always four bytes. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) | -| sfixed64 | Always eight bytes. | int64 | long | int/long | int64 | long | integer/string | Bignum | -| bool | | bool | boolean | boolean | bool | bool | boolean | TrueClass/FalseClass | -| string | A string must always contain UTF-8 encoded or 7-bit ASCII text. | string | String | str/unicode | string | string | string | String (UTF-8) | -| bytes | May contain any arbitrary sequence of bytes. | string | ByteString | str | []byte | ByteString | string | String (ASCII-8BIT) | +# Protobuf documentation +See [ibc-go Buf Protobuf documentation](https://buf.build/cosmos/ibc/tags/main). diff --git a/docs/ibc/relayer.md b/docs/ibc/relayer.md index ce3fabe252d..d8fdd6a2164 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 @@ -17,20 +17,20 @@ Any message that uses IBC will emit events for the corresponding TAO logic execu the [IBC events document](./events.md). In the SDK, it can be assumed that for every message there is an event emitted with the type `message`, -attribute key `action`, and an attribute value representing the type of message sent -(`channel_open_init` would be the attribute value for `MsgChannelOpenInit`). If a relayer queries +attribute key `action`, and an attribute value representing the type of message sent +(`channel_open_init` would be the attribute value for `MsgChannelOpenInit`). If a relayer queries for transaction events, it can split message events using this event Type/Attribute Key pair. The Event Type `message` with the Attribute Key `module` may be emitted multiple times for a single -message due to application callbacks. It can be assumed that any TAO logic executed will result in +message due to application callbacks. It can be assumed that any TAO logic executed will result in a module event emission with the attribute value `ibc_` (02-client emits `ibc_client`). -### Subscribing with Tendermint +### 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 +form `.` to `attribute_value`. This causes extraction of the event ordering to be non-trivial, but still possible. A relayer should use the `message.action` key to extract the number of messages in the transaction @@ -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/README.md b/docs/ibc/upgrades/README.md index 11ccabe2378..8245e85a2ec 100644 --- a/docs/ibc/upgrades/README.md +++ b/docs/ibc/upgrades/README.md @@ -4,9 +4,9 @@ parent: order: 3 --> -### Upgrading IBC Chains Overview +# Upgrading IBC Chains Overview -This directory contains information on how to upgrade an IBC chain without breaking counterparty clients and connections. +This directory contains information on how to upgrade an IBC chain without breaking counterparty clients and connections. IBC-connnected chains must be able to upgrade without breaking connections to other chains. Otherwise there would be a massive disincentive towards upgrading and disrupting high-value IBC connections, thus preventing chains in the IBC ecosystem from evolving and improving. Many chain upgrades may be irrelevant to IBC, however some upgrades could potentially break counterparty clients if not handled correctly. Thus, any IBC chain that wishes to perform a IBC-client-breaking upgrade must perform an IBC upgrade in order to allow counterparty clients to securely upgrade to the new light client. diff --git a/docs/ibc/upgrades/developer-guide.md b/docs/ibc/upgrades/developer-guide.md index 73a19b93664..05b5552279b 100644 --- a/docs/ibc/upgrades/developer-guide.md +++ b/docs/ibc/upgrades/developer-guide.md @@ -6,45 +6,4 @@ order: 2 Learn how to implement upgrade functionality for your custom IBC client. {synopsis} -As mentioned in the [README](./README.md), it is vital that high-value IBC clients can upgrade along with their underlying chains to avoid disruption to the IBC ecosystem. Thus, IBC client developers will want to implement upgrade functionality to enable clients to maintain connections and channels even across chain upgrades. - -The IBC protocol allows client implementations to provide a path to upgrading clients given the upgraded client state, upgraded consensus state and proofs for each. - -```go -// Upgrade functions -// NOTE: proof heights are not included as upgrade to a new revision is expected to pass only on the last -// height committed by the current revision. Clients are responsible for ensuring that the planned last -// height of the current revision is somehow encoded in the proof verification process. -// This is to ensure that no premature upgrades occur, since upgrade plans committed to by the counterparty -// may be cancelled or modified before the last planned height. -VerifyUpgradeAndUpdateState( - ctx sdk.Context, - cdc codec.BinaryCodec, - store sdk.KVStore, - newClient ClientState, - newConsState ConsensusState, - proofUpgradeClient, - proofUpgradeConsState []byte, -) (upgradedClient ClientState, upgradedConsensus ConsensusState, err error) -``` - -Note that the clients should have prior knowledge of the merkle path that the upgraded client and upgraded consensus states will use. The height at which the upgrade has occurred should also be encoded in the proof. The Tendermint client implementation accomplishes this by including an `UpgradePath` in the ClientState itself, which is used along with the upgrade height to construct the merkle path under which the client state and consensus state are committed. - -Developers must ensure that the `UpgradeClientMsg` does not pass until the last height of the old chain has been committed, and after the chain upgrades, the `UpgradeClientMsg` should pass once and only once on all counterparty clients. - -Developers must ensure that the new client adopts all of the new Client parameters that must be uniform across every valid light client of a chain (chain-chosen parameters), while maintaining the Client parameters that are customizable by each individual client (client-chosen parameters) from the previous version of the client. - -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`, `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: - -```go -// Utility function that zeroes out any client customizable fields in client state -// Ledger enforced fields are maintained while all custom fields are zero values -// Used to verify upgrades -ZeroCustomFields() ClientState -``` - -Counterparty clients can upgrade securely by using all of the chain-chosen parameters from the chain-committed `UpgradedClient` and preserving all of the old client-chosen parameters. This enables chains to securely upgrade without relying on an honest relayer, however it can in some cases lead to an invalid final `ClientState` if the new chain-chosen parameters clash with the old client-chosen parameter. This can happen in the Tendermint client case if the upgrading chain lowers the `UnbondingPeriod` (chain-chosen) to a duration below that of a counterparty client's `TrustingPeriod` (client-chosen). Such cases should be clearly documented by developers, so that chains know which upgrades should be avoided to prevent this problem. The final upgraded client should also be validated in `VerifyUpgradeAndUpdateState` before returning to ensure that the client does not upgrade to an invalid `ClientState`. +Please see the section [Handling upgrades](../light-clients/upgrades.md) from the light client developer guide for more information. diff --git a/docs/ibc/upgrades/genesis-restart.md b/docs/ibc/upgrades/genesis-restart.md index 1b6ab4e45b2..abb0fa9a817 100644 --- a/docs/ibc/upgrades/genesis-restart.md +++ b/docs/ibc/upgrades/genesis-restart.md @@ -8,21 +8,21 @@ Learn how to upgrade your chain and counterparty clients using genesis restarts. **NOTE**: Regular genesis restarts are currently unsupported by relayers! -### IBC Client Breaking Upgrades +## IBC Client Breaking Upgrades -IBC client breaking upgrades are possible using genesis restarts. +IBC client breaking upgrades are possible using genesis restarts. It is highly recommended to use the in-place migrations instead of a genesis restart. -Genesis restarts should be used sparingly and as backup plans. +Genesis restarts should be used sparingly and as backup plans. Genesis restarts still require the usage of an IBC upgrade proposal in order to correctly upgrade counterparty clients. -#### Step-by-Step Upgrade Process for SDK Chains +### Step-by-Step Upgrade Process for SDK Chains If the IBC-connected chain is conducting an upgrade that will break counterparty clients, it must ensure that the upgrade is first supported by IBC using the [IBC Client Breaking Upgrade List](https://github.com/cosmos/ibc-go/blob/main/docs/ibc/upgrades/quick-guide.md#ibc-client-breaking-upgrades) and then execute the upgrade process described below in order to prevent counterparty clients from breaking. 1. Create a 02-client [`UpgradeProposal`](https://github.com/cosmos/ibc-go/blob/main/docs/ibc/proto-docs.md#upgradeproposal) with an `UpgradePlan` and a new IBC ClientState in the `UpgradedClientState` field. Note that the `UpgradePlan` must specify an upgrade height **only** (no upgrade time), and the `ClientState` should only include the fields common to all valid clients and zero out any client-customizable fields (such as TrustingPeriod). -2. Vote on and pass the `UpgradeProposal` -3. Halt the node after successful upgrade. +2. Vote on and pass the `UpgradeProposal`. +3. Halt the node after successful upgrade. 4. Export the genesis file. 5. Swap to the new binary. 6. Run migrations on the genesis file. @@ -35,15 +35,12 @@ Upon the `UpgradeProposal` passing, the upgrade module will commit the UpgradedC Once the chain reaches the upgrade height and halts, a relayer can upgrade the counterparty clients to the last block of the old chain. They can then submit the proofs of the `UpgradedClient` and `UpgradedConsensusState` against this last block and upgrade the counterparty client. -#### Step-by-Step Upgrade Process for Relayers Upgrading Counterparty Clients +### Step-by-Step Upgrade Process for Relayers Upgrading Counterparty Clients These steps are identical to the regular [IBC client breaking upgrade process](https://github.com/cosmos/ibc-go/blob/main/docs/ibc/upgrades/quick-guide.md#step-by-step-upgrade-process-for-relayers-upgrading-counterparty-clients). -### Non-IBC Client Breaking Upgrades +## Non-IBC Client Breaking Upgrades -While ibc-go supports genesis restarts which do not break IBC clients, relayers do not support this upgrade path. +While ibc-go supports genesis restarts which do not break IBC clients, relayers do not support this upgrade path. Here is a tracking issue on [Hermes](https://github.com/informalsystems/ibc-rs/issues/1152). Please do not attempt a regular genesis restarts unless you have a tool to update counterparty clients correctly. - - - diff --git a/docs/ibc/upgrades/quick-guide.md b/docs/ibc/upgrades/quick-guide.md index 2c82b3a9047..1bdca7a567e 100644 --- a/docs/ibc/upgrades/quick-guide.md +++ b/docs/ibc/upgrades/quick-guide.md @@ -8,7 +8,7 @@ Learn how to upgrade your chain and counterparty clients. {synopsis} The information in this doc for upgrading chains is relevant to SDK chains. However, the guide for counterparty clients is relevant to any Tendermint client that enables upgrades. -### IBC Client Breaking Upgrades +## IBC Client Breaking Upgrades IBC-connected chains must perform an IBC upgrade if their upgrade will break counterparty IBC clients. The current IBC protocol supports upgrading tendermint chains for a specific subset of IBC-client-breaking upgrades. Here is the exhaustive list of IBC client-breaking upgrades and whether the IBC protocol currently supports such upgrades. @@ -26,18 +26,18 @@ Note: Since upgrades are only implemented for Tendermint clients, this doc only 8. Upgrading to a non-backwards compatible version of IBC: **Unsupported**, as IBC version is negotiated on connection handshake. 9. Changing the Tendermint LightClient algorithm: **Partially Supported**. Changes to the light client algorithm that do not change the ClientState or ConsensusState struct may be supported, provided that the counterparty is also upgraded to support the new light client algorithm. Changes that require updating the ClientState and ConsensusState structs themselves are theoretically possible by providing a path to translate an older ClientState struct into the new ClientState struct; however this is not currently implemented. -### Step-by-Step Upgrade Process for SDK chains +## Step-by-Step Upgrade Process for SDK chains If the IBC-connected chain is conducting an upgrade that will break counterparty clients, it must ensure that the upgrade is first supported by IBC using the list above and then execute the upgrade process described below in order to prevent counterparty clients from breaking. 1. Create a 02-client [`UpgradeProposal`](https://github.com/cosmos/ibc-go/blob/main/docs/ibc/proto-docs.md#upgradeproposal) with an `UpgradePlan` and a new IBC ClientState in the `UpgradedClientState` field. Note that the `UpgradePlan` must specify an upgrade height **only** (no upgrade time), and the `ClientState` should only include the fields common to all valid clients and zero out any client-customizable fields (such as TrustingPeriod). -2. Vote on and pass the `UpgradeProposal` +2. Vote on and pass the `UpgradeProposal`. Upon the `UpgradeProposal` passing, the upgrade module will commit the UpgradedClient under the key: `upgrade/UpgradedIBCState/{upgradeHeight}/upgradedClient`. On the block right before the upgrade height, the upgrade module will also commit an initial consensus state for the next chain under the key: `upgrade/UpgradedIBCState/{upgradeHeight}/upgradedConsState`. Once the chain reaches the upgrade height and halts, a relayer can upgrade the counterparty clients to the last block of the old chain. They can then submit the proofs of the `UpgradedClient` and `UpgradedConsensusState` against this last block and upgrade the counterparty client. -### Step-by-Step Upgrade Process for Relayers Upgrading Counterparty Clients +## Step-by-Step Upgrade Process for Relayers Upgrading Counterparty Clients Once the upgrading chain has committed to upgrading, relayers must wait till the chain halts at the upgrade height before upgrading counterparty clients. This is because chains may reschedule or cancel upgrade plans before they occur. Thus, relayers must wait till the chain reaches the upgrade height and halts before they can be sure the upgrade will take place. diff --git a/docs/middleware/ics29-fee/fee-distribution.md b/docs/middleware/ics29-fee/fee-distribution.md index 79f03792566..f3e1efc2515 100644 --- a/docs/middleware/ics29-fee/fee-distribution.md +++ b/docs/middleware/ics29-fee/fee-distribution.md @@ -26,23 +26,23 @@ Fee distribution for incentivized packet relays takes place on the packet source 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.** -### Relayer operator actions? +### Relayer operator actions 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 { - // unique port identifier - PortId string - // unique channel identifier - ChannelId string - // the relayer address - Relayer string - // the counterparty payee address - CounterpartyPayee string + // unique port identifier + PortId string + // unique channel identifier + ChannelId string + // the relayer address + Relayer string + // the counterparty payee address + CounterpartyPayee string } ``` @@ -50,16 +50,16 @@ type MsgRegisterCounterpartyPayee struct { > > - `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)). +> - `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: ```bash simd tx ibc-fee register-counterparty-payee transfer channel-0 \ -cosmos1rsp837a4kvtgp2m4uqzdge0zzu6efqgucm0qdh \ -osmo1v5y0tz01llxzf4c2afml8s3awue0ymju22wxx2 \ ---from cosmos1rsp837a4kvtgp2m4uqzdge0zzu6efqgucm0qdh + cosmos1rsp837a4kvtgp2m4uqzdge0zzu6efqgucm0qdh \ + osmo1v5y0tz01llxzf4c2afml8s3awue0ymju22wxx2 \ + --from cosmos1rsp837a4kvtgp2m4uqzdge0zzu6efqgucm0qdh ``` ## Register an alternative payee address for reverse and timeout relaying @@ -80,14 +80,14 @@ Note: If a module account address is used as the `Payee` it is recommended to [t ```go type MsgRegisterPayee struct { - // unique port identifier - PortId string - // unique channel identifier - ChannelId string - // the relayer address - Relayer string - // the payee address - Payee string + // unique port identifier + PortId string + // unique channel identifier + ChannelId string + // the relayer address + Relayer string + // the payee address + Payee string } ``` @@ -95,14 +95,14 @@ type MsgRegisterPayee struct { > > - `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)). +> - `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: ```bash simd tx ibc-fee register-payee transfer channel-0 \ -cosmos1rsp837a4kvtgp2m4uqzdge0zzu6efqgucm0qdh \ -cosmos153lf4zntqt33a4v0sm5cytrxyqn78q7kz8j8x5 \ ---from cosmos1rsp837a4kvtgp2m4uqzdge0zzu6efqgucm0qdh + cosmos1rsp837a4kvtgp2m4uqzdge0zzu6efqgucm0qdh \ + cosmos153lf4zntqt33a4v0sm5cytrxyqn78q7kz8j8x5 \ + --from cosmos1rsp837a4kvtgp2m4uqzdge0zzu6efqgucm0qdh ``` diff --git a/docs/middleware/ics29-fee/integration.md b/docs/middleware/ics29-fee/integration.md index f1d0158e702..7912726327e 100644 --- a/docs/middleware/ics29-fee/integration.md +++ b/docs/middleware/ics29-fee/integration.md @@ -14,14 +14,97 @@ 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. +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. These layers form the complete set of application logic that enable developers to build composable and flexible IBC application stacks. For example, an application stack may be just a single base application like `transfer`, however, the same application stack composed with `29-fee` will nest the `transfer` base application by wrapping it with the Fee Middleware module. - ### Transfer See below for an example of how to create an application stack using `transfer` and `29-fee`. @@ -76,10 +159,10 @@ icaHostStack = ibcfee.NewIBCMiddleware(icaHostStack, app.IBCFeeKeeper) // Add authentication module, controller and host to IBC router ibcRouter. - // the ICA Controller middleware needs to be explicitly added to the IBC Router because the - // ICA controller module owns the port capability for ICA. The ICA authentication module - // owns the channel capability. - 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 + // the ICA Controller middleware needs to be explicitly added to the IBC Router because the + // ICA controller module owns the port capability for ICA. The ICA authentication module + // owns the channel capability. + 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). +``` diff --git a/docs/middleware/ics29-fee/msgs.md b/docs/middleware/ics29-fee/msgs.md index 0003d5982ee..1f2dde03841 100644 --- a/docs/middleware/ics29-fee/msgs.md +++ b/docs/middleware/ics29-fee/msgs.md @@ -10,61 +10,61 @@ Learn about the different ways to pay for fees, how the fees are paid out and wh 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. - - ```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 - } - ``` +### `MsgPayPacketFee` + +`MsgPayPacketFee` 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: +### `MsgPayPacketFeeAsync` - Note that a packet can be 'topped up' multiple times with additional fees of any coin denomination by broadcasting multiple `MsgPayPacketFeeAsync` messages. +`MsgPayPacketFeeAsync` 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. - ```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 - } - ``` +```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 +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 - } - ``` +```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. diff --git a/docs/migrations/migration-template.md b/docs/migrations/migration-template.md index 701b74290dd..dc546603012 100644 --- a/docs/migrations/migration-template.md +++ b/docs/migrations/migration-template.md @@ -1,15 +1,15 @@ # Migrating from to -This document is intended to highlight significant changes which may require more information than presented in the CHANGELOG. -Any changes that must be done by a user of ibc-go should be documented here. +This guide provides instructions for migrating to a new version of ibc-go. There are four sections based on the four potential user groups of this document: -- Chains -- IBC Apps -- Relayers -- IBC Light Clients -**Note:** ibc-go supports golang semantic versioning and therefore all imports must be updated to bump the version number on major releases. +- [Chains](#chains) +- [IBC Apps](#ibc-apps) +- [Relayers](#relayers) +- [IBC Light Clients](#ibc-light-clients) + +**Note:** ibc-go supports golang semantic versioning and therefore all imports must be updated on major version releases. ## Chains diff --git a/docs/migrations/sdk-to-v1.md b/docs/migrations/sdk-to-v1.md index d5c1993ea73..76868e356f4 100644 --- a/docs/migrations/sdk-to-v1.md +++ b/docs/migrations/sdk-to-v1.md @@ -1,26 +1,27 @@ # Migrating to ibc-go -This file contains information on how to migrate from the IBC module contained in the SDK 0.41.x and 0.42.x lines to the IBC module in the ibc-go repository based on the 0.44 SDK version. +This file contains information on how to migrate from the IBC module contained in the SDK 0.41.x and 0.42.x lines to the IBC module in the ibc-go repository based on the 0.44 SDK version. ## Import Changes The most obvious changes is import name changes. We need to change: + - applications -> apps - cosmos-sdk/x/ibc -> ibc-go On my GNU/Linux based machine I used the following commands, executed in order: -``` +```shell grep -RiIl 'cosmos-sdk\/x\/ibc\/applications' | xargs sed -i 's/cosmos-sdk\/x\/ibc\/applications/ibc-go\/modules\/apps/g' ``` -``` +```shell grep -RiIl 'cosmos-sdk\/x\/ibc' | xargs sed -i 's/cosmos-sdk\/x\/ibc/ibc-go\/modules/g' ``` ref: [explanation of the above commands](https://www.internalpointers.com/post/linux-find-and-replace-text-multiple-files) -Executing these commands out of order will cause issues. +Executing these commands out of order will cause issues. Feel free to use your own method for modifying import names. @@ -29,11 +30,12 @@ Update the import paths before running `go mod tidy`. ## Chain Upgrades -Chains may choose to upgrade via an upgrade proposal or genesis upgrades. Both in-place store migrations and genesis migrations are supported. +Chains may choose to upgrade via an upgrade proposal or genesis upgrades. Both in-place store migrations and genesis migrations are supported. **WARNING**: Please read at least the quick guide for [IBC client upgrades](../ibc/upgrades/README.md) before upgrading your chain. It is highly recommended you do not change the chain-ID during an upgrade, otherwise you must follow the IBC client upgrade instructions. Both in-place store migrations and genesis migrations will: + - migrate the solo machine client state from v1 to v2 protobuf definitions - prune all solo machine consensus states - prune all expired tendermint consensus states @@ -45,20 +47,21 @@ Chains must set a new connection parameter during either in place store migratio The new chain binary will need to run migrations in the upgrade handler. The fromVM (previous module version) for the IBC module should be 1. This will allow migrations to be run for IBC updating the version from 1 to 2. Ex: + ```go app.UpgradeKeeper.SetUpgradeHandler("my-upgrade-proposal", - func(ctx sdk.Context, _ upgradetypes.Plan, _ module.VersionMap) (module.VersionMap, error) { - // set max expected block time parameter. Replace the default with your expected value - // https://github.com/cosmos/ibc-go/blob/release/v1.0.x/docs/ibc/proto-docs.md#params-2 - app.IBCKeeper.ConnectionKeeper.SetParams(ctx, ibcconnectiontypes.DefaultParams()) - - fromVM := map[string]uint64{ - ... // other modules - "ibc": 1, - ... - } - return app.mm.RunMigrations(ctx, app.configurator, fromVM) - }) + func(ctx sdk.Context, _ upgradetypes.Plan, _ module.VersionMap) (module.VersionMap, error) { + // set max expected block time parameter. Replace the default with your expected value + // https://github.com/cosmos/ibc-go/blob/release/v1.0.x/docs/ibc/proto-docs.md#params-2 + app.IBCKeeper.ConnectionKeeper.SetParams(ctx, ibcconnectiontypes.DefaultParams()) + + fromVM := map[string]uint64{ + ... // other modules + "ibc": 1, + ... + } + return app.mm.RunMigrations(ctx, app.configurator, fromVM) + }) ``` @@ -69,8 +72,8 @@ To perform genesis migrations, the following code must be added to your existing ```go // add imports as necessary import ( - ibcv100 "github.com/cosmos/ibc-go/modules/core/legacy/v100" - ibchost "github.com/cosmos/ibc-go/modules/core/24-host" + ibcv100 "github.com/cosmos/ibc-go/modules/core/legacy/v100" + ibchost "github.com/cosmos/ibc-go/modules/core/24-host" ) ... @@ -80,77 +83,76 @@ import ( // https://github.com/cosmos/ibc-go/blob/release/v1.0.x/docs/ibc/proto-docs.md#params-2 newGenState, err = ibcv100.MigrateGenesis(newGenState, clientCtx, *genDoc, expectedTimePerBlock) if err != nil { - return err + return err } ``` **NOTE:** The genesis chain-id, time and height MUST be updated before migrating IBC, otherwise the tendermint consensus state will not be pruned. - ## IBC Keeper Changes The IBC Keeper now takes in the Upgrade Keeper. Please add the chains' Upgrade Keeper after the Staking Keeper: ```diff - // Create IBC Keeper - app.IBCKeeper = ibckeeper.NewKeeper( -- appCodec, keys[ibchost.StoreKey], app.GetSubspace(ibchost.ModuleName), app.StakingKeeper, scopedIBCKeeper, -+ appCodec, keys[ibchost.StoreKey], app.GetSubspace(ibchost.ModuleName), app.StakingKeeper, app.UpgradeKeeper, scopedIBCKeeper, - ) - -``` +// Create IBC Keeper +app.IBCKeeper = ibckeeper.NewKeeper( +- appCodec, keys[ibchost.StoreKey], app.GetSubspace(ibchost.ModuleName), app.StakingKeeper, scopedIBCKeeper, ++ appCodec, keys[ibchost.StoreKey], app.GetSubspace(ibchost.ModuleName), app.StakingKeeper, app.UpgradeKeeper, scopedIBCKeeper, +) +``` ## Proposals ### UpdateClientProposal -The `UpdateClient` has been modified to take in two client-identifiers and one initial height. Please see the [documentation](../ibc/proposals.md) for more information. +The `UpdateClient` has been modified to take in two client-identifiers and one initial height. Please see the [documentation](../ibc/proposals.md) for more information. ### UpgradeProposal -A new IBC proposal type has been added, `UpgradeProposal`. This handles an IBC (breaking) Upgrade. -The previous `UpgradedClientState` field in an Upgrade `Plan` has been deprecated in favor of this new proposal type. +A new IBC proposal type has been added, `UpgradeProposal`. This handles an IBC (breaking) Upgrade. +The previous `UpgradedClientState` field in an Upgrade `Plan` has been deprecated in favor of this new proposal type. ### Proposal Handler Registration -The `ClientUpdateProposalHandler` has been renamed to `ClientProposalHandler`. +The `ClientUpdateProposalHandler` has been renamed to `ClientProposalHandler`. It handles both `UpdateClientProposal`s and `UpgradeProposal`s. -Add this import: +Add this import: ```diff -+ ibcclienttypes "github.com/cosmos/ibc-go/modules/core/02-client/types" ++ ibcclienttypes "github.com/cosmos/ibc-go/modules/core/02-client/types" ``` Please ensure the governance module adds the correct route: ```diff -- AddRoute(ibchost.RouterKey, ibcclient.NewClientUpdateProposalHandler(app.IBCKeeper.ClientKeeper)) -+ AddRoute(ibcclienttypes.RouterKey, ibcclient.NewClientProposalHandler(app.IBCKeeper.ClientKeeper)) +- AddRoute(ibchost.RouterKey, ibcclient.NewClientUpdateProposalHandler(app.IBCKeeper.ClientKeeper)) ++ AddRoute(ibcclienttypes.RouterKey, ibcclient.NewClientProposalHandler(app.IBCKeeper.ClientKeeper)) ``` -NOTE: Simapp registration was incorrect in the 0.41.x releases. The `UpdateClient` proposal handler should be registered with the router key belonging to `ibc-go/core/02-client/types` -as shown in the diffs above. +NOTE: Simapp registration was incorrect in the 0.41.x releases. The `UpdateClient` proposal handler should be registered with the router key belonging to `ibc-go/core/02-client/types` +as shown in the diffs above. ### Proposal CLI Registration Please ensure both proposal type CLI commands are registered on the governance module by adding the following arguments to `gov.NewAppModuleBasic()`: Add the following import: + ```diff -+ ibcclientclient "github.com/cosmos/ibc-go/modules/core/02-client/client" ++ ibcclientclient "github.com/cosmos/ibc-go/modules/core/02-client/client" ``` -Register the cli commands: +Register the cli commands: -```diff - gov.NewAppModuleBasic( - paramsclient.ProposalHandler, distrclient.ProposalHandler, upgradeclient.ProposalHandler, upgradeclient.CancelProposalHandler, -+ ibcclientclient.UpdateClientProposalHandler, ibcclientclient.UpgradeProposalHandler, - ), +```diff +gov.NewAppModuleBasic( + paramsclient.ProposalHandler, distrclient.ProposalHandler, upgradeclient.ProposalHandler, upgradeclient.CancelProposalHandler, ++ ibcclientclient.UpdateClientProposalHandler, ibcclientclient.UpgradeProposalHandler, +), ``` -REST routes are not supported for these proposals. +REST routes are not supported for these proposals. ## Proto file changes @@ -162,11 +164,11 @@ The solo machine has replaced the FrozenSequence uint64 field with a IsFrozen bo ### OnRecvPacket -Application developers need to update their `OnRecvPacket` callback logic. +Application developers need to update their `OnRecvPacket` callback logic. The `OnRecvPacket` callback has been modified to only return the acknowledgement. The acknowledgement returned must implement the `Acknowledgement` interface. The acknowledgement should indicate if it represents a successful processing of a packet by returning true on `Success()` and false in all other cases. A return value of false on `Success()` will result in all state changes which occurred in the callback being discarded. More information can be found in the [documentation](https://github.com/cosmos/ibc-go/blob/main/docs/ibc/apps.md#receiving-packets). -The `OnRecvPacket`, `OnAcknowledgementPacket`, and `OnTimeoutPacket` callbacks are now passed the `sdk.AccAddress` of the relayer who relayed the IBC packet. Applications may use or ignore this information. +The `OnRecvPacket`, `OnAcknowledgementPacket`, and `OnTimeoutPacket` callbacks are now passed the `sdk.AccAddress` of the relayer who relayed the IBC packet. Applications may use or ignore this information. ## IBC Event changes @@ -178,9 +180,9 @@ The `consensus_height` attribute has been removed in the Misbehaviour event emit ## Relevant SDK changes -* (codec) [\#9226](https://github.com/cosmos/cosmos-sdk/pull/9226) Rename codec interfaces and methods, to follow a general Go interfaces: - * `codec.Marshaler` → `codec.Codec` (this defines objects which serialize other objects) - * `codec.BinaryMarshaler` → `codec.BinaryCodec` - * `codec.JSONMarshaler` → `codec.JSONCodec` - * Removed `BinaryBare` suffix from `BinaryCodec` methods (`MarshalBinaryBare`, `UnmarshalBinaryBare`, ...) - * Removed `Binary` infix from `BinaryCodec` methods (`MarshalBinaryLengthPrefixed`, `UnmarshalBinaryLengthPrefixed`, ...) +- (codec) [\#9226](https://github.com/cosmos/cosmos-sdk/pull/9226) Rename codec interfaces and methods, to follow a general Go interfaces: + - `codec.Marshaler` → `codec.Codec` (this defines objects which serialize other objects) + - `codec.BinaryMarshaler` → `codec.BinaryCodec` + - `codec.JSONMarshaler` → `codec.JSONCodec` + - Removed `BinaryBare` suffix from `BinaryCodec` methods (`MarshalBinaryBare`, `UnmarshalBinaryBare`, ...) + - Removed `Binary` infix from `BinaryCodec` methods (`MarshalBinaryLengthPrefixed`, `UnmarshalBinaryLengthPrefixed`, ...) diff --git a/docs/migrations/support-denoms-with-slashes.md b/docs/migrations/support-denoms-with-slashes.md index 3bc3d1b6b83..3c1bf2c253f 100644 --- a/docs/migrations/support-denoms-with-slashes.md +++ b/docs/migrations/support-denoms-with-slashes.md @@ -4,6 +4,7 @@ This document is intended to highlight significant changes which may require mor Any changes that must be done by a user of ibc-go should be documented here. There are four sections based on the four potential user groups of this document: + - Chains - IBC Apps - Relayers @@ -29,11 +30,10 @@ The transfer module will now support slashes in base denoms, so we must iterate ```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) - }) - + 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) + }) ``` 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. @@ -48,33 +48,33 @@ The migration code required may look like: ```go func migrateGenesisSlashedDenomsUpgrade(appState genutiltypes.AppMap, clientCtx client.Context, genDoc *tmtypes.GenesisDoc) (genutiltypes.AppMap, error) { - if appState[ibctransfertypes.ModuleName] != nil { - transferGenState := &ibctransfertypes.GenesisState{} - clientCtx.Codec.MustUnmarshalJSON(appState[ibctransfertypes.ModuleName], transferGenState) + if appState[ibctransfertypes.ModuleName] != nil { + transferGenState := &ibctransfertypes.GenesisState{} + clientCtx.Codec.MustUnmarshalJSON(appState[ibctransfertypes.ModuleName], transferGenState) - substituteTraces := make([]ibctransfertypes.DenomTrace, len(transferGenState.DenomTraces)) - for i, dt := range transferGenState.DenomTraces { - // replace all previous traces with the latest trace if validation passes - // note most traces will have same value - newTrace := ibctransfertypes.ParseDenomTrace(dt.GetFullDenomPath()) + substituteTraces := make([]ibctransfertypes.DenomTrace, len(transferGenState.DenomTraces)) + for i, dt := range transferGenState.DenomTraces { + // replace all previous traces with the latest trace if validation passes + // note most traces will have same value + newTrace := ibctransfertypes.ParseDenomTrace(dt.GetFullDenomPath()) - if err := newTrace.Validate(); err != nil { - substituteTraces[i] = dt - } else { - substituteTraces[i] = newTrace - } - } + if err := newTrace.Validate(); err != nil { + substituteTraces[i] = dt + } else { + substituteTraces[i] = newTrace + } + } - transferGenState.DenomTraces = substituteTraces + transferGenState.DenomTraces = substituteTraces - // delete old genesis state - delete(appState, ibctransfertypes.ModuleName) + // delete old genesis state + delete(appState, ibctransfertypes.ModuleName) - // set new ibc transfer genesis state - appState[ibctransfertypes.ModuleName] = clientCtx.Codec.MustMarshalJSON(transferGenState) - } + // set new ibc transfer genesis state + appState[ibctransfertypes.ModuleName] = clientCtx.Codec.MustMarshalJSON(transferGenState) + } - return appState, nil + return appState, nil } ``` diff --git a/docs/migrations/v1-to-v2.md b/docs/migrations/v1-to-v2.md index a42da400e43..bef0254ccbb 100644 --- a/docs/migrations/v1-to-v2.md +++ b/docs/migrations/v1-to-v2.md @@ -4,12 +4,14 @@ This document is intended to highlight significant changes which may require mor Any changes that must be done by a user of ibc-go should be documented here. There are four sections based on the four potential user groups of this document: + - Chains - IBC Apps - Relayers - IBC Light Clients **Note:** ibc-go supports golang semantic versioning and therefore all imports must be updated to bump the version number on major releases. + ```go github.com/cosmos/ibc-go -> github.com/cosmos/ibc-go/v2 ``` @@ -21,26 +23,26 @@ github.com/cosmos/ibc-go -> github.com/cosmos/ibc-go/v2 ## IBC Apps A new function has been added to the app module interface: + ```go // NegotiateAppVersion performs application version negotiation given the provided channel ordering, connectionID, portID, counterparty and proposed version. - // An error is returned if version negotiation cannot be performed. For example, an application module implementing this interface - // may decide to return an error in the event of the proposed version being incompatible with it's own - NegotiateAppVersion( - ctx sdk.Context, - order channeltypes.Order, - connectionID string, - portID string, - counterparty channeltypes.Counterparty, - proposedVersion string, - ) (version string, err error) -} +// An error is returned if version negotiation cannot be performed. For example, an application module implementing this interface +// may decide to return an error in the event of the proposed version being incompatible with it's own +NegotiateAppVersion( + ctx sdk.Context, + order channeltypes.Order, + connectionID string, + portID string, + counterparty channeltypes.Counterparty, + proposedVersion string, +) (version string, err error) ``` This function should perform application version negotiation and return the negotiated version. If the version cannot be negotiated, an error should be returned. This function is only used on the client side. -#### sdk.Result removed +### `sdk.Result` removed -sdk.Result has been removed as a return value in the application callbacks. Previously it was being discarded by core IBC and was thus unused. +`sdk.Result` has been removed as a return value in the application callbacks. Previously it was being discarded by core IBC and was thus unused. ## Relayers diff --git a/docs/migrations/v2-to-v3.md b/docs/migrations/v2-to-v3.md index a37f74cf420..75586cefe59 100644 --- a/docs/migrations/v2-to-v3.md +++ b/docs/migrations/v2-to-v3.md @@ -4,12 +4,14 @@ This document is intended to highlight significant changes which may require mor Any changes that must be done by a user of ibc-go should be documented here. There are four sections based on the four potential user groups of this document: + - Chains - IBC Apps - Relayers - IBC Light Clients **Note:** ibc-go supports golang semantic versioning and therefore all imports must be updated to bump the version number on major releases. + ```go github.com/cosmos/ibc-go/v2 -> github.com/cosmos/ibc-go/v3 ``` @@ -20,7 +22,7 @@ No genesis or in-place migrations are required when upgrading from v1 or v2 of i ### ICS20 -The `transferkeeper.NewKeeper(...)` now takes in an ICS4Wrapper. +The `transferkeeper.NewKeeper(...)` now takes in an ICS4Wrapper. The ICS4Wrapper should be the IBC Channel Keeper unless ICS 20 is being connected to a middleware application. ### ICS27 @@ -30,80 +32,79 @@ Please see the [ICS27 documentation](../apps/interchain-accounts/overview.md) fo ### Upgrade Proposal -If the chain will adopt ICS27, it must set the appropriate params during the execution of the upgrade handler in `app.go`: +If the chain will adopt ICS27, it must set the appropriate params during the execution of the upgrade handler in `app.go`: + ```go app.UpgradeKeeper.SetUpgradeHandler("v3", - func(ctx sdk.Context, _ upgradetypes.Plan, fromVM module.VersionMap) (module.VersionMap, error) { - // set the ICS27 consensus version so InitGenesis is not run - fromVM[icatypes.ModuleName] = icamodule.ConsensusVersion() - - // create ICS27 Controller submodule params - controllerParams := icacontrollertypes.Params{ - ControllerEnabled: true, - } - - // create ICS27 Host submodule params - hostParams := icahosttypes.Params{ - HostEnabled: true, - AllowMessages: []string{"/cosmos.bank.v1beta1.MsgSend", ...}, - } - - // initialize ICS27 module - icamodule.InitModule(ctx, controllerParams, hostParams) - - ... - - return app.mm.RunMigrations(ctx, app.configurator, fromVM) - }) + func(ctx sdk.Context, _ upgradetypes.Plan, fromVM module.VersionMap) (module.VersionMap, error) { + // set the ICS27 consensus version so InitGenesis is not run + fromVM[icatypes.ModuleName] = icamodule.ConsensusVersion() + + // create ICS27 Controller submodule params + controllerParams := icacontrollertypes.Params{ + ControllerEnabled: true, + } + // create ICS27 Host submodule params + hostParams := icahosttypes.Params{ + HostEnabled: true, + AllowMessages: []string{"/cosmos.bank.v1beta1.MsgSend", ...}, + } + + // initialize ICS27 module + icamodule.InitModule(ctx, controllerParams, hostParams) + + ... + + return app.mm.RunMigrations(ctx, app.configurator, fromVM) + }) ``` -The host and controller submodule params only need to be set if the chain integrates those submodules. +The host and controller submodule params only need to be set if the chain integrates those submodules. For example, if a chain chooses not to integrate a controller submodule, it may pass empty params into `InitModule`. #### 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) { - storeUpgrades := store.StoreUpgrades{ - Added: []string{icacontrollertypes.StoreKey, icahosttypes.StoreKey}, - } + storeUpgrades := store.StoreUpgrades{ + Added: []string{icacontrollertypes.StoreKey, icahosttypes.StoreKey}, + } - app.SetStoreLoader(upgradetypes.UpgradeStoreLoader(upgradeInfo.Height, &storeUpgrades)) + app.SetStoreLoader(upgradetypes.UpgradeStoreLoader(upgradeInfo.Height, &storeUpgrades)) } ``` -This ensures that the new module's stores are added to the multistore before the migrations begin. +This ensures that the new module's stores are added to the multistore before the migrations begin. The host and controller submodule keys only need to be added if the chain integrates those submodules. For example, if a chain chooses not to integrate a controller submodule, it does not need to add the controller key to the `Added` field. - ### Genesis migrations -If the chain will adopt ICS27 and chooses to upgrade via a genesis export, then the ICS27 parameters must be set during genesis migration. +If the chain will adopt ICS27 and chooses to upgrade via a genesis export, then the ICS27 parameters must be set during genesis migration. The migration code required may look like: ```go - controllerGenesisState := icatypes.DefaultControllerGenesis() - // overwrite parameters as desired - controllerGenesisState.Params = icacontrollertypes.Params{ - ControllerEnabled: true, - } - - hostGenesisState := icatypes.DefaultHostGenesis() - // overwrite parameters as desired - hostGenesisState.Params = icahosttypes.Params{ - HostEnabled: true, - AllowMessages: []string{"/cosmos.bank.v1beta1.MsgSend", ...}, - } - - icaGenesisState := icatypes.NewGenesisState(controllerGenesisState, hostGenesisState) - - // set new ics27 genesis state - appState[icatypes.ModuleName] = clientCtx.JSONCodec.MustMarshalJSON(icaGenesisState) + controllerGenesisState := icatypes.DefaultControllerGenesis() + // overwrite parameters as desired + controllerGenesisState.Params = icacontrollertypes.Params{ + ControllerEnabled: true, + } + + hostGenesisState := icatypes.DefaultHostGenesis() + // overwrite parameters as desired + hostGenesisState.Params = icahosttypes.Params{ + HostEnabled: true, + AllowMessages: []string{"/cosmos.bank.v1beta1.MsgSend", ...}, + } + + icaGenesisState := icatypes.NewGenesisState(controllerGenesisState, hostGenesisState) + + // set new ics27 genesis state + appState[icatypes.ModuleName] = clientCtx.Codec.MustMarshalJSON(icaGenesisState) ``` ### Ante decorator @@ -112,28 +113,28 @@ The field of type `channelkeeper.Keeper` in the `AnteDecorator` structure has be ```diff type AnteDecorator struct { -- k channelkeeper.Keeper -+ k *keeper.Keeper +- k channelkeeper.Keeper ++ k *keeper.Keeper } - func NewAnteDecorator(k channelkeeper.Keeper) AnteDecorator { -+ func NewAnteDecorator(k *keeper.Keeper) AnteDecorator { - return AnteDecorator{k: k} ++ func NewAnteDecorator(k *keeper.Keeper) AnteDecorator { + return AnteDecorator{k: k} } ``` ## IBC Apps - ### `OnChanOpenTry` must return negotiated application version The `OnChanOpenTry` application callback has been modified. -The return signature now includes the application version. -IBC applications must perform application version negoitation in `OnChanOpenTry` using the counterparty version. +The return signature now includes the application version. +IBC applications must perform application version negoitation in `OnChanOpenTry` using the counterparty version. The negotiated application version then must be returned in `OnChanOpenTry` to core IBC. Core IBC will set this version in the TRYOPEN channel. ### `OnChanOpenAck` will take additional `counterpartyChannelID` argument + The `OnChanOpenAck` application callback has been modified. The arguments now include the counterparty channel id. @@ -146,17 +147,17 @@ Now applications will perform this version negotiation during the channel handsh ### Channel state will not be set before application callback -The channel handshake logic has been reorganized within core IBC. +The channel handshake logic has been reorganized within core IBC. Channel state will not be set in state after the application callback is performed. Applications must rely only on the passed in channel parameters instead of querying the channel keeper for channel state. ### IBC application callbacks moved from `AppModule` to `IBCModule` -Previously, IBC module callbacks were apart of the `AppModule` type. -The recommended approach is to create an `IBCModule` type and move the IBC module callbacks from `AppModule` to `IBCModule` in a separate file `ibc_module.go`. +Previously, IBC module callbacks were apart of the `AppModule` type. +The recommended approach is to create an `IBCModule` type and move the IBC module callbacks from `AppModule` to `IBCModule` in a separate file `ibc_module.go`. -The mock module go API has been broken in this release by applying the above format. -The IBC module callbacks have been moved from the mock modules `AppModule` into a new type `IBCModule`. +The mock module go API has been broken in this release by applying the above format. +The IBC module callbacks have been moved from the mock modules `AppModule` into a new type `IBCModule`. As apart of this release, the mock module now supports middleware testing. Please see the [README](../../testing/README.md#middleware-testing) for more information. @@ -164,16 +165,15 @@ Please review the [mock](../../testing/mock/ibc_module.go) and [transfer](../../ ### IBC testing package -`TestChain`s are now created with chainID's beginning from an index of 1. Any calls to `GetChainID(0)` will now fail. Please increment all calls to `GetChainID` by 1. +`TestChain`s are now created with chainID's beginning from an index of 1. Any calls to `GetChainID(0)` will now fail. Please increment all calls to `GetChainID` by 1. ## Relayers `AppVersion` gRPC has been removed. -The `version` string in `MsgChanOpenTry` has been deprecated and will be ignored by core IBC. +The `version` string in `MsgChanOpenTry` has been deprecated and will be ignored by core IBC. Relayers no longer need to determine the version to use on the `ChanOpenTry` step. -IBC applications will determine the correct version using the counterparty version. +IBC applications will determine the correct version using the counterparty version. ## IBC Light Clients -The `GetProofSpecs` function has been removed from the `ClientState` interface. This function was previously unused by core IBC. Light clients which don't use this function may remove it. - +The `GetProofSpecs` function has been removed from the `ClientState` interface. This function was previously unused by core IBC. Light clients which don't use this function may remove it. diff --git a/docs/migrations/v3-to-v4.md b/docs/migrations/v3-to-v4.md index bdf8654dcb7..81cf97b3521 100644 --- a/docs/migrations/v3-to-v4.md +++ b/docs/migrations/v3-to-v4.md @@ -4,12 +4,14 @@ This document is intended to highlight significant changes which may require mor Any changes that must be done by a user of ibc-go should be documented here. There are four sections based on the four potential user groups of this document: + - Chains - IBC Apps - Relayers - IBC Light Clients **Note:** ibc-go supports golang semantic versioning and therefore all imports must be updated to bump the version number on major releases. + ```go github.com/cosmos/ibc-go/v3 -> github.com/cosmos/ibc-go/v4 ``` @@ -18,20 +20,39 @@ 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`: +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) - }) - + 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. @@ -44,13 +65,15 @@ This incorrect trace information must be corrected when the chain does upgrade t ### ICS03 - Connection -Crossing hellos have been removed from 03-connection handshake negotiation. +Crossing hellos have been removed from 03-connection handshake negotiation. `PreviousConnectionId` in `MsgConnectionOpenTry` has been deprecated and is no longer used by core IBC. -### ICS04 - Channel +`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. -The `WriteAcknowledgement` API now takes the `exported.Acknowledgement` type instead of passing in the acknowledgement byte array directly. -This is an API breaking change and as such IBC application developers will have to update any calls to `WriteAcknowledgement`. +### ICS04 - Channel + +The `WriteAcknowledgement` API now takes the `exported.Acknowledgement` type instead of passing in the acknowledgement byte array directly. +This is an API breaking change and as such IBC application developers will have to update any calls to `WriteAcknowledgement`. The `OnChanOpenInit` application callback has been modified. The return signature now includes the application version as detailed in the latest IBC [spec changes](https://github.com/cosmos/ibc/pull/629). @@ -59,34 +82,36 @@ 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. +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. -Consumers of the `RegisterInterchainAccount` are now expected to build the appropriate JSON encoded version string themselves and pass it accordingly. +Consumers of the `RegisterInterchainAccount` are now expected to build the appropriate JSON encoded version string themselves and pass it accordingly. This should be constructed within the interchain accounts authentication module which leverages the APIs exposed via the interchain accounts `controllerKeeper`. If an empty string is passed in the `version` argument, then the version will be initialized to a default value in the `OnChanOpenInit` callback of the controller's handler, so that channel handshake can proceed. The following code snippet illustrates how to construct an appropriate interchain accounts `Metadata` and encode it as a JSON bytestring: ```go icaMetadata := icatypes.Metadata{ - Version: icatypes.Version, - ControllerConnectionId: controllerConnectionID, - HostConnectionId: hostConnectionID, - Encoding: icatypes.EncodingProtobuf, - TxType: icatypes.TxTypeSDKMultiMsg, + Version: icatypes.Version, + ControllerConnectionId: controllerConnectionID, + HostConnectionId: hostConnectionID, + Encoding: icatypes.EncodingProtobuf, + TxType: icatypes.TxTypeSDKMultiMsg, } appVersion, err := icatypes.ModuleCdc.MarshalJSON(&icaMetadata) if err != nil { - return err + return err } if err := k.icaControllerKeeper.RegisterInterchainAccount(ctx, msg.ConnectionId, msg.Owner, string(appVersion)); err != nil { - return err + return err } ``` @@ -94,30 +119,30 @@ Similarly, if the application stack is configured to route through ICS29 fee mid ```go icaMetadata := icatypes.Metadata{ - Version: icatypes.Version, - ControllerConnectionId: controllerConnectionID, - HostConnectionId: hostConnectionID, - Encoding: icatypes.EncodingProtobuf, - TxType: icatypes.TxTypeSDKMultiMsg, + Version: icatypes.Version, + ControllerConnectionId: controllerConnectionID, + HostConnectionId: hostConnectionID, + Encoding: icatypes.EncodingProtobuf, + TxType: icatypes.TxTypeSDKMultiMsg, } appVersion, err := icatypes.ModuleCdc.MarshalJSON(&icaMetadata) if err != nil { - return err + return err } feeMetadata := feetypes.Metadata{ - AppVersion: string(appVersion), - FeeVersion: feetypes.Version, + AppVersion: string(appVersion), + FeeVersion: feetypes.Version, } feeEnabledVersion, err := feetypes.ModuleCdc.MarshalJSON(&feeMetadata) if err != nil { - return err + return err } if err := k.icaControllerKeeper.RegisterInterchainAccount(ctx, msg.ConnectionId, msg.Owner, string(feeEnabledVersion)); err != nil { - return err + return err } ``` diff --git a/docs/migrations/v4-to-v5.md b/docs/migrations/v4-to-v5.md new file mode 100644 index 00000000000..f6fb7e61bb8 --- /dev/null +++ b/docs/migrations/v4-to-v5.md @@ -0,0 +1,434 @@ +# Migrating from v4 to v5 + +This document is intended to highlight significant changes which may require more information than presented in the CHANGELOG. +Any changes that must be done by a user of ibc-go should be documented here. + +There are four sections based on the four potential user groups of this document: + +- [Chains](#chains) +- [IBC Apps](#ibc-apps) +- [Relayers](#relayers) +- [IBC Light Clients](#ibc-light-clients) + +**Note:** ibc-go supports golang semantic versioning and therefore all imports must be updated to bump the version number on major releases. + +```go +github.com/cosmos/ibc-go/v4 -> github.com/cosmos/ibc-go/v5 +``` + +## Chains + +### Ante decorator + +The `AnteDecorator` type in `core/ante` has been renamed to `RedundantRelayDecorator` (and the corresponding constructor function to `NewRedundantRelayDecorator`). Therefore in the function that creates the instance of the `sdk.AnteHandler` type (e.g. `NewAnteHandler`) the change would be like this: + +```diff +func NewAnteHandler(options HandlerOptions) (sdk.AnteHandler, error) { + // parameter validation + + anteDecorators := []sdk.AnteDecorator{ + // other ante decorators +- ibcante.NewAnteDecorator(opts.IBCkeeper), ++ ibcante.NewRedundantRelayDecorator(options.IBCKeeper), + } + + return sdk.ChainAnteDecorators(anteDecorators...), nil +} +``` + +The `AnteDecorator` was actually renamed twice, but in [this PR](https://github.com/cosmos/ibc-go/pull/1820) you can see the changes made for the final rename. + +## IBC Apps + +### Core + +The `key` parameter of the `NewKeeper` function in `modules/core/keeper` is now of type `storetypes.StoreKey` (where `storetypes` is an import alias for `"github.com/cosmos/cosmos-sdk/store/types"`): + +```diff +func NewKeeper( + cdc codec.BinaryCodec, +- key sdk.StoreKey, ++ key storetypes.StoreKey, + paramSpace paramtypes.Subspace, + stakingKeeper clienttypes.StakingKeeper, + upgradeKeeper clienttypes.UpgradeKeeper, + scopedKeeper capabilitykeeper.ScopedKeeper, +) *Keeper +``` + +The `RegisterRESTRoutes` function in `modules/core` has been removed. + +### ICS03 - Connection + +The `key` parameter of the `NewKeeper` function in `modules/core/03-connection/keeper` is now of type `storetypes.StoreKey` (where `storetypes` is an import alias for `"github.com/cosmos/cosmos-sdk/store/types"`): + +```diff +func NewKeeper( + cdc codec.BinaryCodec, +- key sdk.StoreKey, ++ key storetypes.StoreKey, + paramSpace paramtypes.Subspace, + ck types.ClientKeeper +) Keeper +``` + +### ICS04 - Channel + +The function `NewPacketId` in `modules/core/04-channel/types` has been renamed to `NewPacketID`: + +```diff +- func NewPacketId( ++ func NewPacketID( + portID, + channelID string, + seq uint64 +) PacketId +``` + +The `key` parameter of the `NewKeeper` function in `modules/core/04-channel/keeper` is now of type `storetypes.StoreKey` (where `storetypes` is an import alias for `"github.com/cosmos/cosmos-sdk/store/types"`): + +```diff +func NewKeeper( + cdc codec.BinaryCodec, +- key sdk.StoreKey, ++ key storetypes.StoreKey, + clientKeeper types.ClientKeeper, + connectionKeeper types.ConnectionKeeper, + portKeeper types.PortKeeper, + scopedKeeper capabilitykeeper.ScopedKeeper, +) Keeper +``` + +### ICS20 - Transfer + +The `key` parameter of the `NewKeeper` function in `modules/apps/transfer/keeper` is now of type `storetypes.StoreKey` (where `storetypes` is an import alias for `"github.com/cosmos/cosmos-sdk/store/types"`): + +```diff +func NewKeeper( + cdc codec.BinaryCodec, +- key sdk.StoreKey, ++ key storetypes.StoreKey, + paramSpace paramtypes.Subspace, + ics4Wrapper types.ICS4Wrapper, + channelKeeper types.ChannelKeeper, + portKeeper types.PortKeeper, + authKeeper types.AccountKeeper, + bankKeeper types.BankKeeper, + scopedKeeper capabilitykeeper.ScopedKeeper, +) Keeper +``` + +The `amount` parameter of function `GetTransferCoin` in `modules/apps/transfer/types` is now of type `math.Int` (`"cosmossdk.io/math"`): + +```diff +func GetTransferCoin( + portID, channelID, baseDenom string, +- amount sdk.Int ++ amount math.Int +) sdk.Coin +``` + +The `RegisterRESTRoutes` function in `modules/apps/transfer` has been removed. + +### ICS27 - Interchain Accounts + +The `key` and `msgRouter` parameters of the `NewKeeper` functions in + +- `modules/apps/27-interchain-accounts/controller/keeper` +- and `modules/apps/27-interchain-accounts/host/keeper` + +have changed type. The `key` parameter is now of type `storetypes.StoreKey` (where `storetypes` is an import alias for `"github.com/cosmos/cosmos-sdk/store/types"`), and the `msgRouter` parameter is now of type `*icatypes.MessageRouter` (where `icatypes` is an import alias for `"github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/types"`): + +```diff +// NewKeeper creates a new interchain accounts controller Keeper instance +func NewKeeper( + cdc codec.BinaryCodec, +- key sdk.StoreKey, ++ key storetypes.StoreKey, + paramSpace paramtypes.Subspace, + ics4Wrapper icatypes.ICS4Wrapper, + channelKeeper icatypes.ChannelKeeper, + portKeeper icatypes.PortKeeper, + scopedKeeper capabilitykeeper.ScopedKeeper, +- msgRouter *baseapp.MsgServiceRouter, ++ msgRouter *icatypes.MessageRouter, +) Keeper +``` + +```diff +// NewKeeper creates a new interchain accounts host Keeper instance +func NewKeeper( + cdc codec.BinaryCodec, +- key sdk.StoreKey, ++ key storetypes.StoreKey, + paramSpace paramtypes.Subspace, + channelKeeper icatypes.ChannelKeeper, + portKeeper icatypes.PortKeeper, + accountKeeper icatypes.AccountKeeper, + scopedKeeper capabilitykeeper.ScopedKeeper, +- msgRouter *baseapp.MsgServiceRouter, ++ msgRouter *icatypes.MessageRouter, +) Keeper +``` + +The new `MessageRouter` interface is defined as: + +```go +type MessageRouter interface { + Handler(msg sdk.Msg) baseapp.MsgServiceHandler +} +``` + +The `RegisterRESTRoutes` function in `modules/apps/27-interchain-accounts` has been removed. + +An additional parameter, `ics4Wrapper` has been added to the `host` submodule `NewKeeper` function in `modules/apps/27-interchain-accounts/host/keeper`. +This allows the `host` submodule to correctly unwrap the channel version for channel reopening handshakes in the `OnChanOpenTry` callback. + +```diff +func NewKeeper( + cdc codec.BinaryCodec, + key storetypes.StoreKey, + paramSpace paramtypes.Subspace, ++ ics4Wrapper icatypes.ICS4Wrapper, + channelKeeper icatypes.ChannelKeeper, + portKeeper icatypes.PortKeeper, + accountKeeper icatypes.AccountKeeper, + scopedKeeper icatypes.ScopedKeeper, + msgRouter icatypes.MessageRouter, +) Keeper +``` + +#### Cosmos SDK message handler responses in packet acknowledgement + +The construction of the transaction response of a message execution on the host chain has changed. The `Data` field in the `sdk.TxMsgData` has been deprecated and since Cosmos SDK 0.46 the `MsgResponses` field contains the message handler responses packed into `Any`s. + +For chains on Cosmos SDK 0.45 and below, the message response was constructed like this: + +```go +txMsgData := &sdk.TxMsgData{ + Data: make([]*sdk.MsgData, len(msgs)), +} + +for i, msg := range msgs { + // message validation + + msgResponse, err := k.executeMsg(cacheCtx, msg) + // return if err != nil + + txMsgData.Data[i] = &sdk.MsgData{ + MsgType: sdk.MsgTypeURL(msg), + Data: msgResponse, + } +} + +// emit events + +txResponse, err := proto.Marshal(txMsgData) +// return if err != nil + +return txResponse, nil +``` + +And for chains on Cosmos SDK 0.46 and above, it is now done like this: + +```go +txMsgData := &sdk.TxMsgData{ + MsgResponses: make([]*codectypes.Any, len(msgs)), +} + +for i, msg := range msgs { + // message validation + + protoAny, err := k.executeMsg(cacheCtx, msg) + // return if err != nil + + txMsgData.MsgResponses[i] = protoAny +} + +// emit events + +txResponse, err := proto.Marshal(txMsgData) +// return if err != nil + +return txResponse, nil +``` + +When handling the acknowledgement in the `OnAcknowledgementPacket` callback of a custom ICA controller module, then depending on whether `txMsgData.Data` is empty or not, the logic to handle the message handler response will be different. **Only controller chains on Cosmos SDK 0.46 or above will be able to write the logic needed to handle the response from a host chain on Cosmos SDK 0.46 or above.** + +```go +var ack channeltypes.Acknowledgement +if err := channeltypes.SubModuleCdc.UnmarshalJSON(acknowledgement, &ack); err != nil { + return err +} + +var txMsgData sdk.TxMsgData +if err := proto.Unmarshal(ack.GetResult(), txMsgData); err != nil { + return err +} + +switch len(txMsgData.Data) { +case 0: // for SDK 0.46 and above + for _, msgResponse := range txMsgData.MsgResponses { + // unmarshall msgResponse and execute logic based on the response + } + return nil +default: // for SDK 0.45 and below + for _, msgData := range txMsgData.Data { + // unmarshall msgData and execute logic based on the response + } +} +``` + +See [ADR-03](../architecture/adr-003-ics27-acknowledgement.md/#next-major-version-format) for more information or the [corrresponding documentation about authentication modules](../apps/interchain-accounts/auth-modules.md#onacknowledgementpacket). + +### ICS29 - Fee Middleware + +The `key` parameter of the `NewKeeper` function in `modules/apps/29-fee` is now of type `storetypes.StoreKey` (where `storetypes` is an import alias for `"github.com/cosmos/cosmos-sdk/store/types"`): + +```diff +func NewKeeper( + cdc codec.BinaryCodec, +- key sdk.StoreKey, ++ key storetypes.StoreKey, + paramSpace paramtypes.Subspace, + ics4Wrapper types.ICS4Wrapper, + channelKeeper types.ChannelKeeper, + portKeeper types.PortKeeper, + authKeeper types.AccountKeeper, + bankKeeper types.BankKeeper, +) Keeper +``` + +The `RegisterRESTRoutes` function in `modules/apps/29-fee` has been removed. + +### IBC testing package + +The `MockIBCApp` type has been renamed to `IBCApp` (and the corresponding constructor function to `NewIBCApp`). This has resulted therefore in: + +- The `IBCApp` field of the `*IBCModule` in `testing/mock` to change its type as well to `*IBCApp`: + +```diff +type IBCModule struct { + appModule *AppModule +- IBCApp *MockIBCApp // base application of an IBC middleware stack ++ IBCApp *IBCApp // base application of an IBC middleware stack +} +``` + +- The `app` parameter to `*NewIBCModule` in `testing/mock` to change its type as well to `*IBCApp`: + +```diff +func NewIBCModule( + appModule *AppModule, +- app *MockIBCApp ++ app *IBCApp +) IBCModule +``` + +The `MockEmptyAcknowledgement` type has been renamed to `EmptyAcknowledgement` (and the corresponding constructor function to `NewEmptyAcknowledgement`). + +The `TestingApp` interface in `testing` has gone through some modifications: + +- The return type of the function `GetStakingKeeper` is not the concrete type `stakingkeeper.Keeper` anymore (where `stakingkeeper` is an import alias for `"github.com/cosmos/cosmos-sdk/x/staking/keeper"`), but it has been changed to the interface `ibctestingtypes.StakingKeeper` (where `ibctestingtypes` is an import alias for `""github.com/cosmos/ibc-go/v5/testing/types"`). See this [PR](https://github.com/cosmos/ibc-go/pull/2028) for more details. The `StakingKeeper` interface is defined as: + +```go +type StakingKeeper interface { + GetHistoricalInfo(ctx sdk.Context, height int64) (stakingtypes.HistoricalInfo, bool) +} +``` + +- The return type of the function `LastCommitID` has changed to `storetypes.CommitID` (where `storetypes` is an import alias for `"github.com/cosmos/cosmos-sdk/store/types"`). + +See the following `git diff` for more details: + +```diff +type TestingApp interface { + abci.Application + + // ibc-go additions + GetBaseApp() *baseapp.BaseApp +- GetStakingKeeper() stakingkeeper.Keeper ++ GetStakingKeeper() ibctestingtypes.StakingKeeper + GetIBCKeeper() *keeper.Keeper + GetScopedIBCKeeper() capabilitykeeper.ScopedKeeper + GetTxConfig() client.TxConfig + + // Implemented by SimApp + AppCodec() codec.Codec + + // Implemented by BaseApp +- LastCommitID() sdk.CommitID ++ LastCommitID() storetypes.CommitID + LastBlockHeight() int64 +} +``` + +The `powerReduction` parameter of the function `SetupWithGenesisValSet` in `testing` is now of type `math.Int` (`"cosmossdk.io/math"`): + +```diff +func SetupWithGenesisValSet( + t *testing.T, + valSet *tmtypes.ValidatorSet, + genAccs []authtypes.GenesisAccount, + chainID string, +- powerReduction sdk.Int, ++ powerReduction math.Int, + balances ...banktypes.Balance +) TestingApp +``` + +The `accAmt` parameter of the functions + +- `AddTestAddrsFromPubKeys` , +- `AddTestAddrs` +- and `AddTestAddrsIncremental` + +in `testing/simapp` are now of type `math.Int` (`"cosmossdk.io/math"`): + +```diff +func AddTestAddrsFromPubKeys( + app *SimApp, + ctx sdk.Context, + pubKeys []cryptotypes.PubKey, +- accAmt sdk.Int, ++ accAmt math.Int +) +func addTestAddrs( + app *SimApp, + ctx sdk.Context, + accNum int, +- accAmt sdk.Int, ++ accAmt math.Int, + strategy GenerateAccountStrategy +) []sdk.AccAddress +func AddTestAddrsIncremental( + app *SimApp, + ctx sdk.Context, + accNum int, +- accAmt sdk.Int, ++ accAmt math.Int +) []sdk.AccAddress +``` + +The `RegisterRESTRoutes` function in `testing/mock` has been removed. + +## Relayers + +- No relevant changes were made in this release. + +## IBC Light Clients + +### ICS02 - Client + +The `key` parameter of the `NewKeeper` function in `modules/core/02-client/keeper` is now of type `storetypes.StoreKey` (where `storetypes` is an import alias for `"github.com/cosmos/cosmos-sdk/store/types"`): + +```diff +func NewKeeper( + cdc codec.BinaryCodec, +- key sdk.StoreKey, ++ key storetypes.StoreKey, + paramSpace paramtypes.Subspace, + sk types.StakingKeeper, + uk types.UpgradeKeeper +) Keeper +``` diff --git a/docs/migrations/v5-to-v6.md b/docs/migrations/v5-to-v6.md new file mode 100644 index 00000000000..06052170970 --- /dev/null +++ b/docs/migrations/v5-to-v6.md @@ -0,0 +1,292 @@ +# Migrating from ibc-go v5 to v6 + +This document is intended to highlight significant changes which may require more information than presented in the CHANGELOG. +Any changes that must be done by a user of ibc-go should be documented here. + +There are four sections based on the four potential user groups of this document: + +- Chains +- IBC Apps +- Relayers +- IBC Light Clients + +**Note:** ibc-go supports golang semantic versioning and therefore all imports must be updated to bump the version number on major releases. + +## Chains + +The `ibc-go/v6` release introduces a new set of migrations for `27-interchain-accounts`. Ownership of ICS27 channel capabilities is transferred from ICS27 authentication modules and will now reside with the ICS27 controller submodule moving forward. + +For chains which contain a custom authentication module using the ICS27 controller submodule this requires a migration function to be included in the chain upgrade handler. A subsequent migration handler is run automatically, asserting the ownership of ICS27 channel capabilities has been transferred successfully. + +This migration is not required for chains which *do not* contain a custom authentication module using the ICS27 controller submodule. + +This migration facilitates the addition of the ICS27 controller submodule `MsgServer` which provides a standardised approach to integrating existing forms of authentication such as `x/gov` and `x/group` provided by the Cosmos SDK. + +For more information please refer to [ADR 009](../architecture/adr-009-v6-ics27-msgserver.md). + +### Upgrade proposal + +Please refer to [PR #2383](https://github.com/cosmos/ibc-go/pull/2383) for integrating the ICS27 channel capability migration logic or follow the steps outlined below: + +1. Add the upgrade migration logic to chain distribution. This may be, for example, maintained under a package `app/upgrades/v6`. + +```go +package v6 + +import ( + "github.com/cosmos/cosmos-sdk/codec" + storetypes "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + capabilitykeeper "github.com/cosmos/cosmos-sdk/x/capability/keeper" + upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" + + v6 "github.com/cosmos/ibc-go/v6/modules/apps/27-interchain-accounts/controller/migrations/v6" +) + +const ( + UpgradeName = "v6" +) + +func CreateUpgradeHandler( + mm *module.Manager, + configurator module.Configurator, + cdc codec.BinaryCodec, + capabilityStoreKey *storetypes.KVStoreKey, + capabilityKeeper *capabilitykeeper.Keeper, + moduleName string, +) upgradetypes.UpgradeHandler { + return func(ctx sdk.Context, _ upgradetypes.Plan, vm module.VersionMap) (module.VersionMap, error) { + if err := v6.MigrateICS27ChannelCapability(ctx, cdc, capabilityStoreKey, capabilityKeeper, moduleName); err != nil { + return nil, err + } + + return mm.RunMigrations(ctx, configurator, vm) + } +} +``` + +2. Set the upgrade handler in `app.go`. The `moduleName` parameter refers to the authentication module's `ScopedKeeper` name. This is the name provided upon instantiation in `app.go` via the [`x/capability` keeper `ScopeToModule(moduleName string)`](https://github.com/cosmos/cosmos-sdk/blob/v0.46.1/x/capability/keeper/keeper.go#L70) method. [See here for an example in `simapp`](https://github.com/cosmos/ibc-go/blob/v5.0.0/testing/simapp/app.go#L304). + +```go +app.UpgradeKeeper.SetUpgradeHandler( + v6.UpgradeName, + v6.CreateUpgradeHandler( + app.mm, + app.configurator, + app.appCodec, + app.keys[capabilitytypes.ModuleName], + app.CapabilityKeeper, + >>>> moduleName <<<<, + ), +) +``` + +## IBC Apps + +### ICS27 - Interchain Accounts + +#### Controller APIs + +In previous releases of ibc-go, chain developers integrating the ICS27 interchain accounts controller functionality were expected to create a custom `Base Application` referred to as an authentication module, see the section [Building an authentication module](../apps/interchain-accounts/auth-modules.md) from the documentation. + +The `Base Application` was intended to be composed with the ICS27 controller submodule `Keeper` and faciliate many forms of message authentication depending on a chain's particular use case. + +Prior to ibc-go v6 the controller submodule exposed only these two functions (to which we will refer as the legacy APIs): + +- [`RegisterInterchainAccount`](https://github.com/cosmos/ibc-go/blob/v5.0.0/modules/apps/27-interchain-accounts/controller/keeper/account.go#L19) +- [`SendTx`](https://github.com/cosmos/ibc-go/blob/v5.0.0/modules/apps/27-interchain-accounts/controller/keeper/relay.go#L18) + +However, these functions have now been deprecated in favour of the new controller submodule `MsgServer` and will be removed in later releases. + +Both APIs remain functional and maintain backwards compatibility in ibc-go v6, however consumers of these APIs are now recommended to follow the message passing paradigm outlined in Cosmos SDK [ADR 031](https://github.com/cosmos/cosmos-sdk/blob/main/docs/architecture/adr-031-msg-service.md) and [ADR 033](https://github.com/cosmos/cosmos-sdk/blob/main/docs/architecture/adr-033-protobuf-inter-module-comm.md). This is facilitated by the Cosmos SDK [`MsgServiceRouter`](https://github.com/cosmos/cosmos-sdk/blob/main/baseapp/msg_service_router.go#L17) and chain developers creating custom application logic can now omit the ICS27 controller submodule `Keeper` from their module and instead depend on message routing. + +Depending on the use case, developers of custom authentication modules face one of three scenarios: + +![AuthModuleDecisionTree](../assets/auth-module-decision-tree.png) + +**My authentication module needs to access IBC packet callbacks** + +Application developers that wish to consume IBC packet callbacks and react upon packet acknowledgements **must** continue using the controller submodule's legacy APIs. The authentication modules will not need a `ScopedKeeper` anymore, though, because the channel capability will be claimed by the controller submodule. For example, given an Interchain Accounts authentication module keeper `ICAAuthKeeper`, the authentication module's `ScopedKeeper` (`scopedICAAuthKeeper`) is not needed anymore and can be removed for the argument list of the keeper constructor function, as shown here: + +```diff +app.ICAAuthKeeper = icaauthkeeper.NewKeeper( + appCodec, + keys[icaauthtypes.StoreKey], + app.ICAControllerKeeper, +- scopedICAAuthKeeper, +) +``` + +Please note that the authentication module's `ScopedKeeper` name is still needed as part of the channel capability migration described in section [Upgrade proposal](#upgrade-proposal) above. Therefore the authentication module's `ScopedKeeper` cannot be completely removed from the chain code until the migration has run. + +In the future, the use of the legacy APIs for accessing packet callbacks will be replaced by IBC Actor Callbacks (see [ADR 008](https://github.com/cosmos/ibc-go/pull/1976) for more details) and it will also be possible to access them with the `MsgServiceRouter`. + +**My authentication module does not need access to IBC packet callbacks** + +The authentication module can migrate from using the legacy APIs and it can be composed instead with the `MsgServiceRouter`, so that the authentication module is able to pass messages to the controller submodule's `MsgServer` to register interchain accounts and send packets to the interchain account. For example, given an Interchain Accounts authentication module keeper `ICAAuthKeeper`, the ICS27 controller submodule keeper (`ICAControllerKeeper`) and authentication module scoped keeper (`scopedICAAuthKeeper`) are not needed anymore and can be replaced with the `MsgServiceRouter`, as shown here: + +```diff +app.ICAAuthKeeper = icaauthkeeper.NewKeeper( + appCodec, + keys[icaauthtypes.StoreKey], +- app.ICAControllerKeeper, +- scopedICAAuthKeeper, ++ app.MsgServiceRouter(), +) +``` + +In your authentication module you can route messages to the controller submodule's `MsgServer` instead of using the legacy APIs. For example, for registering an interchain account: + +```diff +- if err := keeper.icaControllerKeeper.RegisterInterchainAccount( +- ctx, +- connectionID, +- owner.String(), +- version, +- ); err != nil { +- return err +- } ++ msg := controllertypes.NewMsgRegisterInterchainAccount( ++ connectionID, ++ owner.String(), ++ version, ++ ) ++ handler := keeper.msgRouter.Handler(msg) ++ res, err := handler(ctx, msg) ++ if err != nil { ++ return err ++ } +``` + +where `controllertypes` is an import alias for `"github.com/cosmos/ibc-go/v6/modules/apps/27-interchain-accounts/controller/types"`. + +In addition, in this use case the authentication module does not need to implement the `IBCModule` interface anymore. + +**I do not need a custom authentication module anymore** + +If your authentication module does not have any extra functionality compared to the default authentication module added in ibc-go v6 (the `MsgServer`), or if you can use a generic authentication module, such as the `x/auth`, `x/gov` or `x/group` modules from the Cosmos SDK (v0.46 and later), then you can remove your authentication module completely and use instead the gRPC endpoints of the `MsgServer` or the CLI added in ibc-go v6. + +Please remember that the authentication module's `ScopedKeeper` name is still needed as part of the channel capability migration described in section [Upgrade proposal](#upgrade-proposal) above. + +#### Host params + +The ICS27 host submodule default params have been updated to include the `AllowAllHostMsgs` wildcard `*`. +This enables execution of any `sdk.Msg` type for ICS27 registered on the host chain `InterfaceRegistry`. + +```diff +// AllowAllHostMsgs holds the string key that allows all message types on interchain accounts host module +const AllowAllHostMsgs = "*" + +... + +// DefaultParams is the default parameter configuration for the host submodule +func DefaultParams() Params { +- return NewParams(DefaultHostEnabled, nil) ++ return NewParams(DefaultHostEnabled, []string{AllowAllHostMsgs}) +} +``` + +#### API breaking changes + +`SerializeCosmosTx` takes in a `[]proto.Message` instead of `[]sdk.Message`. This allows for the serialization of proto messages without requiring the fulfillment of the `sdk.Msg` interface. + +The `27-interchain-accounts` genesis types have been moved to their own package: `modules/apps/27-interchain-acccounts/genesis/types`. +This change facilitates the addition of the ICS27 controller submodule `MsgServer` and avoids cyclic imports. This should have minimal disruption to chain developers integrating `27-interchain-accounts`. + +The ICS27 host submodule `NewKeeper` function in `modules/apps/27-interchain-acccounts/host/keeper` now includes an additional parameter of type `ICS4Wrapper`. +This provides the host submodule with the ability to correctly unwrap channel versions in the event of a channel reopening handshake. + +```diff +func NewKeeper( + cdc codec.BinaryCodec, key storetypes.StoreKey, paramSpace paramtypes.Subspace, +- channelKeeper icatypes.ChannelKeeper, portKeeper icatypes.PortKeeper, ++ ics4Wrapper icatypes.ICS4Wrapper, channelKeeper icatypes.ChannelKeeper, portKeeper icatypes.PortKeeper, + accountKeeper icatypes.AccountKeeper, scopedKeeper icatypes.ScopedKeeper, msgRouter icatypes.MessageRouter, +) Keeper +``` + +### ICS29 - `NewKeeper` API change + +The `NewKeeper` function of ICS29 has been updated to remove the `paramSpace` parameter as it was unused. + +```diff +func NewKeeper( +- cdc codec.BinaryCodec, key storetypes.StoreKey, paramSpace paramtypes.Subspace, +- ics4Wrapper types.ICS4Wrapper, channelKeeper types.ChannelKeeper, portKeeper types.PortKeeper, authKeeper types.AccountKeeper, bankKeeper types.BankKeeper, ++ cdc codec.BinaryCodec, key storetypes.StoreKey, ++ ics4Wrapper types.ICS4Wrapper, channelKeeper types.ChannelKeeper, ++ portKeeper types.PortKeeper, authKeeper types.AccountKeeper, bankKeeper types.BankKeeper, +) Keeper { +``` + +### ICS20 - `SendTransfer` is no longer exported + +The `SendTransfer` function of ICS20 has been removed. IBC transfers should now be initiated with `MsgTransfer` and routed to the ICS20 `MsgServer`. + +See below for example: + +```go +if handler := msgRouter.Handler(msgTransfer); handler != nil { + if err := msgTransfer.ValidateBasic(); err != nil { + return nil, err + } + + res, err := handler(ctx, msgTransfer) + if err != nil { + return nil, err + } +} +``` + +### ICS04 - `SendPacket` API change + +The `SendPacket` API has been simplified: + +```diff +// SendPacket is called by a module in order to send an IBC packet on a channel +func (k Keeper) SendPacket( + ctx sdk.Context, + channelCap *capabilitytypes.Capability, +- packet exported.PacketI, +-) error { ++ sourcePort string, ++ sourceChannel string, ++ timeoutHeight clienttypes.Height, ++ timeoutTimestamp uint64, ++ data []byte, ++) (uint64, error) { +``` + +Callers no longer need to pass in a pre-constructed packet. +The destination port/channel identifiers and the packet sequence will be determined by core IBC. +`SendPacket` will return the packet sequence. + +### IBC testing package + +The `SendPacket` API has been simplified: + +```diff +// SendPacket is called by a module in order to send an IBC packet on a channel +func (k Keeper) SendPacket( + ctx sdk.Context, + channelCap *capabilitytypes.Capability, +- packet exported.PacketI, +-) error { ++ sourcePort string, ++ sourceChannel string, ++ timeoutHeight clienttypes.Height, ++ timeoutTimestamp uint64, ++ data []byte, ++) (uint64, error) { +``` + +Callers no longer need to pass in a pre-constructed packet. `SendPacket` will return the packet sequence. + +## Relayers + +- No relevant changes were made in this release. + +## IBC Light Clients + +- No relevant changes were made in this release. diff --git a/docs/migrations/v6-to-v7.md b/docs/migrations/v6-to-v7.md new file mode 100644 index 00000000000..4e15fc5061b --- /dev/null +++ b/docs/migrations/v6-to-v7.md @@ -0,0 +1,351 @@ +# Migrating from ibc-go v6 to v7 + +This document is intended to highlight significant changes which may require more information than presented in the CHANGELOG. +Any changes that must be done by a user of ibc-go should be documented here. + +There are four sections based on the four potential user groups of this document: + +- Chains +- IBC Apps +- Relayers +- IBC Light Clients + +**Note:** ibc-go supports golang semantic versioning and therefore all imports must be updated to bump the version number on major releases. + +## Chains + +Chains will perform automatic migrations to remove existing localhost clients and to migrate the solomachine to v3 of the protobuf definition. + +An optional upgrade handler has been added to prune expired tendermint consensus states. It may be used during any upgrade (from v7 onwards). +Add the following to the function call to the upgrade handler in `app/app.go`, to perform the optional state pruning. + +```go +import ( + // ... + ibctmmigrations "github.com/cosmos/ibc-go/v6/modules/light-clients/07-tendermint/migrations" +) + +// ... + +app.UpgradeKeeper.SetUpgradeHandler( + upgradeName, + func(ctx sdk.Context, _ upgradetypes.Plan, _ module.VersionMap) (module.VersionMap, error) { + // prune expired tendermint consensus states to save storage space + _, err := ibctmmigrations.PruneExpiredConsensusStates(ctx, app.Codec, app.IBCKeeper.ClientKeeper) + if err != nil { + return nil, err + } + + return app.mm.RunMigrations(ctx, app.configurator, fromVM) + }, +) +``` + +Checkout the logs to see how many consensus states are pruned. + +### Light client registration + +Chains must explicitly register the types of any light client modules it wishes to integrate. + +#### Tendermint registration + +To register the tendermint client, modify the `app.go` file to include the tendermint `AppModuleBasic`: + +```diff +import ( + // ... ++ ibctm "github.com/cosmos/ibc-go/v6/modules/light-clients/07-tendermint" +) + +// ... + +ModuleBasics = module.NewBasicManager( + ... + ibc.AppModuleBasic{}, ++ ibctm.AppModuleBasic{}, + ... +) +``` + +It may be useful to reference the [PR](https://github.com/cosmos/ibc-go/pull/2825) which added the `AppModuleBasic` for the tendermint client. + +#### Solo machine registration + +To register the solo machine client, modify the `app.go` file to include the solo machine `AppModuleBasic`: + +```diff +import ( + // ... ++ solomachine "github.com/cosmos/ibc-go/v6/modules/light-clients/06-solomachine" +) + +// ... + +ModuleBasics = module.NewBasicManager( + ... + ibc.AppModuleBasic{}, ++ solomachine.AppModuleBasic{}, + ... +) +``` + +It may be useful to reference the [PR](https://github.com/cosmos/ibc-go/pull/2826) which added the `AppModuleBasic` for the solo machine client. + +### Testing package API + +The `SetChannelClosed` utility method in `testing/endpoint.go` has been updated to `SetChannelState`, which will take a `channeltypes.State` argument so that the `ChannelState` can be set to any of the possible channel states. + +## IBC Apps + +- No relevant changes were made in this release. + +## Relayers + +- No relevant changes were made in this release. + +## IBC Light Clients + +### `ClientState` interface changes + +The `VerifyUpgradeAndUpdateState` function has been modified. The client state and consensus state return values have been removed. + +Light clients **must** handle all management of client and consensus states including the setting of updated client state and consensus state in the client store. + +The `Initialize` method is now expected to set the initial client state, consensus state and any client-specific metadata in the provided store upon client creation. + +The `CheckHeaderAndUpdateState` method has been split into 4 new methods: + +- `VerifyClientMessage` verifies a `ClientMessage`. A `ClientMessage` could be a `Header`, `Misbehaviour`, or batch update. Calls to `CheckForMisbehaviour`, `UpdateState`, and `UpdateStateOnMisbehaviour` will assume that the content of the `ClientMessage` has been verified and can be trusted. An error should be returned if the `ClientMessage` fails to verify. + +- `CheckForMisbehaviour` checks for evidence of a misbehaviour in `Header` or `Misbehaviour` types. + +- `UpdateStateOnMisbehaviour` performs appropriate state changes on a `ClientState` given that misbehaviour has been detected and verified. + +- `UpdateState` updates and stores as necessary any associated information for an IBC client, such as the `ClientState` and corresponding `ConsensusState`. An error is returned if `ClientMessage` is of type `Misbehaviour`. Upon successful update, a list containing the updated consensus state height is returned. + +The `CheckMisbehaviourAndUpdateState` function has been removed from `ClientState` interface. This functionality is now encapsulated by the usage of `VerifyClientMessage`, `CheckForMisbehaviour`, `UpdateStateOnMisbehaviour`. + +The function `GetTimestampAtHeight` has been added to the `ClientState` interface. It should return the timestamp for a consensus state associated with the provided height. + +Prior to ibc-go/v7 the `ClientState` interface defined a method for each data type which was being verified in the counterparty state store. +The state verification functions for all IBC data types have been consolidated into two generic methods, `VerifyMembership` and `VerifyNonMembership`. +Both are expected to be provided with a standardised key path, `exported.Path`, as defined in [ICS 24 host requirements](https://github.com/cosmos/ibc/tree/main/spec/core/ics-024-host-requirements). Membership verification requires callers to provide the marshalled value `[]byte`. Delay period values should be zero for non-packet processing verification. A zero proof height is now allowed by core IBC and may be passed into `VerifyMembership` and `VerifyNonMembership`. Light clients are responsible for returning an error if a zero proof height is invalid behaviour. + +See below for an example of how ibc-go now performs channel state verification. + +```go +merklePath := commitmenttypes.NewMerklePath(host.ChannelPath(portID, channelID)) +merklePath, err := commitmenttypes.ApplyPrefix(connection.GetCounterparty().GetPrefix(), merklePath) +if err != nil { + return err +} + +channelEnd, ok := channel.(channeltypes.Channel) +if !ok { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "invalid channel type %T", channel) +} + +bz, err := k.cdc.Marshal(&channelEnd) +if err != nil { + return err +} + +if err := clientState.VerifyMembership( + ctx, clientStore, k.cdc, height, + 0, 0, // skip delay period checks for non-packet processing verification + proof, merklePath, bz, +); err != nil { + return sdkerrors.Wrapf(err, "failed channel state verification for client (%s)", clientID) +} +``` + +### `Header` and `Misbehaviour` + +`exported.Header` and `exported.Misbehaviour` interface types have been merged and renamed to `ClientMessage` interface. + +`GetHeight` function has been removed from `exported.Header` and thus is not included in the `ClientMessage` interface + +### `ConsensusState` + +The `GetRoot` function has been removed from consensus state interface since it was not used by core IBC. + +### Client keeper + +Keeper function `CheckMisbehaviourAndUpdateState` has been removed since function `UpdateClient` can now handle updating `ClientState` on `ClientMessage` type which can be any `Misbehaviour` implementations. + +### SDK message + +`MsgSubmitMisbehaviour` is deprecated since `MsgUpdateClient` can now submit a `ClientMessage` type which can be any `Misbehaviour` implementations. + +The field `header` in `MsgUpdateClient` has been renamed to `client_message`. + +## Solomachine + +The `06-solomachine` client implementation has been simplified in ibc-go/v7. In-place store migrations have been added to migrate solomachine clients from `v2` to `v3`. + +### `ClientState` + +The `ClientState` protobuf message definition has been updated to remove the deprecated `bool` field `allow_update_after_proposal`. + +```diff +message ClientState { + option (gogoproto.goproto_getters) = false; + + uint64 sequence = 1; + bool is_frozen = 2 [(gogoproto.moretags) = "yaml:\"is_frozen\""]; + ConsensusState consensus_state = 3 [(gogoproto.moretags) = "yaml:\"consensus_state\""]; +- bool allow_update_after_proposal = 4 [(gogoproto.moretags) = "yaml:\"allow_update_after_proposal\""]; +} +``` + +### `Header` and `Misbehaviour` + +The `06-solomachine` protobuf message `Header` has been updated to remove the `sequence` field. This field was seen as redundant as the implementation can safely rely on the `sequence` value maintained within the `ClientState`. + +```diff +message Header { + option (gogoproto.goproto_getters) = false; + +- uint64 sequence = 1; +- uint64 timestamp = 2; +- bytes signature = 3; +- google.protobuf.Any new_public_key = 4 [(gogoproto.moretags) = "yaml:\"new_public_key\""]; +- string new_diversifier = 5 [(gogoproto.moretags) = "yaml:\"new_diversifier\""]; ++ uint64 timestamp = 1; ++ bytes signature = 2; ++ google.protobuf.Any new_public_key = 3 [(gogoproto.moretags) = "yaml:\"new_public_key\""]; ++ string new_diversifier = 4 [(gogoproto.moretags) = "yaml:\"new_diversifier\""]; +} +``` + +Similarly, the `Misbehaviour` protobuf message has been updated to remove the `client_id` field. + +```diff +message Misbehaviour { + option (gogoproto.goproto_getters) = false; + +- string client_id = 1 [(gogoproto.moretags) = "yaml:\"client_id\""]; +- uint64 sequence = 2; +- SignatureAndData signature_one = 3 [(gogoproto.moretags) = "yaml:\"signature_one\""]; +- SignatureAndData signature_two = 4 [(gogoproto.moretags) = "yaml:\"signature_two\""]; ++ uint64 sequence = 1; ++ SignatureAndData signature_one = 2 [(gogoproto.moretags) = "yaml:\"signature_one\""]; ++ SignatureAndData signature_two = 3 [(gogoproto.moretags) = "yaml:\"signature_two\""]; +} +``` + +### `SignBytes` + +Most notably, the `SignBytes` protobuf definition has been modified to replace the `data_type` field with a new field, `path`. The `path` field is defined as `bytes` and represents a serialized [ICS-24](https://github.com/cosmos/ibc/tree/main/spec/core/ics-024-host-requirements) standardized key path under which the `data` is stored. + +```diff +message SignBytes { + option (gogoproto.goproto_getters) = false; + + uint64 sequence = 1; + uint64 timestamp = 2; + string diversifier = 3; +- DataType data_type = 4 [(gogoproto.moretags) = "yaml:\"data_type\""]; ++ bytes path = 4; + bytes data = 5; +} +``` + +The `DataType` enum and all associated data types have been removed, greatly reducing the number of message definitions and complexity in constructing the `SignBytes` message type. Likewise, solomachine implementations must now use the serialized `path` value when constructing `SignatureAndData` for signature verification of `SignBytes` data. + +```diff +message SignatureAndData { + option (gogoproto.goproto_getters) = false; + + bytes signature = 1; +- DataType data_type = 2 [(gogoproto.moretags) = "yaml:\"data_type\""]; ++ bytes path = 2; + bytes data = 3; + uint64 timestamp = 4; +} +``` + +For more information, please refer to [ADR-007](https://github.com/cosmos/ibc-go/blob/02-client-refactor-beta1/docs/architecture/adr-007-solomachine-signbytes.md). + +### IBC module constants + +IBC module constants have been moved from the `host` package to the `exported` package. Any usages will need to be updated. + +```diff +import ( + // ... +- host "github.com/cosmos/ibc-go/v6/modules/core/24-host" ++ ibcexported "github.com/cosmos/ibc-go/v6/modules/core/exported" + // ... +) + +- host.ModuleName ++ ibcexported.ModuleName + +- host.StoreKey ++ ibcexported.StoreKey + +- host.QuerierRoute ++ ibcexported.QuerierRoute + +- host.RouterKey ++ ibcexported.RouterKey +``` + +## Upgrading to Cosmos SDK 0.47 + +The following should be considered as complementary to [Cosmos SDK v0.47 UPGRADING.md](https://github.com/cosmos/cosmos-sdk/blob/v0.47.0-rc2/UPGRADING.md). + +### Protobuf + +Protobuf code generation, linting and formatting have been updated to leverage the `ghcr.io/cosmos/proto-builder:0.11.5` docker container. IBC protobuf definitions are now packaged and published to [buf.build/cosmos/ibc](https://buf.build/cosmos/ibc) via CI workflows. The `third_party/proto` directory has been removed in favour of dependency management using [buf.build](https://docs.buf.build/introduction). + +### App modules + +Legacy APIs of the `AppModule` interface have been removed from ibc-go modules. For example, for + +```diff +- // Route implements the AppModule interface +- func (am AppModule) Route() sdk.Route { +- return sdk.Route{} +- } +- +- // QuerierRoute implements the AppModule interface +- func (AppModule) QuerierRoute() string { +- return types.QuerierRoute +- } +- +- // LegacyQuerierHandler implements the AppModule interface +- func (am AppModule) LegacyQuerierHandler(*codec.LegacyAmino) sdk.Querier { +- return nil +- } +- +- // ProposalContents doesn't return any content functions for governance proposals. +- func (AppModule) ProposalContents(_ module.SimulationState) []simtypes.WeightedProposalContent { +- return nil +- } +``` + +### Imports + +Imports for ics23 have been updated as the repository have been migrated from confio to cosmos. + +```diff +import ( + // ... +- ics23 "github.com/confio/ics23/go" ++ ics23 "github.com/cosmos/ics23/go" + // ... +) +``` + +Imports for gogoproto have been updated. + +```diff +import ( + // ... +- "github.com/gogo/protobuf/proto" ++ "github.com/cosmos/gogoproto/proto" + // ... +) +``` diff --git a/docs/package-lock.json b/docs/package-lock.json index ff82a7755b0..552c3164446 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -1,8 +1,14279 @@ { "name": "docs", "version": "1.0.0", - "lockfileVersion": 1, + "lockfileVersion": 2, "requires": true, + "packages": { + "": { + "name": "docs", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "vuepress-theme-cosmos": "^1.0.183" + } + }, + "node_modules/@algolia/cache-browser-local-storage": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.12.0.tgz", + "integrity": "sha512-l+G560B6N1k0rIcOjTO1yCzFUbg2Zy2HCii9s03e13jGgqduVQmk79UUCYszjsJ5GPJpUEKcVEtAIpP7tjsXVA==", + "dependencies": { + "@algolia/cache-common": "4.12.0" + } + }, + "node_modules/@algolia/cache-common": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/@algolia/cache-common/-/cache-common-4.12.0.tgz", + "integrity": "sha512-2Z8BV+NX7oN7RmmQbLqmW8lfN9aAjOexX1FJjzB0YfKC9ifpi9Jl4nSxlnbU+iLR6QhHo0IfuyQ7wcnucCGCGQ==" + }, + "node_modules/@algolia/cache-in-memory": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/@algolia/cache-in-memory/-/cache-in-memory-4.12.0.tgz", + "integrity": "sha512-b6ANkZF6vGAo+sYv6g25W5a0u3o6F549gEAgtTDTVA1aHcdWwe/HG/dTJ7NsnHbuR+A831tIwnNYQjRp3/V/Jw==", + "dependencies": { + "@algolia/cache-common": "4.12.0" + } + }, + "node_modules/@algolia/client-account": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/@algolia/client-account/-/client-account-4.12.0.tgz", + "integrity": "sha512-gzXN75ZydNheNXUN3epS+aLsKnB/PHFVlGUUjXL8WHs4lJP3B5FtHvaA/NCN5DsM3aamhuY5p0ff1XIA+Lbcrw==", + "dependencies": { + "@algolia/client-common": "4.12.0", + "@algolia/client-search": "4.12.0", + "@algolia/transporter": "4.12.0" + } + }, + "node_modules/@algolia/client-analytics": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-4.12.0.tgz", + "integrity": "sha512-rO2cZCt00Opk66QBZb7IBGfCq4ZE3EiuGkXssf2Monb5urujy0r8CknK2i7bzaKtPbd2vlvhmLP4CEHQqF6SLQ==", + "dependencies": { + "@algolia/client-common": "4.12.0", + "@algolia/client-search": "4.12.0", + "@algolia/requester-common": "4.12.0", + "@algolia/transporter": "4.12.0" + } + }, + "node_modules/@algolia/client-common": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.12.0.tgz", + "integrity": "sha512-fcrFN7FBmxiSyjeu3sF4OnPkC1l7/8oyQ8RMM8CHpVY8cad6/ay35MrfRfgfqdzdFA8LzcBYO7fykuJv0eOqxw==", + "dependencies": { + "@algolia/requester-common": "4.12.0", + "@algolia/transporter": "4.12.0" + } + }, + "node_modules/@algolia/client-personalization": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-4.12.0.tgz", + "integrity": "sha512-wCJfSQEmX6ZOuJBJGjy+sbXiW0iy7tMNAhsVMV9RRaJE4727e5WAqwFWZssD877WQ74+/nF/VyTaB1+wejo33Q==", + "dependencies": { + "@algolia/client-common": "4.12.0", + "@algolia/requester-common": "4.12.0", + "@algolia/transporter": "4.12.0" + } + }, + "node_modules/@algolia/client-search": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.12.0.tgz", + "integrity": "sha512-ik6dswcTQtOdZN+8aKntI9X2E6Qpqjtyda/+VANiHThY9GD2PBXuNuuC2HvlF26AbBYp5xaSE/EKxn1DIiIJ4Q==", + "dependencies": { + "@algolia/client-common": "4.12.0", + "@algolia/requester-common": "4.12.0", + "@algolia/transporter": "4.12.0" + } + }, + "node_modules/@algolia/logger-common": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/@algolia/logger-common/-/logger-common-4.12.0.tgz", + "integrity": "sha512-V//9rzLdJujA3iZ/tPhmKR/m2kjSZrymxOfUiF3024u2/7UyOpH92OOCrHUf023uMGYHRzyhBz5ESfL1oCdh7g==" + }, + "node_modules/@algolia/logger-console": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/@algolia/logger-console/-/logger-console-4.12.0.tgz", + "integrity": "sha512-pHvoGv53KXRIJHLk9uxBwKirwEo12G9+uo0sJLWESThAN3v5M+ycliU1AkUXQN8+9rds2KxfULAb+vfyfBKf8A==", + "dependencies": { + "@algolia/logger-common": "4.12.0" + } + }, + "node_modules/@algolia/requester-browser-xhr": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.12.0.tgz", + "integrity": "sha512-rGlHNMM3jIZBwSpz33CVkeXHilzuzHuFXEEW1icP/k3KW7kwBrKFJwBy42RzAJa5BYlLsTCFTS3xkPhYwTQKLg==", + "dependencies": { + "@algolia/requester-common": "4.12.0" + } + }, + "node_modules/@algolia/requester-common": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-common/-/requester-common-4.12.0.tgz", + "integrity": "sha512-qgfdc73nXqpVyOMr6CMTx3nXvud9dP6GcMGDqPct+fnxogGcJsp24cY2nMqUrAfgmTJe9Nmy7Lddv0FyHjONMg==" + }, + "node_modules/@algolia/requester-node-http": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-4.12.0.tgz", + "integrity": "sha512-mOTRGf/v/dXshBoZKNhMG00ZGxoUH9QdSpuMKYnuWwIgstN24uj3DQx+Ho3c+uq0TYfq7n2v71uoJWuiW32NMQ==", + "dependencies": { + "@algolia/requester-common": "4.12.0" + } + }, + "node_modules/@algolia/transporter": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/@algolia/transporter/-/transporter-4.12.0.tgz", + "integrity": "sha512-MOQVHZ4BcBpf3LtOY/3fqXHAcvI8MahrXDHk9QrBE/iGensQhDiZby5Dn3o2JN/zd9FMnVbdPQ8gnkiMwZiakQ==", + "dependencies": { + "@algolia/cache-common": "4.12.0", + "@algolia/logger-common": "4.12.0", + "@algolia/requester-common": "4.12.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz", + "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==", + "dependencies": { + "@babel/highlight": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.16.8.tgz", + "integrity": "sha512-m7OkX0IdKLKPpBlJtF561YJal5y/jyI5fNfWbPxh2D/nbzzGI4qRyrD8xO2jB24u7l+5I2a43scCG2IrfjC50Q==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.16.12", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.16.12.tgz", + "integrity": "sha512-dK5PtG1uiN2ikk++5OzSYsitZKny4wOCD0nrO4TqnW4BVBTQ2NGS3NgilvT/TEyxTST7LNyWV/T4tXDoD3fOgg==", + "dependencies": { + "@babel/code-frame": "^7.16.7", + "@babel/generator": "^7.16.8", + "@babel/helper-compilation-targets": "^7.16.7", + "@babel/helper-module-transforms": "^7.16.7", + "@babel/helpers": "^7.16.7", + "@babel/parser": "^7.16.12", + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.16.10", + "@babel/types": "^7.16.8", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.1.2", + "semver": "^6.3.0", + "source-map": "^0.5.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@babel/core/node_modules/json5": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", + "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", + "dependencies": { + "minimist": "^1.2.5" + }, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@babel/core/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/@babel/core/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@babel/generator": { + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.16.8.tgz", + "integrity": "sha512-1ojZwE9+lOXzcWdWmO6TbUzDfqLD39CmEhN8+2cX9XkDo5yW1OpgfejfliysR2AWLpMamTiOiAp/mtroaymhpw==", + "dependencies": { + "@babel/types": "^7.16.8", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/generator/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz", + "integrity": "sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==", + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.16.7.tgz", + "integrity": "sha512-C6FdbRaxYjwVu/geKW4ZeQ0Q31AftgRcdSnZ5/jsH6BzCJbtvXvhpfkbkThYSuutZA7nCXpPR6AD9zd1dprMkA==", + "dependencies": { + "@babel/helper-explode-assignable-expression": "^7.16.7", + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.7.tgz", + "integrity": "sha512-mGojBwIWcwGD6rfqgRXVlVYmPAv7eOpIemUG3dGnDdCY4Pae70ROij3XmfrH6Fa1h1aiDylpglbZyktfzyo/hA==", + "dependencies": { + "@babel/compat-data": "^7.16.4", + "@babel/helper-validator-option": "^7.16.7", + "browserslist": "^4.17.5", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.16.10", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.16.10.tgz", + "integrity": "sha512-wDeej0pu3WN/ffTxMNCPW5UCiOav8IcLRxSIyp/9+IF2xJUM9h/OYjg0IJLHaL6F8oU8kqMz9nc1vryXhMsgXg==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-function-name": "^7.16.7", + "@babel/helper-member-expression-to-functions": "^7.16.7", + "@babel/helper-optimise-call-expression": "^7.16.7", + "@babel/helper-replace-supers": "^7.16.7", + "@babel/helper-split-export-declaration": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.16.7.tgz", + "integrity": "sha512-fk5A6ymfp+O5+p2yCkXAu5Kyj6v0xh0RBeNcAkYUMDvvAAoxvSKXn+Jb37t/yWFiQVDFK1ELpUTD8/aLhCPu+g==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "regexpu-core": "^4.7.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.1.tgz", + "integrity": "sha512-J9hGMpJQmtWmj46B3kBHmL38UhJGhYX7eqkcq+2gsstyYt341HmPeWspihX43yVRA0mS+8GGk2Gckc7bY/HCmA==", + "dependencies": { + "@babel/helper-compilation-targets": "^7.13.0", + "@babel/helper-module-imports": "^7.12.13", + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/traverse": "^7.13.0", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2", + "semver": "^6.1.2" + }, + "peerDependencies": { + "@babel/core": "^7.4.0-0" + } + }, + "node_modules/@babel/helper-define-polyfill-provider/node_modules/debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@babel/helper-define-polyfill-provider/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz", + "integrity": "sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag==", + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-explode-assignable-expression": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.16.7.tgz", + "integrity": "sha512-KyUenhWMC8VrxzkGP0Jizjo4/Zx+1nNZhgocs+gLzyZyB8SHidhoq9KK/8Ato4anhwsivfkBLftky7gvzbZMtQ==", + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz", + "integrity": "sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA==", + "dependencies": { + "@babel/helper-get-function-arity": "^7.16.7", + "@babel/template": "^7.16.7", + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-get-function-arity": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz", + "integrity": "sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw==", + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz", + "integrity": "sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==", + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.16.7.tgz", + "integrity": "sha512-VtJ/65tYiU/6AbMTDwyoXGPKHgTsfRarivm+YbB5uAzKUyuPjgZSgAFeG87FCigc7KNHu2Pegh1XIT3lXjvz3Q==", + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz", + "integrity": "sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==", + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.16.7.tgz", + "integrity": "sha512-gaqtLDxJEFCeQbYp9aLAefjhkKdjKcdh6DB7jniIGU3Pz52WAmP268zK0VgPz9hUNkMSYeH976K2/Y6yPadpng==", + "dependencies": { + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-module-imports": "^7.16.7", + "@babel/helper-simple-access": "^7.16.7", + "@babel/helper-split-export-declaration": "^7.16.7", + "@babel/helper-validator-identifier": "^7.16.7", + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.16.7", + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.7.tgz", + "integrity": "sha512-EtgBhg7rd/JcnpZFXpBy0ze1YRfdm7BnBX4uKMBd3ixa3RGAE002JZB66FJyNH7g0F38U05pXmA5P8cBh7z+1w==", + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz", + "integrity": "sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-remap-async-to-generator": { + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.16.8.tgz", + "integrity": "sha512-fm0gH7Flb8H51LqJHy3HJ3wnE1+qtYR2A99K06ahwrawLdOFsCEWjZOrYricXJHoPSudNKxrMBUPEIPxiIIvBw==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "@babel/helper-wrap-function": "^7.16.8", + "@babel/types": "^7.16.8" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.16.7.tgz", + "integrity": "sha512-y9vsWilTNaVnVh6xiJfABzsNpgDPKev9HnAgz6Gb1p6UUwf9NepdlsV7VXGCftJM+jqD5f7JIEubcpLjZj5dBw==", + "dependencies": { + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-member-expression-to-functions": "^7.16.7", + "@babel/helper-optimise-call-expression": "^7.16.7", + "@babel/traverse": "^7.16.7", + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.16.7.tgz", + "integrity": "sha512-ZIzHVyoeLMvXMN/vok/a4LWRy8G2v205mNP0XOuf9XRLyX5/u9CnVulUtDgUTama3lT+bf/UqucuZjqiGuTS1g==", + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.16.0.tgz", + "integrity": "sha512-+il1gTy0oHwUsBQZyJvukbB4vPMdcYBrFHa0Uc4AizLxbq6BOYC51Rv4tWocX9BLBDLZ4kc6qUFpQ6HRgL+3zw==", + "dependencies": { + "@babel/types": "^7.16.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz", + "integrity": "sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==", + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", + "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz", + "integrity": "sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-wrap-function": { + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.16.8.tgz", + "integrity": "sha512-8RpyRVIAW1RcDDGTA+GpPAwV22wXCfKOoM9bet6TLkGIFTkRQSkH1nMQ5Yet4MpoXe1ZwHPVtNasc2w0uZMqnw==", + "dependencies": { + "@babel/helper-function-name": "^7.16.7", + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.16.8", + "@babel/types": "^7.16.8" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.16.7.tgz", + "integrity": "sha512-9ZDoqtfY7AuEOt3cxchfii6C7GDyyMBffktR5B2jvWv8u2+efwvpnVKXMWzNehqy68tKgAfSwfdw/lWpthS2bw==", + "dependencies": { + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.16.7", + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.16.10", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.10.tgz", + "integrity": "sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw==", + "dependencies": { + "@babel/helper-validator-identifier": "^7.16.7", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.16.12", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.12.tgz", + "integrity": "sha512-VfaV15po8RiZssrkPweyvbGVSe4x2y+aciFCgn0n0/SJMR22cwofRV1mtnJQYcSB1wUTaA/X1LnA3es66MCO5A==", + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.16.7.tgz", + "integrity": "sha512-anv/DObl7waiGEnC24O9zqL0pSuI9hljihqiDuFHC8d7/bjr/4RLGPWuc8rYOff/QPzbEPSkzG8wGG9aDuhHRg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.16.7.tgz", + "integrity": "sha512-di8vUHRdf+4aJ7ltXhaDbPoszdkh59AQtJM5soLsuHpQJdFQZOA4uGj0V2u/CZ8bJ/u8ULDL5yq6FO/bCXnKHw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0", + "@babel/plugin-proposal-optional-chaining": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.13.0" + } + }, + "node_modules/@babel/plugin-proposal-async-generator-functions": { + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.16.8.tgz", + "integrity": "sha512-71YHIvMuiuqWJQkebWJtdhQTfd4Q4mF76q2IX37uZPkG9+olBxsX+rH1vkhFto4UeJZ9dPY2s+mDvhDm1u2BGQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-remap-async-to-generator": "^7.16.8", + "@babel/plugin-syntax-async-generators": "^7.8.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-class-properties": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.16.7.tgz", + "integrity": "sha512-IobU0Xme31ewjYOShSIqd/ZGM/r/cuOz2z0MDbNrhF5FW+ZVgi0f2lyeoj9KFPDOAqsYxmLWZte1WOwlvY9aww==", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-class-static-block": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.16.7.tgz", + "integrity": "sha512-dgqJJrcZoG/4CkMopzhPJjGxsIe9A8RlkQLnL/Vhhx8AA9ZuaRwGSlscSh42hazc7WSrya/IK7mTeoF0DP9tEw==", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-class-static-block": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0" + } + }, + "node_modules/@babel/plugin-proposal-decorators": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.16.7.tgz", + "integrity": "sha512-DoEpnuXK14XV9btI1k8tzNGCutMclpj4yru8aXKoHlVmbO1s+2A+g2+h4JhcjrxkFJqzbymnLG6j/niOf3iFXQ==", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-decorators": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-dynamic-import": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.16.7.tgz", + "integrity": "sha512-I8SW9Ho3/8DRSdmDdH3gORdyUuYnk1m4cMxUAdu5oy4n3OfN8flDEH+d60iG7dUfi0KkYwSvoalHzzdRzpWHTg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-dynamic-import": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-export-namespace-from": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.16.7.tgz", + "integrity": "sha512-ZxdtqDXLRGBL64ocZcs7ovt71L3jhC1RGSyR996svrCi3PYqHNkb3SwPJCs8RIzD86s+WPpt2S73+EHCGO+NUA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-json-strings": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.16.7.tgz", + "integrity": "sha512-lNZ3EEggsGY78JavgbHsK9u5P3pQaW7k4axlgFLYkMd7UBsiNahCITShLjNQschPyjtO6dADrL24757IdhBrsQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-json-strings": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-logical-assignment-operators": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.16.7.tgz", + "integrity": "sha512-K3XzyZJGQCr00+EtYtrDjmwX7o7PLK6U9bi1nCwkQioRFVUv6dJoxbQjtWVtP+bCPy82bONBKG8NPyQ4+i6yjg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-nullish-coalescing-operator": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.16.7.tgz", + "integrity": "sha512-aUOrYU3EVtjf62jQrCj63pYZ7k6vns2h/DQvHPWGmsJRYzWXZ6/AsfgpiRy6XiuIDADhJzP2Q9MwSMKauBQ+UQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-numeric-separator": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.16.7.tgz", + "integrity": "sha512-vQgPMknOIgiuVqbokToyXbkY/OmmjAzr/0lhSIbG/KmnzXPGwW/AdhdKpi+O4X/VkWiWjnkKOBiqJrTaC98VKw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-object-rest-spread": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.16.7.tgz", + "integrity": "sha512-3O0Y4+dw94HA86qSg9IHfyPktgR7q3gpNVAeiKQd+8jBKFaU5NQS1Yatgo4wY+UFNuLjvxcSmzcsHqrhgTyBUA==", + "dependencies": { + "@babel/compat-data": "^7.16.4", + "@babel/helper-compilation-targets": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-optional-catch-binding": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.16.7.tgz", + "integrity": "sha512-eMOH/L4OvWSZAE1VkHbr1vckLG1WUcHGJSLqqQwl2GaUqG6QjddvrOaTUMNYiv77H5IKPMZ9U9P7EaHwvAShfA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-optional-chaining": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.16.7.tgz", + "integrity": "sha512-eC3xy+ZrUcBtP7x+sq62Q/HYd674pPTb/77XZMb5wbDPGWIdUbSr4Agr052+zaUPSb+gGRnjxXfKFvx5iMJ+DA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-private-methods": { + "version": "7.16.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.16.11.tgz", + "integrity": "sha512-F/2uAkPlXDr8+BHpZvo19w3hLFKge+k75XUprE6jaqKxjGkSYcK+4c+bup5PdW/7W/Rpjwql7FTVEDW+fRAQsw==", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.16.10", + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.16.7.tgz", + "integrity": "sha512-rMQkjcOFbm+ufe3bTZLyOfsOUOxyvLXZJCTARhJr+8UMSoZmqTe1K1BgkFcrW37rAchWg57yI69ORxiWvUINuQ==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "@babel/helper-create-class-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-unicode-property-regex": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.16.7.tgz", + "integrity": "sha512-QRK0YI/40VLhNVGIjRNAAQkEHws0cswSdFFjpFyt943YmJIU1da9uW63Iu6NFV6CxTZW5eTDCrwZUstBWgp/Rg==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-decorators": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.16.7.tgz", + "integrity": "sha512-vQ+PxL+srA7g6Rx6I1e15m55gftknl2X8GCUW1JTlkTaXZLJOS0UcaY0eK9jYT7IYf4awn6qwyghVHLDz1WyMw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-export-namespace-from": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", + "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.16.7.tgz", + "integrity": "sha512-Esxmk7YjA8QysKeT3VhTXvF6y77f/a91SIs4pWb4H2eWGQkCKFgQaG6hdoEVZtGsrAcb2K5BW66XsOErD4WU3Q==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.16.7.tgz", + "integrity": "sha512-9ffkFFMbvzTvv+7dTp/66xvZAWASuPD5Tl9LK3Z9vhOmANo6j94rik+5YMBt4CwHVMWLWpMsriIc2zsa3WW3xQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.16.8.tgz", + "integrity": "sha512-MtmUmTJQHCnyJVrScNzNlofQJ3dLFuobYn3mwOTKHnSCMtbNsqvF71GQmJfFjdrXSsAA7iysFmYWw4bXZ20hOg==", + "dependencies": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-remap-async-to-generator": "^7.16.8" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.16.7.tgz", + "integrity": "sha512-JUuzlzmF40Z9cXyytcbZEZKckgrQzChbQJw/5PuEHYeqzCsvebDx0K0jWnIIVcmmDOAVctCgnYs0pMcrYj2zJg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.16.7.tgz", + "integrity": "sha512-ObZev2nxVAYA4bhyusELdo9hb3H+A56bxH3FZMbEImZFiEDYVHXQSJ1hQKFlDnlt8G9bBrCZ5ZpURZUrV4G5qQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-classes": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.16.7.tgz", + "integrity": "sha512-WY7og38SFAGYRe64BrjKf8OrE6ulEHtr5jEYaZMwox9KebgqPi67Zqz8K53EKk1fFEJgm96r32rkKZ3qA2nCWQ==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-function-name": "^7.16.7", + "@babel/helper-optimise-call-expression": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-replace-supers": "^7.16.7", + "@babel/helper-split-export-declaration": "^7.16.7", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.16.7.tgz", + "integrity": "sha512-gN72G9bcmenVILj//sv1zLNaPyYcOzUho2lIJBMh/iakJ9ygCo/hEF9cpGb61SCMEDxbbyBoVQxrt+bWKu5KGw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.16.7.tgz", + "integrity": "sha512-VqAwhTHBnu5xBVDCvrvqJbtLUa++qZaWC0Fgr2mqokBlulZARGyIvZDoqbPlPaKImQ9dKAcCzbv+ul//uqu70A==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-dotall-regex": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.16.7.tgz", + "integrity": "sha512-Lyttaao2SjZF6Pf4vk1dVKv8YypMpomAbygW+mU5cYP3S5cWTfCJjG8xV6CFdzGFlfWK81IjL9viiTvpb6G7gQ==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-keys": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.16.7.tgz", + "integrity": "sha512-03DvpbRfvWIXyK0/6QiR1KMTWeT6OcQ7tbhjrXyFS02kjuX/mu5Bvnh5SDSWHxyawit2g5aWhKwI86EE7GUnTw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-exponentiation-operator": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.16.7.tgz", + "integrity": "sha512-8UYLSlyLgRixQvlYH3J2ekXFHDFLQutdy7FfFAMm3CPZ6q9wHCwnUyiXpQCe3gVVnQlHc5nsuiEVziteRNTXEA==", + "dependencies": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-for-of": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.16.7.tgz", + "integrity": "sha512-/QZm9W92Ptpw7sjI9Nx1mbcsWz33+l8kuMIQnDwgQBG5s3fAfQvkRjQ7NqXhtNcKOnPkdICmUHyCaWW06HCsqg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-function-name": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.16.7.tgz", + "integrity": "sha512-SU/C68YVwTRxqWj5kgsbKINakGag0KTgq9f2iZEXdStoAbOzLHEBRYzImmA6yFo8YZhJVflvXmIHUO7GWHmxxA==", + "dependencies": { + "@babel/helper-compilation-targets": "^7.16.7", + "@babel/helper-function-name": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-literals": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.16.7.tgz", + "integrity": "sha512-6tH8RTpTWI0s2sV6uq3e/C9wPo4PTqqZps4uF0kzQ9/xPLFQtipynvmT1g/dOfEJ+0EQsHhkQ/zyRId8J2b8zQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-member-expression-literals": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.16.7.tgz", + "integrity": "sha512-mBruRMbktKQwbxaJof32LT9KLy2f3gH+27a5XSuXo6h7R3vqltl0PgZ80C8ZMKw98Bf8bqt6BEVi3svOh2PzMw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-amd": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.16.7.tgz", + "integrity": "sha512-KaaEtgBL7FKYwjJ/teH63oAmE3lP34N3kshz8mm4VMAw7U3PxjVwwUmxEFksbgsNUaO3wId9R2AVQYSEGRa2+g==", + "dependencies": { + "@babel/helper-module-transforms": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "babel-plugin-dynamic-import-node": "^2.3.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.16.8.tgz", + "integrity": "sha512-oflKPvsLT2+uKQopesJt3ApiaIS2HW+hzHFcwRNtyDGieAeC/dIHZX8buJQ2J2X1rxGPy4eRcUijm3qcSPjYcA==", + "dependencies": { + "@babel/helper-module-transforms": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-simple-access": "^7.16.7", + "babel-plugin-dynamic-import-node": "^2.3.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-systemjs": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.16.7.tgz", + "integrity": "sha512-DuK5E3k+QQmnOqBR9UkusByy5WZWGRxfzV529s9nPra1GE7olmxfqO2FHobEOYSPIjPBTr4p66YDcjQnt8cBmw==", + "dependencies": { + "@babel/helper-hoist-variables": "^7.16.7", + "@babel/helper-module-transforms": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-validator-identifier": "^7.16.7", + "babel-plugin-dynamic-import-node": "^2.3.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-umd": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.16.7.tgz", + "integrity": "sha512-EMh7uolsC8O4xhudF2F6wedbSHm1HHZ0C6aJ7K67zcDNidMzVcxWdGr+htW9n21klm+bOn+Rx4CBsAntZd3rEQ==", + "dependencies": { + "@babel/helper-module-transforms": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.16.8.tgz", + "integrity": "sha512-j3Jw+n5PvpmhRR+mrgIh04puSANCk/T/UA3m3P1MjJkhlK906+ApHhDIqBQDdOgL/r1UYpz4GNclTXxyZrYGSw==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-new-target": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.16.7.tgz", + "integrity": "sha512-xiLDzWNMfKoGOpc6t3U+etCE2yRnn3SM09BXqWPIZOBpL2gvVrBWUKnsJx0K/ADi5F5YC5f8APFfWrz25TdlGg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-super": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.16.7.tgz", + "integrity": "sha512-14J1feiQVWaGvRxj2WjyMuXS2jsBkgB3MdSN5HuC2G5nRspa5RK9COcs82Pwy5BuGcjb+fYaUj94mYcOj7rCvw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-replace-supers": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-parameters": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.16.7.tgz", + "integrity": "sha512-AT3MufQ7zZEhU2hwOA11axBnExW0Lszu4RL/tAlUJBuNoRak+wehQW8h6KcXOcgjY42fHtDxswuMhMjFEuv/aw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-property-literals": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.16.7.tgz", + "integrity": "sha512-z4FGr9NMGdoIl1RqavCqGG+ZuYjfZ/hkCIeuH6Do7tXmSm0ls11nYVSJqFEUOSJbDab5wC6lRE/w6YjVcr6Hqw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regenerator": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.16.7.tgz", + "integrity": "sha512-mF7jOgGYCkSJagJ6XCujSQg+6xC1M77/03K2oBmVJWoFGNUtnVJO4WHKJk3dnPC8HCcj4xBQP1Egm8DWh3Pb3Q==", + "dependencies": { + "regenerator-transform": "^0.14.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-reserved-words": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.16.7.tgz", + "integrity": "sha512-KQzzDnZ9hWQBjwi5lpY5v9shmm6IVG0U9pB18zvMu2i4H90xpT4gmqwPYsn8rObiadYe2M0gmgsiOIF5A/2rtg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime": { + "version": "7.16.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.16.10.tgz", + "integrity": "sha512-9nwTiqETv2G7xI4RvXHNfpGdr8pAA+Q/YtN3yLK7OoK7n9OibVm/xymJ838a9A6E/IciOLPj82lZk0fW6O4O7w==", + "dependencies": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "babel-plugin-polyfill-corejs2": "^0.3.0", + "babel-plugin-polyfill-corejs3": "^0.5.0", + "babel-plugin-polyfill-regenerator": "^0.3.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-shorthand-properties": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.16.7.tgz", + "integrity": "sha512-hah2+FEnoRoATdIb05IOXf+4GzXYTq75TVhIn1PewihbpyrNWUt2JbudKQOETWw6QpLe+AIUpJ5MVLYTQbeeUg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-spread": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.16.7.tgz", + "integrity": "sha512-+pjJpgAngb53L0iaA5gU/1MLXJIfXcYepLgXB3esVRf4fqmj8f2cxM3/FKaHsZms08hFQJkFccEWuIpm429TXg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-sticky-regex": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.16.7.tgz", + "integrity": "sha512-NJa0Bd/87QV5NZZzTuZG5BPJjLYadeSZ9fO6oOUoL4iQx+9EEuw/eEM92SrsT19Yc2jgB1u1hsjqDtH02c3Drw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-template-literals": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.16.7.tgz", + "integrity": "sha512-VwbkDDUeenlIjmfNeDX/V0aWrQH2QiVyJtwymVQSzItFDTpxfyJh3EVaQiS0rIN/CqbLGr0VcGmuwyTdZtdIsA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typeof-symbol": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.16.7.tgz", + "integrity": "sha512-p2rOixCKRJzpg9JB4gjnG4gjWkWa89ZoYUnl9snJ1cWIcTH/hvxZqfO+WjG6T8DRBpctEol5jw1O5rA8gkCokQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-escapes": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.16.7.tgz", + "integrity": "sha512-TAV5IGahIz3yZ9/Hfv35TV2xEm+kaBDaZQCn2S/hG9/CZ0DktxJv9eKfPc7yYCvOYR4JGx1h8C+jcSOvgaaI/Q==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-regex": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.16.7.tgz", + "integrity": "sha512-oC5tYYKw56HO75KZVLQ+R/Nl3Hro9kf8iG0hXoaHP7tjAyCpvqBiSNe6vGrZni1Z6MggmUOC6A7VP7AVmw225Q==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-env": { + "version": "7.16.11", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.16.11.tgz", + "integrity": "sha512-qcmWG8R7ZW6WBRPZK//y+E3Cli151B20W1Rv7ln27vuPaXU/8TKms6jFdiJtF7UDTxcrb7mZd88tAeK9LjdT8g==", + "dependencies": { + "@babel/compat-data": "^7.16.8", + "@babel/helper-compilation-targets": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-validator-option": "^7.16.7", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.16.7", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.16.7", + "@babel/plugin-proposal-async-generator-functions": "^7.16.8", + "@babel/plugin-proposal-class-properties": "^7.16.7", + "@babel/plugin-proposal-class-static-block": "^7.16.7", + "@babel/plugin-proposal-dynamic-import": "^7.16.7", + "@babel/plugin-proposal-export-namespace-from": "^7.16.7", + "@babel/plugin-proposal-json-strings": "^7.16.7", + "@babel/plugin-proposal-logical-assignment-operators": "^7.16.7", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.7", + "@babel/plugin-proposal-numeric-separator": "^7.16.7", + "@babel/plugin-proposal-object-rest-spread": "^7.16.7", + "@babel/plugin-proposal-optional-catch-binding": "^7.16.7", + "@babel/plugin-proposal-optional-chaining": "^7.16.7", + "@babel/plugin-proposal-private-methods": "^7.16.11", + "@babel/plugin-proposal-private-property-in-object": "^7.16.7", + "@babel/plugin-proposal-unicode-property-regex": "^7.16.7", + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5", + "@babel/plugin-transform-arrow-functions": "^7.16.7", + "@babel/plugin-transform-async-to-generator": "^7.16.8", + "@babel/plugin-transform-block-scoped-functions": "^7.16.7", + "@babel/plugin-transform-block-scoping": "^7.16.7", + "@babel/plugin-transform-classes": "^7.16.7", + "@babel/plugin-transform-computed-properties": "^7.16.7", + "@babel/plugin-transform-destructuring": "^7.16.7", + "@babel/plugin-transform-dotall-regex": "^7.16.7", + "@babel/plugin-transform-duplicate-keys": "^7.16.7", + "@babel/plugin-transform-exponentiation-operator": "^7.16.7", + "@babel/plugin-transform-for-of": "^7.16.7", + "@babel/plugin-transform-function-name": "^7.16.7", + "@babel/plugin-transform-literals": "^7.16.7", + "@babel/plugin-transform-member-expression-literals": "^7.16.7", + "@babel/plugin-transform-modules-amd": "^7.16.7", + "@babel/plugin-transform-modules-commonjs": "^7.16.8", + "@babel/plugin-transform-modules-systemjs": "^7.16.7", + "@babel/plugin-transform-modules-umd": "^7.16.7", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.16.8", + "@babel/plugin-transform-new-target": "^7.16.7", + "@babel/plugin-transform-object-super": "^7.16.7", + "@babel/plugin-transform-parameters": "^7.16.7", + "@babel/plugin-transform-property-literals": "^7.16.7", + "@babel/plugin-transform-regenerator": "^7.16.7", + "@babel/plugin-transform-reserved-words": "^7.16.7", + "@babel/plugin-transform-shorthand-properties": "^7.16.7", + "@babel/plugin-transform-spread": "^7.16.7", + "@babel/plugin-transform-sticky-regex": "^7.16.7", + "@babel/plugin-transform-template-literals": "^7.16.7", + "@babel/plugin-transform-typeof-symbol": "^7.16.7", + "@babel/plugin-transform-unicode-escapes": "^7.16.7", + "@babel/plugin-transform-unicode-regex": "^7.16.7", + "@babel/preset-modules": "^0.1.5", + "@babel/types": "^7.16.8", + "babel-plugin-polyfill-corejs2": "^0.3.0", + "babel-plugin-polyfill-corejs3": "^0.5.0", + "babel-plugin-polyfill-regenerator": "^0.3.0", + "core-js-compat": "^3.20.2", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-modules": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz", + "integrity": "sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", + "@babel/plugin-transform-dotall-regex": "^7.4.4", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.7.tgz", + "integrity": "sha512-9E9FJowqAsytyOY6LG+1KuueckRL+aQW+mKvXRXnuFGyRAyepJPmEo9vgMfXUA6O9u3IeEdv9MAkppFcaQwogQ==", + "dependencies": { + "regenerator-runtime": "^0.13.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz", + "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==", + "dependencies": { + "@babel/code-frame": "^7.16.7", + "@babel/parser": "^7.16.7", + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.16.10", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.16.10.tgz", + "integrity": "sha512-yzuaYXoRJBGMlBhsMJoUW7G1UmSb/eXr/JHYM/MsOJgavJibLwASijW7oXBdw3NQ6T0bW7Ty5P/VarOs9cHmqw==", + "dependencies": { + "@babel/code-frame": "^7.16.7", + "@babel/generator": "^7.16.8", + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-function-name": "^7.16.7", + "@babel/helper-hoist-variables": "^7.16.7", + "@babel/helper-split-export-declaration": "^7.16.7", + "@babel/parser": "^7.16.10", + "@babel/types": "^7.16.8", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@babel/traverse/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/@babel/types": { + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.8.tgz", + "integrity": "sha512-smN2DQc5s4M7fntyjGtyIPbRJv6wW4rU/94fmYJ7PKQuZkC0qGMHXJbg6sNGt12JmVr4k5YaptI/XtiLJBnmIg==", + "dependencies": { + "@babel/helper-validator-identifier": "^7.16.7", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@cosmos-ui/vue": { + "version": "0.35.0", + "resolved": "https://registry.npmjs.org/@cosmos-ui/vue/-/vue-0.35.0.tgz", + "integrity": "sha512-WTCJBWSoiDckgvXWPByKkQ7ZVSf9LSMsizIAHBnsi0Zp3GOaEqPNBpgjGt2JEhpDPr7+YwyIgmqQ0S3D+Hq5iQ==", + "dependencies": { + "algoliasearch": "^4.1.0", + "axios": "^0.19.2", + "clipboard-copy": "^3.1.0", + "fuse.js": "^3.4.6", + "hotkeys-js": "^3.7.3", + "js-base64": "^2.5.2", + "lodash": "^4.17.15", + "markdown-it": "^10.0.0", + "prismjs": "^1.19.0", + "querystring": "^0.2.0", + "tiny-cookie": "^2.3.1", + "vue": "^2.6.10" + } + }, + "node_modules/@cosmos-ui/vue/node_modules/axios": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz", + "integrity": "sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==", + "deprecated": "Critical security vulnerability fixed in v0.21.1. For more information, see https://github.com/axios/axios/pull/3410", + "dependencies": { + "follow-redirects": "1.5.10" + } + }, + "node_modules/@cosmos-ui/vue/node_modules/clipboard-copy": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/clipboard-copy/-/clipboard-copy-3.2.0.tgz", + "integrity": "sha512-vooFaGFL6ulEP1liiaWFBmmfuPm3cY3y7T9eB83ZTnYc/oFeAKsq3NcDrOkBC8XaauEE8zHQwI7k0+JSYiVQSQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/@cosmos-ui/vue/node_modules/entities": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.3.tgz", + "integrity": "sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ==" + }, + "node_modules/@cosmos-ui/vue/node_modules/markdown-it": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-10.0.0.tgz", + "integrity": "sha512-YWOP1j7UbDNz+TumYP1kpwnP0aEa711cJjrAQrzd0UXlbJfc5aAq0F/PZHjiioqDC1NKgvIMX+o+9Bk7yuM2dg==", + "dependencies": { + "argparse": "^1.0.7", + "entities": "~2.0.0", + "linkify-it": "^2.0.0", + "mdurl": "^1.0.1", + "uc.micro": "^1.0.5" + }, + "bin": { + "markdown-it": "bin/markdown-it.js" + } + }, + "node_modules/@mrmlnc/readdir-enhanced": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz", + "integrity": "sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g==", + "dependencies": { + "call-me-maybe": "^1.0.1", + "glob-to-regexp": "^0.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz", + "integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/@sindresorhus/is": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", + "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/@szmarczak/http-timer": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", + "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", + "dependencies": { + "defer-to-connect": "^1.0.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/connect-history-api-fallback": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz", + "integrity": "sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw==", + "dependencies": { + "@types/express-serve-static-core": "*", + "@types/node": "*" + } + }, + "node_modules/@types/express": { + "version": "4.17.13", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz", + "integrity": "sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.18", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.17.28", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.28.tgz", + "integrity": "sha512-P1BJAEAW3E2DJUlkgq4tOL3RyMunoWXqbSCygWo5ZIWTjUgN1YnaXWW4VWl/oc8vs/XoYibEGBKP0uZyF4AHig==", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, + "node_modules/@types/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==", + "dependencies": { + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "node_modules/@types/highlight.js": { + "version": "9.12.4", + "resolved": "https://registry.npmjs.org/@types/highlight.js/-/highlight.js-9.12.4.tgz", + "integrity": "sha512-t2szdkwmg2JJyuCM20e8kR2X59WCE5Zkl4bzm1u1Oukjm79zpbiAv+QjnwLnuuV0WHEcX2NgUItu0pAMKuOPww==" + }, + "node_modules/@types/http-proxy": { + "version": "1.17.8", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.8.tgz", + "integrity": "sha512-5kPLG5BKpWYkw/LVOGWpiq3nEVqxiN32rTgI53Sk12/xHFQ2rG3ehI9IO+O3W2QoKeyB92dJkoka8SUm6BX1pA==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.9", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", + "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==" + }, + "node_modules/@types/linkify-it": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-3.0.2.tgz", + "integrity": "sha512-HZQYqbiFVWufzCwexrvh694SOim8z2d+xJl5UNamcvQFejLY/2YUtzXHYi3cHdI7PMlS8ejH2slRAOJQ32aNbA==" + }, + "node_modules/@types/markdown-it": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-10.0.3.tgz", + "integrity": "sha512-daHJk22isOUvNssVGF2zDnnSyxHhFYhtjeX4oQaKD6QzL3ZR1QSgiD1g+Q6/WSWYVogNXYDXODtbgW/WiFCtyw==", + "dependencies": { + "@types/highlight.js": "^9.7.0", + "@types/linkify-it": "*", + "@types/mdurl": "*", + "highlight.js": "^9.7.0" + } + }, + "node_modules/@types/mdurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-1.0.2.tgz", + "integrity": "sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==" + }, + "node_modules/@types/mime": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", + "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==" + }, + "node_modules/@types/minimatch": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz", + "integrity": "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==" + }, + "node_modules/@types/node": { + "version": "17.0.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.12.tgz", + "integrity": "sha512-4YpbAsnJXWYK/fpTVFlMIcUIho2AYCi4wg5aNPrG1ng7fn/1/RZfCIpRCiBX+12RVa34RluilnvCqD+g3KiSiA==" + }, + "node_modules/@types/q": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.5.tgz", + "integrity": "sha512-L28j2FcJfSZOnL1WBjDYp2vUHCeIFlyYI/53EwD/rKUBQ7MtUUfbQWiyKJGpcnv4/WgrhWsFKrcPstcAt/J0tQ==" + }, + "node_modules/@types/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==" + }, + "node_modules/@types/range-parser": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", + "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" + }, + "node_modules/@types/serve-static": { + "version": "1.13.10", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz", + "integrity": "sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/source-list-map": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.2.tgz", + "integrity": "sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA==" + }, + "node_modules/@types/tapable": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/tapable/-/tapable-1.0.8.tgz", + "integrity": "sha512-ipixuVrh2OdNmauvtT51o3d8z12p6LtFW9in7U79der/kwejjdNchQC5UMn5u/KxNoM7VHHOs/l8KS8uHxhODQ==" + }, + "node_modules/@types/uglify-js": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.13.1.tgz", + "integrity": "sha512-O3MmRAk6ZuAKa9CHgg0Pr0+lUOqoMLpc9AS4R8ano2auvsg7IE8syF3Xh/NPr26TWklxYcqoEEFdzLLs1fV9PQ==", + "dependencies": { + "source-map": "^0.6.1" + } + }, + "node_modules/@types/uglify-js/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@types/webpack": { + "version": "4.41.32", + "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.41.32.tgz", + "integrity": "sha512-cb+0ioil/7oz5//7tZUSwbrSAN/NWHrQylz5cW8G0dWTcF/g+/dSdMlKVZspBYuMAN1+WnwHrkxiRrLcwd0Heg==", + "dependencies": { + "@types/node": "*", + "@types/tapable": "^1", + "@types/uglify-js": "*", + "@types/webpack-sources": "*", + "anymatch": "^3.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/@types/webpack-dev-server": { + "version": "3.11.6", + "resolved": "https://registry.npmjs.org/@types/webpack-dev-server/-/webpack-dev-server-3.11.6.tgz", + "integrity": "sha512-XCph0RiiqFGetukCTC3KVnY1jwLcZ84illFRMbyFzCcWl90B/76ew0tSqF46oBhnLC4obNDG7dMO0JfTN0MgMQ==", + "dependencies": { + "@types/connect-history-api-fallback": "*", + "@types/express": "*", + "@types/serve-static": "*", + "@types/webpack": "^4", + "http-proxy-middleware": "^1.0.0" + } + }, + "node_modules/@types/webpack-sources": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@types/webpack-sources/-/webpack-sources-3.2.0.tgz", + "integrity": "sha512-Ft7YH3lEVRQ6ls8k4Ff1oB4jN6oy/XmU6tQISKdhfh+1mR+viZFphS6WL0IrtDOzvefmJg5a0s7ZQoRXwqTEFg==", + "dependencies": { + "@types/node": "*", + "@types/source-list-map": "*", + "source-map": "^0.7.3" + } + }, + "node_modules/@types/webpack/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@vue/babel-helper-vue-jsx-merge-props": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@vue/babel-helper-vue-jsx-merge-props/-/babel-helper-vue-jsx-merge-props-1.2.1.tgz", + "integrity": "sha512-QOi5OW45e2R20VygMSNhyQHvpdUwQZqGPc748JLGCYEy+yp8fNFNdbNIGAgZmi9e+2JHPd6i6idRuqivyicIkA==" + }, + "node_modules/@vue/babel-helper-vue-transform-on": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@vue/babel-helper-vue-transform-on/-/babel-helper-vue-transform-on-1.0.2.tgz", + "integrity": "sha512-hz4R8tS5jMn8lDq6iD+yWL6XNB699pGIVLk7WSJnn1dbpjaazsjZQkieJoRX6gW5zpYSCFqQ7jUquPNY65tQYA==" + }, + "node_modules/@vue/babel-plugin-jsx": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@vue/babel-plugin-jsx/-/babel-plugin-jsx-1.1.1.tgz", + "integrity": "sha512-j2uVfZjnB5+zkcbc/zsOc0fSNGCMMjaEXP52wdwdIfn0qjFfEYpYZBFKFg+HHnQeJCVrjOeO0YxgaL7DMrym9w==", + "dependencies": { + "@babel/helper-module-imports": "^7.0.0", + "@babel/plugin-syntax-jsx": "^7.0.0", + "@babel/template": "^7.0.0", + "@babel/traverse": "^7.0.0", + "@babel/types": "^7.0.0", + "@vue/babel-helper-vue-transform-on": "^1.0.2", + "camelcase": "^6.0.0", + "html-tags": "^3.1.0", + "svg-tags": "^1.0.0" + } + }, + "node_modules/@vue/babel-plugin-transform-vue-jsx": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@vue/babel-plugin-transform-vue-jsx/-/babel-plugin-transform-vue-jsx-1.2.1.tgz", + "integrity": "sha512-HJuqwACYehQwh1fNT8f4kyzqlNMpBuUK4rSiSES5D4QsYncv5fxFsLyrxFPG2ksO7t5WP+Vgix6tt6yKClwPzA==", + "dependencies": { + "@babel/helper-module-imports": "^7.0.0", + "@babel/plugin-syntax-jsx": "^7.2.0", + "@vue/babel-helper-vue-jsx-merge-props": "^1.2.1", + "html-tags": "^2.0.0", + "lodash.kebabcase": "^4.1.1", + "svg-tags": "^1.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@vue/babel-plugin-transform-vue-jsx/node_modules/html-tags": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-2.0.0.tgz", + "integrity": "sha1-ELMKOGCF9Dzt41PMj6fLDe7qZos=", + "engines": { + "node": ">=4" + } + }, + "node_modules/@vue/babel-preset-app": { + "version": "4.5.15", + "resolved": "https://registry.npmjs.org/@vue/babel-preset-app/-/babel-preset-app-4.5.15.tgz", + "integrity": "sha512-J+YttzvwRfV1BPczf8r3qCevznYk+jh531agVF+5EYlHF4Sgh/cGXTz9qkkiux3LQgvhEGXgmCteg1n38WuuKg==", + "dependencies": { + "@babel/core": "^7.11.0", + "@babel/helper-compilation-targets": "^7.9.6", + "@babel/helper-module-imports": "^7.8.3", + "@babel/plugin-proposal-class-properties": "^7.8.3", + "@babel/plugin-proposal-decorators": "^7.8.3", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-jsx": "^7.8.3", + "@babel/plugin-transform-runtime": "^7.11.0", + "@babel/preset-env": "^7.11.0", + "@babel/runtime": "^7.11.0", + "@vue/babel-plugin-jsx": "^1.0.3", + "@vue/babel-preset-jsx": "^1.2.4", + "babel-plugin-dynamic-import-node": "^2.3.3", + "core-js": "^3.6.5", + "core-js-compat": "^3.6.5", + "semver": "^6.1.0" + }, + "peerDependencies": { + "@babel/core": "*", + "core-js": "^3", + "vue": "^2 || ^3.0.0-0" + }, + "peerDependenciesMeta": { + "core-js": { + "optional": true + }, + "vue": { + "optional": true + } + } + }, + "node_modules/@vue/babel-preset-jsx": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@vue/babel-preset-jsx/-/babel-preset-jsx-1.2.4.tgz", + "integrity": "sha512-oRVnmN2a77bYDJzeGSt92AuHXbkIxbf/XXSE3klINnh9AXBmVS1DGa1f0d+dDYpLfsAKElMnqKTQfKn7obcL4w==", + "dependencies": { + "@vue/babel-helper-vue-jsx-merge-props": "^1.2.1", + "@vue/babel-plugin-transform-vue-jsx": "^1.2.1", + "@vue/babel-sugar-composition-api-inject-h": "^1.2.1", + "@vue/babel-sugar-composition-api-render-instance": "^1.2.4", + "@vue/babel-sugar-functional-vue": "^1.2.2", + "@vue/babel-sugar-inject-h": "^1.2.2", + "@vue/babel-sugar-v-model": "^1.2.3", + "@vue/babel-sugar-v-on": "^1.2.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@vue/babel-sugar-composition-api-inject-h": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@vue/babel-sugar-composition-api-inject-h/-/babel-sugar-composition-api-inject-h-1.2.1.tgz", + "integrity": "sha512-4B3L5Z2G+7s+9Bwbf+zPIifkFNcKth7fQwekVbnOA3cr3Pq71q71goWr97sk4/yyzH8phfe5ODVzEjX7HU7ItQ==", + "dependencies": { + "@babel/plugin-syntax-jsx": "^7.2.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@vue/babel-sugar-composition-api-render-instance": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@vue/babel-sugar-composition-api-render-instance/-/babel-sugar-composition-api-render-instance-1.2.4.tgz", + "integrity": "sha512-joha4PZznQMsxQYXtR3MnTgCASC9u3zt9KfBxIeuI5g2gscpTsSKRDzWQt4aqNIpx6cv8On7/m6zmmovlNsG7Q==", + "dependencies": { + "@babel/plugin-syntax-jsx": "^7.2.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@vue/babel-sugar-functional-vue": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@vue/babel-sugar-functional-vue/-/babel-sugar-functional-vue-1.2.2.tgz", + "integrity": "sha512-JvbgGn1bjCLByIAU1VOoepHQ1vFsroSA/QkzdiSs657V79q6OwEWLCQtQnEXD/rLTA8rRit4rMOhFpbjRFm82w==", + "dependencies": { + "@babel/plugin-syntax-jsx": "^7.2.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@vue/babel-sugar-inject-h": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@vue/babel-sugar-inject-h/-/babel-sugar-inject-h-1.2.2.tgz", + "integrity": "sha512-y8vTo00oRkzQTgufeotjCLPAvlhnpSkcHFEp60+LJUwygGcd5Chrpn5480AQp/thrxVm8m2ifAk0LyFel9oCnw==", + "dependencies": { + "@babel/plugin-syntax-jsx": "^7.2.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@vue/babel-sugar-v-model": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@vue/babel-sugar-v-model/-/babel-sugar-v-model-1.2.3.tgz", + "integrity": "sha512-A2jxx87mySr/ulAsSSyYE8un6SIH0NWHiLaCWpodPCVOlQVODCaSpiR4+IMsmBr73haG+oeCuSvMOM+ttWUqRQ==", + "dependencies": { + "@babel/plugin-syntax-jsx": "^7.2.0", + "@vue/babel-helper-vue-jsx-merge-props": "^1.2.1", + "@vue/babel-plugin-transform-vue-jsx": "^1.2.1", + "camelcase": "^5.0.0", + "html-tags": "^2.0.0", + "svg-tags": "^1.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@vue/babel-sugar-v-model/node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/@vue/babel-sugar-v-model/node_modules/html-tags": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-2.0.0.tgz", + "integrity": "sha1-ELMKOGCF9Dzt41PMj6fLDe7qZos=", + "engines": { + "node": ">=4" + } + }, + "node_modules/@vue/babel-sugar-v-on": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@vue/babel-sugar-v-on/-/babel-sugar-v-on-1.2.3.tgz", + "integrity": "sha512-kt12VJdz/37D3N3eglBywV8GStKNUhNrsxChXIV+o0MwVXORYuhDTHJRKPgLJRb/EY3vM2aRFQdxJBp9CLikjw==", + "dependencies": { + "@babel/plugin-syntax-jsx": "^7.2.0", + "@vue/babel-plugin-transform-vue-jsx": "^1.2.1", + "camelcase": "^5.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@vue/babel-sugar-v-on/node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/@vue/component-compiler-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@vue/component-compiler-utils/-/component-compiler-utils-3.3.0.tgz", + "integrity": "sha512-97sfH2mYNU+2PzGrmK2haqffDpVASuib9/w2/noxiFi31Z54hW+q3izKQXXQZSNhtiUpAI36uSuYepeBe4wpHQ==", + "dependencies": { + "consolidate": "^0.15.1", + "hash-sum": "^1.0.2", + "lru-cache": "^4.1.2", + "merge-source-map": "^1.1.0", + "postcss": "^7.0.36", + "postcss-selector-parser": "^6.0.2", + "source-map": "~0.6.1", + "vue-template-es2015-compiler": "^1.9.0" + }, + "optionalDependencies": { + "prettier": "^1.18.2 || ^2.0.0" + } + }, + "node_modules/@vue/component-compiler-utils/node_modules/lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dependencies": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "node_modules/@vue/component-compiler-utils/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@vue/component-compiler-utils/node_modules/yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" + }, + "node_modules/@vuepress/core": { + "version": "1.9.7", + "resolved": "https://registry.npmjs.org/@vuepress/core/-/core-1.9.7.tgz", + "integrity": "sha512-u5eb1mfNLV8uG2UuxlvpB/FkrABxeMHqymTsixOnsOg2REziv9puEIbqaZ5BjLPvbCDvSj6rn+DwjENmBU+frQ==", + "dependencies": { + "@babel/core": "^7.8.4", + "@vue/babel-preset-app": "^4.1.2", + "@vuepress/markdown": "1.9.7", + "@vuepress/markdown-loader": "1.9.7", + "@vuepress/plugin-last-updated": "1.9.7", + "@vuepress/plugin-register-components": "1.9.7", + "@vuepress/shared-utils": "1.9.7", + "@vuepress/types": "1.9.7", + "autoprefixer": "^9.5.1", + "babel-loader": "^8.0.4", + "bundle-require": "2.1.8", + "cache-loader": "^3.0.0", + "chokidar": "^2.0.3", + "connect-history-api-fallback": "^1.5.0", + "copy-webpack-plugin": "^5.0.2", + "core-js": "^3.6.4", + "cross-spawn": "^6.0.5", + "css-loader": "^2.1.1", + "esbuild": "0.14.7", + "file-loader": "^3.0.1", + "js-yaml": "^3.13.1", + "lru-cache": "^5.1.1", + "mini-css-extract-plugin": "0.6.0", + "optimize-css-assets-webpack-plugin": "^5.0.1", + "portfinder": "^1.0.13", + "postcss-loader": "^3.0.0", + "postcss-safe-parser": "^4.0.1", + "toml": "^3.0.0", + "url-loader": "^1.0.1", + "vue": "^2.6.10", + "vue-loader": "^15.7.1", + "vue-router": "^3.4.5", + "vue-server-renderer": "^2.6.10", + "vue-template-compiler": "^2.6.10", + "vuepress-html-webpack-plugin": "^3.2.0", + "vuepress-plugin-container": "^2.0.2", + "webpack": "^4.8.1", + "webpack-chain": "^6.0.0", + "webpack-dev-server": "^3.5.1", + "webpack-merge": "^4.1.2", + "webpackbar": "3.2.0" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/@vuepress/markdown": { + "version": "1.9.7", + "resolved": "https://registry.npmjs.org/@vuepress/markdown/-/markdown-1.9.7.tgz", + "integrity": "sha512-DFOjYkwV6fT3xXTGdTDloeIrT1AbwJ9pwefmrp0rMgC6zOz3XUJn6qqUwcYFO5mNBWpbiFQ3JZirCtgOe+xxBA==", + "dependencies": { + "@vuepress/shared-utils": "1.9.7", + "markdown-it": "^8.4.1", + "markdown-it-anchor": "^5.0.2", + "markdown-it-chain": "^1.3.0", + "markdown-it-emoji": "^1.4.0", + "markdown-it-table-of-contents": "^0.4.0", + "prismjs": "^1.13.0" + } + }, + "node_modules/@vuepress/markdown-loader": { + "version": "1.9.7", + "resolved": "https://registry.npmjs.org/@vuepress/markdown-loader/-/markdown-loader-1.9.7.tgz", + "integrity": "sha512-mxXF8FtX/QhOg/UYbe4Pr1j5tcf/aOEI502rycTJ3WF2XAtOmewjkGV4eAA6f6JmuM/fwzOBMZKDyy9/yo2I6Q==", + "dependencies": { + "@vuepress/markdown": "1.9.7", + "loader-utils": "^1.1.0", + "lru-cache": "^5.1.1" + } + }, + "node_modules/@vuepress/markdown/node_modules/entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" + }, + "node_modules/@vuepress/markdown/node_modules/markdown-it": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-8.4.2.tgz", + "integrity": "sha512-GcRz3AWTqSUphY3vsUqQSFMbgR38a4Lh3GWlHRh/7MRwz8mcu9n2IO7HOh+bXHrR9kOPDl5RNCaEsrneb+xhHQ==", + "dependencies": { + "argparse": "^1.0.7", + "entities": "~1.1.1", + "linkify-it": "^2.0.0", + "mdurl": "^1.0.1", + "uc.micro": "^1.0.5" + }, + "bin": { + "markdown-it": "bin/markdown-it.js" + } + }, + "node_modules/@vuepress/plugin-active-header-links": { + "version": "1.9.7", + "resolved": "https://registry.npmjs.org/@vuepress/plugin-active-header-links/-/plugin-active-header-links-1.9.7.tgz", + "integrity": "sha512-G1M8zuV9Og3z8WBiKkWrofG44NEXsHttc1MYreDXfeWh/NLjr9q1GPCEXtiCjrjnHZHB3cSQTKnTqAHDq35PGA==", + "dependencies": { + "@vuepress/types": "1.9.7", + "lodash.debounce": "^4.0.8" + } + }, + "node_modules/@vuepress/plugin-google-analytics": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/@vuepress/plugin-google-analytics/-/plugin-google-analytics-1.8.2.tgz", + "integrity": "sha512-BMFayLzT2BvXmnhM9mDHw0UPU7J0pH1X9gQA4HmZxOf7f3+atK5eJGsc1Ia/+1FTG2ESvhFLUU/CC3h5arjEJw==" + }, + "node_modules/@vuepress/plugin-last-updated": { + "version": "1.9.7", + "resolved": "https://registry.npmjs.org/@vuepress/plugin-last-updated/-/plugin-last-updated-1.9.7.tgz", + "integrity": "sha512-FiFBOl49dlFRjbLRnRAv77HDWfe+S/eCPtMQobq4/O3QWuL3Na5P4fCTTVzq1K7rWNO9EPsWNB2Jb26ndlQLKQ==", + "dependencies": { + "@vuepress/types": "1.9.7", + "cross-spawn": "^6.0.5" + } + }, + "node_modules/@vuepress/plugin-nprogress": { + "version": "1.9.7", + "resolved": "https://registry.npmjs.org/@vuepress/plugin-nprogress/-/plugin-nprogress-1.9.7.tgz", + "integrity": "sha512-sI148igbdRfLgyzB8PdhbF51hNyCDYXsBn8bBWiHdzcHBx974sVNFKtfwdIZcSFsNrEcg6zo8YIrQ+CO5vlUhQ==", + "dependencies": { + "@vuepress/types": "1.9.7", + "nprogress": "^0.2.0" + } + }, + "node_modules/@vuepress/plugin-register-components": { + "version": "1.9.7", + "resolved": "https://registry.npmjs.org/@vuepress/plugin-register-components/-/plugin-register-components-1.9.7.tgz", + "integrity": "sha512-l/w1nE7Dpl+LPMb8+AHSGGFYSP/t5j6H4/Wltwc2QcdzO7yqwC1YkwwhtTXvLvHOV8O7+rDg2nzvq355SFkfKA==", + "dependencies": { + "@vuepress/shared-utils": "1.9.7", + "@vuepress/types": "1.9.7" + } + }, + "node_modules/@vuepress/plugin-search": { + "version": "1.9.7", + "resolved": "https://registry.npmjs.org/@vuepress/plugin-search/-/plugin-search-1.9.7.tgz", + "integrity": "sha512-MLpbUVGLxaaHEwflFxvy0pF9gypFVUT3Q9Zc6maWE+0HDWAvzMxo6GBaj6mQPwjOqNQMf4QcN3hDzAZktA+DQg==", + "dependencies": { + "@vuepress/types": "1.9.7" + } + }, + "node_modules/@vuepress/shared-utils": { + "version": "1.9.7", + "resolved": "https://registry.npmjs.org/@vuepress/shared-utils/-/shared-utils-1.9.7.tgz", + "integrity": "sha512-lIkO/eSEspXgVHjYHa9vuhN7DuaYvkfX1+TTJDiEYXIwgwqtvkTv55C+IOdgswlt0C/OXDlJaUe1rGgJJ1+FTw==", + "dependencies": { + "chalk": "^2.3.2", + "escape-html": "^1.0.3", + "fs-extra": "^7.0.1", + "globby": "^9.2.0", + "gray-matter": "^4.0.1", + "hash-sum": "^1.0.2", + "semver": "^6.0.0", + "toml": "^3.0.0", + "upath": "^1.1.0" + } + }, + "node_modules/@vuepress/theme-default": { + "version": "1.9.7", + "resolved": "https://registry.npmjs.org/@vuepress/theme-default/-/theme-default-1.9.7.tgz", + "integrity": "sha512-NZzCLIl+bgJIibhkqVmk/NSku57XIuXugxAN3uiJrCw6Mu6sb3xOvbk0En3k+vS2BKHxAZ6Cx7dbCiyknDQnSA==", + "dependencies": { + "@vuepress/plugin-active-header-links": "1.9.7", + "@vuepress/plugin-nprogress": "1.9.7", + "@vuepress/plugin-search": "1.9.7", + "@vuepress/types": "1.9.7", + "docsearch.js": "^2.5.2", + "lodash": "^4.17.15", + "stylus": "^0.54.8", + "stylus-loader": "^3.0.2", + "vuepress-plugin-container": "^2.0.2", + "vuepress-plugin-smooth-scroll": "^0.0.3" + } + }, + "node_modules/@vuepress/theme-default/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@vuepress/theme-default/node_modules/stylus": { + "version": "0.54.8", + "resolved": "https://registry.npmjs.org/stylus/-/stylus-0.54.8.tgz", + "integrity": "sha512-vr54Or4BZ7pJafo2mpf0ZcwA74rpuYCZbxrHBsH8kbcXOwSfvBFwsRfpGO5OD5fhG5HDCFW737PKaawI7OqEAg==", + "dependencies": { + "css-parse": "~2.0.0", + "debug": "~3.1.0", + "glob": "^7.1.6", + "mkdirp": "~1.0.4", + "safer-buffer": "^2.1.2", + "sax": "~1.2.4", + "semver": "^6.3.0", + "source-map": "^0.7.3" + }, + "bin": { + "stylus": "bin/stylus" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@vuepress/types": { + "version": "1.9.7", + "resolved": "https://registry.npmjs.org/@vuepress/types/-/types-1.9.7.tgz", + "integrity": "sha512-moLQzkX3ED2o18dimLemUm7UVDKxhcrJmGt5C0Ng3xxrLPaQu7UqbROtEKB3YnMRt4P/CA91J+Ck+b9LmGabog==", + "dependencies": { + "@types/markdown-it": "^10.0.0", + "@types/webpack-dev-server": "^3", + "webpack-chain": "^6.0.0" + } + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz", + "integrity": "sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA==", + "dependencies": { + "@webassemblyjs/helper-module-context": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/wast-parser": "1.9.0" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.9.0.tgz", + "integrity": "sha512-TG5qcFsS8QB4g4MhrxK5TqfdNe7Ey/7YL/xN+36rRjl/BlGE/NcBvJcqsRgCP6Z92mRE+7N50pRIi8SmKUbcQA==" + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.0.tgz", + "integrity": "sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw==" + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.0.tgz", + "integrity": "sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA==" + }, + "node_modules/@webassemblyjs/helper-code-frame": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.9.0.tgz", + "integrity": "sha512-ERCYdJBkD9Vu4vtjUYe8LZruWuNIToYq/ME22igL+2vj2dQ2OOujIZr3MEFvfEaqKoVqpsFKAGsRdBSBjrIvZA==", + "dependencies": { + "@webassemblyjs/wast-printer": "1.9.0" + } + }, + "node_modules/@webassemblyjs/helper-fsm": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.9.0.tgz", + "integrity": "sha512-OPRowhGbshCb5PxJ8LocpdX9Kl0uB4XsAjl6jH/dWKlk/mzsANvhwbiULsaiqT5GZGT9qinTICdj6PLuM5gslw==" + }, + "node_modules/@webassemblyjs/helper-module-context": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.9.0.tgz", + "integrity": "sha512-MJCW8iGC08tMk2enck1aPW+BE5Cw8/7ph/VGZxwyvGbJwjktKkDK7vy7gAmMDx88D7mhDTCNKAW5tED+gZ0W8g==", + "dependencies": { + "@webassemblyjs/ast": "1.9.0" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz", + "integrity": "sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw==" + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.0.tgz", + "integrity": "sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw==", + "dependencies": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-buffer": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/wasm-gen": "1.9.0" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.9.0.tgz", + "integrity": "sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg==", + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.9.0.tgz", + "integrity": "sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw==", + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.9.0.tgz", + "integrity": "sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w==" + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.0.tgz", + "integrity": "sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw==", + "dependencies": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-buffer": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/helper-wasm-section": "1.9.0", + "@webassemblyjs/wasm-gen": "1.9.0", + "@webassemblyjs/wasm-opt": "1.9.0", + "@webassemblyjs/wasm-parser": "1.9.0", + "@webassemblyjs/wast-printer": "1.9.0" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.0.tgz", + "integrity": "sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA==", + "dependencies": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/ieee754": "1.9.0", + "@webassemblyjs/leb128": "1.9.0", + "@webassemblyjs/utf8": "1.9.0" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.0.tgz", + "integrity": "sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A==", + "dependencies": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-buffer": "1.9.0", + "@webassemblyjs/wasm-gen": "1.9.0", + "@webassemblyjs/wasm-parser": "1.9.0" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.0.tgz", + "integrity": "sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA==", + "dependencies": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-api-error": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/ieee754": "1.9.0", + "@webassemblyjs/leb128": "1.9.0", + "@webassemblyjs/utf8": "1.9.0" + } + }, + "node_modules/@webassemblyjs/wast-parser": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.9.0.tgz", + "integrity": "sha512-qsqSAP3QQ3LyZjNC/0jBJ/ToSxfYJ8kYyuiGvtn/8MK89VrNEfwj7BPQzJVHi0jGTRK2dGdJ5PRqhtjzoww+bw==", + "dependencies": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/floating-point-hex-parser": "1.9.0", + "@webassemblyjs/helper-api-error": "1.9.0", + "@webassemblyjs/helper-code-frame": "1.9.0", + "@webassemblyjs/helper-fsm": "1.9.0", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.9.0.tgz", + "integrity": "sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA==", + "dependencies": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/wast-parser": "1.9.0", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==" + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==" + }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, + "node_modules/accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "dependencies": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/agentkeepalive": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-2.2.0.tgz", + "integrity": "sha1-xdG9SxKQCPEWPyNvhuX66iAm4u8=", + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-errors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz", + "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==", + "peerDependencies": { + "ajv": ">=5.0.0" + } + }, + "node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/algoliasearch": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-4.12.0.tgz", + "integrity": "sha512-fZOMMm+F3Bi5M/MoFIz7hiuyCitJza0Hu+r8Wzz4LIQClC6YGMRq7kT6NNU1fSSoFDSeJIwMfedbbi5G9dJoVQ==", + "dependencies": { + "@algolia/cache-browser-local-storage": "4.12.0", + "@algolia/cache-common": "4.12.0", + "@algolia/cache-in-memory": "4.12.0", + "@algolia/client-account": "4.12.0", + "@algolia/client-analytics": "4.12.0", + "@algolia/client-common": "4.12.0", + "@algolia/client-personalization": "4.12.0", + "@algolia/client-search": "4.12.0", + "@algolia/logger-common": "4.12.0", + "@algolia/logger-console": "4.12.0", + "@algolia/requester-browser-xhr": "4.12.0", + "@algolia/requester-common": "4.12.0", + "@algolia/requester-node-http": "4.12.0", + "@algolia/transporter": "4.12.0" + } + }, + "node_modules/alphanum-sort": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz", + "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=" + }, + "node_modules/ansi-align": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", + "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", + "dependencies": { + "string-width": "^4.1.0" + } + }, + "node_modules/ansi-align/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-align/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/ansi-align/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-align/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-align/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-colors": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz", + "integrity": "sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-html-community": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", + "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", + "engines": [ + "node >= 0.8.0" + ], + "bin": { + "ansi-html": "bin/ansi-html" + } + }, + "node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-flatten": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", + "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==" + }, + "node_modules/array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dependencies": { + "array-uniq": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" + }, + "node_modules/asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, + "node_modules/asn1.js": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", + "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", + "dependencies": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/asn1.js/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "node_modules/assert": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz", + "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==", + "dependencies": { + "object-assign": "^4.1.1", + "util": "0.10.3" + } + }, + "node_modules/assert-never": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/assert-never/-/assert-never-1.2.1.tgz", + "integrity": "sha512-TaTivMB6pYI1kXwrFlEhLeGfOqoDNdTxjCdwRfFFkEA30Eu+k48W34nlok2EYWJfFFzqaEmichdNM7th6M5HNw==" + }, + "node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/assert/node_modules/inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=" + }, + "node_modules/assert/node_modules/util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "dependencies": { + "inherits": "2.0.1" + } + }, + "node_modules/assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/async": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "dependencies": { + "lodash": "^4.17.14" + } + }, + "node_modules/async-each": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", + "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==" + }, + "node_modules/async-limiter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "node_modules/atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "bin": { + "atob": "bin/atob.js" + }, + "engines": { + "node": ">= 4.5.0" + } + }, + "node_modules/autocomplete.js": { + "version": "0.36.0", + "resolved": "https://registry.npmjs.org/autocomplete.js/-/autocomplete.js-0.36.0.tgz", + "integrity": "sha512-jEwUXnVMeCHHutUt10i/8ZiRaCb0Wo+ZyKxeGsYwBDtw6EJHqEeDrq4UwZRD8YBSvp3g6klP678il2eeiVXN2Q==", + "dependencies": { + "immediate": "^3.2.3" + } + }, + "node_modules/autoprefixer": { + "version": "9.8.8", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.8.8.tgz", + "integrity": "sha512-eM9d/swFopRt5gdJ7jrpCwgvEMIayITpojhkkSMRsFHYuH5bkSQ4p/9qTEHtmNudUZh22Tehu7I6CxAW0IXTKA==", + "dependencies": { + "browserslist": "^4.12.0", + "caniuse-lite": "^1.0.30001109", + "normalize-range": "^0.1.2", + "num2fraction": "^1.2.2", + "picocolors": "^0.2.1", + "postcss": "^7.0.32", + "postcss-value-parser": "^4.1.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "funding": { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + } + }, + "node_modules/autoprefixer/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==" + }, + "node_modules/aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "engines": { + "node": "*" + } + }, + "node_modules/aws4": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", + "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==" + }, + "node_modules/axios": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.24.0.tgz", + "integrity": "sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==", + "dependencies": { + "follow-redirects": "^1.14.4" + } + }, + "node_modules/axios/node_modules/follow-redirects": { + "version": "1.14.7", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.7.tgz", + "integrity": "sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/babel-loader": { + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.3.tgz", + "integrity": "sha512-n4Zeta8NC3QAsuyiizu0GkmRcQ6clkV9WFUnUf1iXP//IeSKbWjofW3UHyZVwlOB4y039YQKefawyTn64Zwbuw==", + "dependencies": { + "find-cache-dir": "^3.3.1", + "loader-utils": "^1.4.0", + "make-dir": "^3.1.0", + "schema-utils": "^2.6.5" + }, + "engines": { + "node": ">= 8.9" + }, + "peerDependencies": { + "@babel/core": "^7.0.0", + "webpack": ">=2" + } + }, + "node_modules/babel-plugin-dynamic-import-node": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", + "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", + "dependencies": { + "object.assign": "^4.1.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.1.tgz", + "integrity": "sha512-v7/T6EQcNfVLfcN2X8Lulb7DjprieyLWJK/zOWH5DUYcAgex9sP3h25Q+DLsX9TloXe3y1O8l2q2Jv9q8UVB9w==", + "dependencies": { + "@babel/compat-data": "^7.13.11", + "@babel/helper-define-polyfill-provider": "^0.3.1", + "semver": "^6.1.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.1.tgz", + "integrity": "sha512-TihqEe4sQcb/QcPJvxe94/9RZuLQuF1+To4WqQcRvc+3J3gLCPIPgDKzGLG6zmQLfH3nn25heRuDNkS2KR4I8A==", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.3.1", + "core-js-compat": "^3.20.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.3.1.tgz", + "integrity": "sha512-Y2B06tvgHYt1x0yz17jGkGeeMr5FeKUu+ASJ+N6nB5lQ8Dapfg42i0OVrf8PNGJ3zKL4A23snMi1IRwrqqND7A==", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.3.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/babel-walk": { + "version": "3.0.0-canary-5", + "resolved": "https://registry.npmjs.org/babel-walk/-/babel-walk-3.0.0-canary-5.tgz", + "integrity": "sha512-GAwkz0AihzY5bkwIY5QDR+LvsRQgB/B+1foMPvi0FZPMl5fjD7ICiznUiBdLYMH1QYe6vqu4gWYytZOccLouFw==", + "dependencies": { + "@babel/types": "^7.9.6" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dependencies": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base/node_modules/define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base/node_modules/is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base/node_modules/is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base/node_modules/is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dependencies": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=" + }, + "node_modules/bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "dependencies": { + "tweetnacl": "^0.14.3" + } + }, + "node_modules/big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "engines": { + "node": "*" + } + }, + "node_modules/binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "optional": true, + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" + }, + "node_modules/bn.js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz", + "integrity": "sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw==" + }, + "node_modules/body-parser": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.1.tgz", + "integrity": "sha512-8ljfQi5eBk8EJfECMrgqNGWPEY5jWP+1IzkzkGdFFEwFQZZyaZ21UqdaHktgiMlH0xLHqIFtE/u2OYE5dOtViA==", + "dependencies": { + "bytes": "3.1.1", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.8.1", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.9.6", + "raw-body": "2.4.2", + "type-is": "~1.6.18" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/body-parser/node_modules/bytes": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.1.tgz", + "integrity": "sha512-dWe4nWO/ruEOY7HkUJ5gFt1DCFV9zPRoJr8pV0/ASQermOZjtq8jMjOprC0Kd10GLN+l7xaUPvxzJFWtxGu8Fg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/qs": { + "version": "6.9.6", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.6.tgz", + "integrity": "sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ==", + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/bonjour": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", + "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=", + "dependencies": { + "array-flatten": "^2.1.0", + "deep-equal": "^1.0.1", + "dns-equal": "^1.0.0", + "dns-txt": "^2.0.2", + "multicast-dns": "^6.0.1", + "multicast-dns-service-types": "^1.1.0" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=" + }, + "node_modules/boxen": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-4.2.0.tgz", + "integrity": "sha512-eB4uT9RGzg2odpER62bBwSLvUeGC+WbRjjyyFhGsKnc8wp/m0+hQsMUvUe3H2V0D5vw0nBdO1hCJoZo5mKeuIQ==", + "dependencies": { + "ansi-align": "^3.0.0", + "camelcase": "^5.3.1", + "chalk": "^3.0.0", + "cli-boxes": "^2.2.0", + "string-width": "^4.1.0", + "term-size": "^2.1.0", + "type-fest": "^0.8.1", + "widest-line": "^3.1.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/boxen/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/boxen/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/boxen/node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/boxen/node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/boxen/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/boxen/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/boxen/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/boxen/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/boxen/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/boxen/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/boxen/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/boxen/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/boxen/node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dependencies": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" + }, + "node_modules/browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dependencies": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "dependencies": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "node_modules/browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "dependencies": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/browserify-rsa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz", + "integrity": "sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==", + "dependencies": { + "bn.js": "^5.0.0", + "randombytes": "^2.0.1" + } + }, + "node_modules/browserify-sign": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz", + "integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==", + "dependencies": { + "bn.js": "^5.1.1", + "browserify-rsa": "^4.0.1", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "elliptic": "^6.5.3", + "inherits": "^2.0.4", + "parse-asn1": "^5.1.5", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + } + }, + "node_modules/browserify-sign/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/browserify-sign/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "dependencies": { + "pako": "~1.0.5" + } + }, + "node_modules/browserslist": { + "version": "4.19.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.19.1.tgz", + "integrity": "sha512-u2tbbG5PdKRTUoctO3NBD8FQ5HdPh1ZXPHzp1rwaa5jTc+RV9/+RlWiAIKmjRPQF+xbGM9Kklj5bZQFa2s/38A==", + "dependencies": { + "caniuse-lite": "^1.0.30001286", + "electron-to-chromium": "^1.4.17", + "escalade": "^3.1.1", + "node-releases": "^2.0.1", + "picocolors": "^1.0.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + } + }, + "node_modules/buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "dependencies": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "node_modules/buffer-indexof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", + "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==" + }, + "node_modules/buffer-json": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/buffer-json/-/buffer-json-2.0.0.tgz", + "integrity": "sha512-+jjPFVqyfF1esi9fvfUs3NqM0pH1ziZ36VP4hmA/y/Ssfo/5w5xHKfTw9BwQjoJ1w/oVtpLomqwUHKdefGyuHw==" + }, + "node_modules/buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=" + }, + "node_modules/builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=" + }, + "node_modules/bundle-require": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/bundle-require/-/bundle-require-2.1.8.tgz", + "integrity": "sha512-oOEg3A0hy/YzvNWNowtKD0pmhZKseOFweCbgyMqTIih4gRY1nJWsvrOCT27L9NbIyL5jMjTFrAUpGxxpW68Puw==", + "peerDependencies": { + "esbuild": ">=0.13" + } + }, + "node_modules/bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/cac": { + "version": "6.7.12", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.12.tgz", + "integrity": "sha512-rM7E2ygtMkJqD9c7WnFU6fruFcN3xe4FM5yUmgxhZzIKJk4uHl9U/fhwdajGFQbQuv43FAUo1Fe8gX/oIKDeSA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/cacache": { + "version": "12.0.4", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz", + "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==", + "dependencies": { + "bluebird": "^3.5.5", + "chownr": "^1.1.1", + "figgy-pudding": "^3.5.1", + "glob": "^7.1.4", + "graceful-fs": "^4.1.15", + "infer-owner": "^1.0.3", + "lru-cache": "^5.1.1", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.3", + "ssri": "^6.0.1", + "unique-filename": "^1.1.1", + "y18n": "^4.0.0" + } + }, + "node_modules/cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dependencies": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cache-loader": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/cache-loader/-/cache-loader-3.0.1.tgz", + "integrity": "sha512-HzJIvGiGqYsFUrMjAJNDbVZoG7qQA+vy9AIoKs7s9DscNfki0I589mf2w6/tW+kkFH3zyiknoWV5Jdynu6b/zw==", + "dependencies": { + "buffer-json": "^2.0.0", + "find-cache-dir": "^2.1.0", + "loader-utils": "^1.2.3", + "mkdirp": "^0.5.1", + "neo-async": "^2.6.1", + "schema-utils": "^1.0.0" + }, + "engines": { + "node": ">= 6.9.0" + }, + "peerDependencies": { + "webpack": "^4.0.0" + } + }, + "node_modules/cache-loader/node_modules/find-cache-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", + "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^2.0.0", + "pkg-dir": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/cache-loader/node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/cache-loader/node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/cache-loader/node_modules/make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dependencies": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/cache-loader/node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/cache-loader/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "engines": { + "node": ">=4" + } + }, + "node_modules/cache-loader/node_modules/pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dependencies": { + "find-up": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/cache-loader/node_modules/schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dependencies": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/cache-loader/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/cacheable-request": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", + "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", + "dependencies": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^3.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^4.1.0", + "responselike": "^1.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cacheable-request/node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cacheable-request/node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/cacheable-request/node_modules/normalize-url": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", + "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-me-maybe": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.1.tgz", + "integrity": "sha1-JtII6onje1y95gJQoV8DHBak1ms=" + }, + "node_modules/caller-callsite": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", + "integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=", + "dependencies": { + "callsites": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/caller-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz", + "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=", + "dependencies": { + "caller-callsite": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/callsites": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", + "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=", + "engines": { + "node": ">=4" + } + }, + "node_modules/camel-case": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz", + "integrity": "sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=", + "dependencies": { + "no-case": "^2.2.0", + "upper-case": "^1.1.1" + } + }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/caniuse-api": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", + "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", + "dependencies": { + "browserslist": "^4.0.0", + "caniuse-lite": "^1.0.0", + "lodash.memoize": "^4.1.2", + "lodash.uniq": "^4.5.0" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001301", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001301.tgz", + "integrity": "sha512-csfD/GpHMqgEL3V3uIgosvh+SVIQvCh43SNu9HRbP1lnxkKm1kjDG4f32PP571JplkLjfS+mg2p1gxR7MYrrIA==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + } + }, + "node_modules/caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + }, + "node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/character-parser": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/character-parser/-/character-parser-2.2.0.tgz", + "integrity": "sha1-x84o821LzZdE5f/CxfzeHHMmH8A=", + "dependencies": { + "is-regex": "^1.0.3" + } + }, + "node_modules/cheerio": { + "version": "1.0.0-rc.10", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.10.tgz", + "integrity": "sha512-g0J0q/O6mW8z5zxQ3A8E8J1hUgp4SMOvEoW/x84OwyHKe/Zccz83PVT4y5Crcr530FV6NgmKI1qvGTKVl9XXVw==", + "dependencies": { + "cheerio-select": "^1.5.0", + "dom-serializer": "^1.3.2", + "domhandler": "^4.2.0", + "htmlparser2": "^6.1.0", + "parse5": "^6.0.1", + "parse5-htmlparser2-tree-adapter": "^6.0.1", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/cheeriojs/cheerio?sponsor=1" + } + }, + "node_modules/cheerio-select": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-1.5.0.tgz", + "integrity": "sha512-qocaHPv5ypefh6YNxvnbABM07KMxExbtbfuJoIie3iZXX1ERwYmJcIiRrr9H05ucQP1k28dav8rpdDgjQd8drg==", + "dependencies": { + "css-select": "^4.1.3", + "css-what": "^5.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0", + "domutils": "^2.7.0" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "deprecated": "Chokidar 2 does not receive security updates since 2019. Upgrade to chokidar 3 with 15x fewer dependencies", + "dependencies": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + }, + "optionalDependencies": { + "fsevents": "^1.2.7" + } + }, + "node_modules/chokidar/node_modules/anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dependencies": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + } + }, + "node_modules/chokidar/node_modules/anymatch/node_modules/normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dependencies": { + "remove-trailing-separator": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dependencies": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + } + }, + "node_modules/chokidar/node_modules/glob-parent/node_modules/is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dependencies": { + "is-extglob": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" + }, + "node_modules/chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "engines": { + "node": ">=6.0" + } + }, + "node_modules/ci-info": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.3.0.tgz", + "integrity": "sha512-riT/3vI5YpVH6/qomlDnJow6TBee2PBKSEpx3O32EGPYbWGIRsIlGRms3Sm74wYE1JMo8RnO04Hb12+v1J5ICw==" + }, + "node_modules/cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "dependencies": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/clean-css": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.4.tgz", + "integrity": "sha512-EJUDT7nDVFDvaQgAo2G/PJvxmp1o/c6iXLbswsBbUFXi1Nr+AjA2cKmfbKDMjMvzEe75g3P6JkaDDAKk96A85A==", + "dependencies": { + "source-map": "~0.6.0" + }, + "engines": { + "node": ">= 4.0" + } + }, + "node_modules/clean-css/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cli-boxes": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", + "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/clipboard-copy": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clipboard-copy/-/clipboard-copy-4.0.1.tgz", + "integrity": "sha512-wOlqdqziE/NNTUJsfSgXmBMIrYmfd5V0HCGsR8uAKHcg+h9NENWINcfRjtWGU77wDHC8B8ijV4hMTGYbrKovng==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dependencies": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/clone-response": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", + "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "dependencies": { + "mimic-response": "^1.0.0" + } + }, + "node_modules/coa": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz", + "integrity": "sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==", + "dependencies": { + "@types/q": "^1.5.1", + "chalk": "^2.4.1", + "q": "^1.1.2" + }, + "engines": { + "node": ">= 4.0" + } + }, + "node_modules/collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "dependencies": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/color": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "dependencies": { + "color-convert": "^1.9.3", + "color-string": "^1.6.0" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "node_modules/color-string": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.0.tgz", + "integrity": "sha512-9Mrz2AQLefkH1UvASKj6v6hj/7eWgjnT/cVsR8CumieLoT+g900exWeNogqtweI8dxloXN9BDQTYro1oWu/5CQ==", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "2.17.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", + "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==" + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=" + }, + "node_modules/component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" + }, + "node_modules/compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "dependencies": { + "mime-db": ">= 1.43.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "dependencies": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/compression/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "node_modules/concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "engines": [ + "node >= 0.8" + ], + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/configstore": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", + "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", + "dependencies": { + "dot-prop": "^5.2.0", + "graceful-fs": "^4.1.2", + "make-dir": "^3.0.0", + "unique-string": "^2.0.0", + "write-file-atomic": "^3.0.0", + "xdg-basedir": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/connect-history-api-fallback": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", + "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/consola": { + "version": "2.15.3", + "resolved": "https://registry.npmjs.org/consola/-/consola-2.15.3.tgz", + "integrity": "sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==" + }, + "node_modules/console-browserify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", + "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==" + }, + "node_modules/consolidate": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/consolidate/-/consolidate-0.15.1.tgz", + "integrity": "sha512-DW46nrsMJgy9kqAbPt5rKaCr7uFtpo4mSUvLHIUbJEjm0vo+aY5QLwBUq3FK4tRnJr/X0Psc0C4jf/h+HtXSMw==", + "dependencies": { + "bluebird": "^3.1.1" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/constantinople": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/constantinople/-/constantinople-4.0.1.tgz", + "integrity": "sha512-vCrqcSIq4//Gx74TXXCGnHpulY1dskqLTFGDmhrGxzeXL8lF8kvXv6mpNWlJj1uD4DW23D4ljAqbY4RRaaUZIw==", + "dependencies": { + "@babel/parser": "^7.6.0", + "@babel/types": "^7.6.1" + } + }, + "node_modules/constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=" + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-disposition/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", + "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", + "dependencies": { + "safe-buffer": "~5.1.1" + } + }, + "node_modules/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "node_modules/copy-concurrently": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", + "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", + "dependencies": { + "aproba": "^1.1.1", + "fs-write-stream-atomic": "^1.0.8", + "iferr": "^0.1.5", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.0" + } + }, + "node_modules/copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/copy-webpack-plugin": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-5.1.2.tgz", + "integrity": "sha512-Uh7crJAco3AjBvgAy9Z75CjK8IG+gxaErro71THQ+vv/bl4HaQcpkexAY8KVW/T6D2W2IRr+couF/knIRkZMIQ==", + "dependencies": { + "cacache": "^12.0.3", + "find-cache-dir": "^2.1.0", + "glob-parent": "^3.1.0", + "globby": "^7.1.1", + "is-glob": "^4.0.1", + "loader-utils": "^1.2.3", + "minimatch": "^3.0.4", + "normalize-path": "^3.0.0", + "p-limit": "^2.2.1", + "schema-utils": "^1.0.0", + "serialize-javascript": "^4.0.0", + "webpack-log": "^2.0.0" + }, + "engines": { + "node": ">= 6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/copy-webpack-plugin/node_modules/find-cache-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", + "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^2.0.0", + "pkg-dir": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/copy-webpack-plugin/node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/copy-webpack-plugin/node_modules/glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dependencies": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + } + }, + "node_modules/copy-webpack-plugin/node_modules/glob-parent/node_modules/is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dependencies": { + "is-extglob": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/copy-webpack-plugin/node_modules/globby": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/globby/-/globby-7.1.1.tgz", + "integrity": "sha1-+yzP+UAfhgCUXfral0QMypcrhoA=", + "dependencies": { + "array-union": "^1.0.1", + "dir-glob": "^2.0.0", + "glob": "^7.1.2", + "ignore": "^3.3.5", + "pify": "^3.0.0", + "slash": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/copy-webpack-plugin/node_modules/globby/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "engines": { + "node": ">=4" + } + }, + "node_modules/copy-webpack-plugin/node_modules/ignore": { + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz", + "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==" + }, + "node_modules/copy-webpack-plugin/node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/copy-webpack-plugin/node_modules/make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dependencies": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/copy-webpack-plugin/node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/copy-webpack-plugin/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "engines": { + "node": ">=4" + } + }, + "node_modules/copy-webpack-plugin/node_modules/pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dependencies": { + "find-up": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/copy-webpack-plugin/node_modules/schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dependencies": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/copy-webpack-plugin/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/copy-webpack-plugin/node_modules/slash": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", + "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/core-js": { + "version": "3.20.3", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.20.3.tgz", + "integrity": "sha512-vVl8j8ph6tRS3B8qir40H7yw7voy17xL0piAjlbBUsH7WIfzoedL/ZOr1OV9FyZQLWXsayOJyV4tnRyXR85/ag==", + "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.", + "hasInstallScript": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-js-compat": { + "version": "3.20.3", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.20.3.tgz", + "integrity": "sha512-c8M5h0IkNZ+I92QhIpuSijOxGAcj3lgpsWdkCqmUTZNwidujF4r3pi6x1DCN+Vcs5qTS2XWWMfWSuCqyupX8gw==", + "dependencies": { + "browserslist": "^4.19.1", + "semver": "7.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-js-compat/node_modules/semver": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", + "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, + "node_modules/cosmiconfig": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", + "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", + "dependencies": { + "import-fresh": "^2.0.0", + "is-directory": "^0.3.1", + "js-yaml": "^3.13.1", + "parse-json": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/create-ecdh": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", + "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", + "dependencies": { + "bn.js": "^4.1.0", + "elliptic": "^6.5.3" + } + }, + "node_modules/create-ecdh/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "node_modules/create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dependencies": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "node_modules/create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dependencies": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "node_modules/cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dependencies": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "engines": { + "node": ">=4.8" + } + }, + "node_modules/cross-spawn/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "dependencies": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + }, + "engines": { + "node": "*" + } + }, + "node_modules/crypto-random-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", + "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/css": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/css/-/css-3.0.0.tgz", + "integrity": "sha512-DG9pFfwOrzc+hawpmqX/dHYHJG+Bsdb0klhyi1sDneOgGOXy9wQIC8hzyVp1e4NRYDBdxcylvywPkkXCHAzTyQ==", + "dependencies": { + "inherits": "^2.0.4", + "source-map": "^0.6.1", + "source-map-resolve": "^0.6.0" + } + }, + "node_modules/css-color-names": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", + "integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=", + "engines": { + "node": "*" + } + }, + "node_modules/css-declaration-sorter": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-4.0.1.tgz", + "integrity": "sha512-BcxQSKTSEEQUftYpBVnsH4SF05NTuBokb19/sBt6asXGKZ/6VP7PLG1CBCkFDYOnhXhPh0jMhO6xZ71oYHXHBA==", + "dependencies": { + "postcss": "^7.0.1", + "timsort": "^0.3.0" + }, + "engines": { + "node": ">4" + } + }, + "node_modules/css-loader": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-2.1.1.tgz", + "integrity": "sha512-OcKJU/lt232vl1P9EEDamhoO9iKY3tIjY5GU+XDLblAykTdgs6Ux9P1hTHve8nFKy5KPpOXOsVI/hIwi3841+w==", + "dependencies": { + "camelcase": "^5.2.0", + "icss-utils": "^4.1.0", + "loader-utils": "^1.2.3", + "normalize-path": "^3.0.0", + "postcss": "^7.0.14", + "postcss-modules-extract-imports": "^2.0.0", + "postcss-modules-local-by-default": "^2.0.6", + "postcss-modules-scope": "^2.1.0", + "postcss-modules-values": "^2.0.0", + "postcss-value-parser": "^3.3.0", + "schema-utils": "^1.0.0" + }, + "engines": { + "node": ">= 6.9.0" + }, + "peerDependencies": { + "webpack": "^4.0.0" + } + }, + "node_modules/css-loader/node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/css-loader/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/css-loader/node_modules/schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dependencies": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/css-parse": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/css-parse/-/css-parse-2.0.0.tgz", + "integrity": "sha1-pGjuZnwW2BzPBcWMONKpfHgNv9Q=", + "dependencies": { + "css": "^2.0.0" + } + }, + "node_modules/css-parse/node_modules/css": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/css/-/css-2.2.4.tgz", + "integrity": "sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw==", + "dependencies": { + "inherits": "^2.0.3", + "source-map": "^0.6.1", + "source-map-resolve": "^0.5.2", + "urix": "^0.1.0" + } + }, + "node_modules/css-parse/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/css-parse/node_modules/source-map-resolve": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", + "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", + "deprecated": "See https://github.com/lydell/source-map-resolve#deprecated", + "dependencies": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "node_modules/css-select": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.2.1.tgz", + "integrity": "sha512-/aUslKhzkTNCQUB2qTX84lVmfia9NyjP3WpDGtj/WxhwBzWBYUV3DgUpurHTme8UTPcPlAD1DJ+b0nN/t50zDQ==", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^5.1.0", + "domhandler": "^4.3.0", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-select-base-adapter": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz", + "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==" + }, + "node_modules/css-tree": { + "version": "1.0.0-alpha.37", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz", + "integrity": "sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==", + "dependencies": { + "mdn-data": "2.0.4", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/css-tree/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/css-what": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-5.1.0.tgz", + "integrity": "sha512-arSMRWIIFY0hV8pIxZMEfmMI47Wj3R/aWpZDDxWYCPEiOMv6tfOrnpDtgxBYPEQD4V0Y/958+1TdC3iWTFcUPw==", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cssnano": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-4.1.11.tgz", + "integrity": "sha512-6gZm2htn7xIPJOHY824ERgj8cNPgPxyCSnkXc4v7YvNW+TdVfzgngHcEhy/8D11kUWRUMbke+tC+AUcUsnMz2g==", + "dependencies": { + "cosmiconfig": "^5.0.0", + "cssnano-preset-default": "^4.0.8", + "is-resolvable": "^1.0.0", + "postcss": "^7.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/cssnano-preset-default": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-4.0.8.tgz", + "integrity": "sha512-LdAyHuq+VRyeVREFmuxUZR1TXjQm8QQU/ktoo/x7bz+SdOge1YKc5eMN6pRW7YWBmyq59CqYba1dJ5cUukEjLQ==", + "dependencies": { + "css-declaration-sorter": "^4.0.1", + "cssnano-util-raw-cache": "^4.0.1", + "postcss": "^7.0.0", + "postcss-calc": "^7.0.1", + "postcss-colormin": "^4.0.3", + "postcss-convert-values": "^4.0.1", + "postcss-discard-comments": "^4.0.2", + "postcss-discard-duplicates": "^4.0.2", + "postcss-discard-empty": "^4.0.1", + "postcss-discard-overridden": "^4.0.1", + "postcss-merge-longhand": "^4.0.11", + "postcss-merge-rules": "^4.0.3", + "postcss-minify-font-values": "^4.0.2", + "postcss-minify-gradients": "^4.0.2", + "postcss-minify-params": "^4.0.2", + "postcss-minify-selectors": "^4.0.2", + "postcss-normalize-charset": "^4.0.1", + "postcss-normalize-display-values": "^4.0.2", + "postcss-normalize-positions": "^4.0.2", + "postcss-normalize-repeat-style": "^4.0.2", + "postcss-normalize-string": "^4.0.2", + "postcss-normalize-timing-functions": "^4.0.2", + "postcss-normalize-unicode": "^4.0.1", + "postcss-normalize-url": "^4.0.1", + "postcss-normalize-whitespace": "^4.0.2", + "postcss-ordered-values": "^4.1.2", + "postcss-reduce-initial": "^4.0.3", + "postcss-reduce-transforms": "^4.0.2", + "postcss-svgo": "^4.0.3", + "postcss-unique-selectors": "^4.0.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/cssnano-util-get-arguments": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cssnano-util-get-arguments/-/cssnano-util-get-arguments-4.0.0.tgz", + "integrity": "sha1-7ToIKZ8h11dBsg87gfGU7UnMFQ8=", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/cssnano-util-get-match": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cssnano-util-get-match/-/cssnano-util-get-match-4.0.0.tgz", + "integrity": "sha1-wOTKB/U4a7F+xeUiULT1lhNlFW0=", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/cssnano-util-raw-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/cssnano-util-raw-cache/-/cssnano-util-raw-cache-4.0.1.tgz", + "integrity": "sha512-qLuYtWK2b2Dy55I8ZX3ky1Z16WYsx544Q0UWViebptpwn/xDBmog2TLg4f+DBMg1rJ6JDWtn96WHbOKDWt1WQA==", + "dependencies": { + "postcss": "^7.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/cssnano-util-same-parent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/cssnano-util-same-parent/-/cssnano-util-same-parent-4.0.1.tgz", + "integrity": "sha512-WcKx5OY+KoSIAxBW6UBBRay1U6vkYheCdjyVNDm85zt5K9mHoGOfsOsqIszfAqrQQFIIKgjh2+FDgIj/zsl21Q==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/csso": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", + "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", + "dependencies": { + "css-tree": "^1.1.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/csso/node_modules/css-tree": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", + "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "dependencies": { + "mdn-data": "2.0.14", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/csso/node_modules/mdn-data": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==" + }, + "node_modules/csso/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cyclist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz", + "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=" + }, + "node_modules/dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dependencies": { + "assert-plus": "^1.0.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/de-indent": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", + "integrity": "sha1-sgOOhG3DO6pXlhKNCAS0VbjB4h0=" + }, + "node_modules/debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "dependencies": { + "mimic-response": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/deep-equal": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", + "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", + "dependencies": { + "is-arguments": "^1.0.4", + "is-date-object": "^1.0.1", + "is-regex": "^1.0.4", + "object-is": "^1.0.1", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.2.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/deepmerge": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-1.5.2.tgz", + "integrity": "sha512-95k0GDqvBjZavkuvzx/YqVLv/6YYa17fz6ILMSf7neqQITCPbnfEnQvEgMPNjH4kgobe7+WIL0yJEHku+H3qtQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/default-gateway": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-4.2.0.tgz", + "integrity": "sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA==", + "dependencies": { + "execa": "^1.0.0", + "ip-regex": "^2.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/defer-to-connect": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", + "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==" + }, + "node_modules/define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dependencies": { + "object-keys": "^1.0.12" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dependencies": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-property/node_modules/is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-property/node_modules/is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-property/node_modules/is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dependencies": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/del": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz", + "integrity": "sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==", + "dependencies": { + "@types/glob": "^7.1.1", + "globby": "^6.1.0", + "is-path-cwd": "^2.0.0", + "is-path-in-cwd": "^2.0.0", + "p-map": "^2.0.0", + "pify": "^4.0.1", + "rimraf": "^2.6.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/del/node_modules/globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", + "dependencies": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/del/node_modules/globby/node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/des.js": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", + "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", + "dependencies": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==" + }, + "node_modules/diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "dependencies": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + } + }, + "node_modules/diffie-hellman/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "node_modules/dir-glob": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.2.2.tgz", + "integrity": "sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw==", + "dependencies": { + "path-type": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/dns-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", + "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=" + }, + "node_modules/dns-packet": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.4.tgz", + "integrity": "sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA==", + "dependencies": { + "ip": "^1.1.0", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/dns-txt": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", + "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=", + "dependencies": { + "buffer-indexof": "^1.0.0" + } + }, + "node_modules/docsearch.js": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/docsearch.js/-/docsearch.js-2.6.3.tgz", + "integrity": "sha512-GN+MBozuyz664ycpZY0ecdQE0ND/LSgJKhTLA0/v3arIS3S1Rpf2OJz6A35ReMsm91V5apcmzr5/kM84cvUg+A==", + "deprecated": "This package has been deprecated and is no longer maintained. Please use @docsearch/js.", + "dependencies": { + "algoliasearch": "^3.24.5", + "autocomplete.js": "0.36.0", + "hogan.js": "^3.0.2", + "request": "^2.87.0", + "stack-utils": "^1.0.1", + "to-factory": "^1.0.0", + "zepto": "^1.2.0" + } + }, + "node_modules/docsearch.js/node_modules/algoliasearch": { + "version": "3.35.1", + "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-3.35.1.tgz", + "integrity": "sha512-K4yKVhaHkXfJ/xcUnil04xiSrB8B8yHZoFEhWNpXg23eiCnqvTZw1tn/SqvdsANlYHLJlKl0qi3I/Q2Sqo7LwQ==", + "dependencies": { + "agentkeepalive": "^2.2.0", + "debug": "^2.6.9", + "envify": "^4.0.0", + "es6-promise": "^4.1.0", + "events": "^1.1.0", + "foreach": "^2.0.5", + "global": "^4.3.2", + "inherits": "^2.0.1", + "isarray": "^2.0.1", + "load-script": "^1.0.0", + "object-keys": "^1.0.11", + "querystring-es3": "^0.2.1", + "reduce": "^1.0.1", + "semver": "^5.1.0", + "tunnel-agent": "^0.6.0" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/docsearch.js/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/docsearch.js/node_modules/events": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=", + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/docsearch.js/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" + }, + "node_modules/docsearch.js/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/doctypes": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/doctypes/-/doctypes-1.1.0.tgz", + "integrity": "sha1-6oCxBqh1OHdOijpKWv4pPeSJ4Kk=" + }, + "node_modules/dom-converter": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", + "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", + "dependencies": { + "utila": "~0.4" + } + }, + "node_modules/dom-serializer": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz", + "integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==", + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/dom-serializer/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/dom-walk": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz", + "integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==" + }, + "node_modules/domain-browser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", + "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", + "engines": { + "node": ">=0.4", + "npm": ">=1.2" + } + }, + "node_modules/domelementtype": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", + "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/domhandler": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.0.tgz", + "integrity": "sha512-fC0aXNQXqKSFTr2wDNZDhsEYjCiYsDWl3D01kwt25hm1YIPyDGHvvi3rw+PLqHAl/m71MaiF7d5zvBr0p5UB2g==", + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/duplexer3": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", + "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=" + }, + "node_modules/duplexify": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", + "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "dependencies": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + } + }, + "node_modules/ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "dependencies": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "node_modules/electron-to-chromium": { + "version": "1.4.52", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.52.tgz", + "integrity": "sha512-JGkh8HEh5PnVrhU4HbpyyO0O791dVY6k7AdqfDeqbcRMeoGxtNHWT77deR2nhvbLe4dKpxjlDEvdEwrvRLGu2Q==" + }, + "node_modules/elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "dependencies": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/elliptic/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "node_modules/emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" + }, + "node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "engines": { + "node": ">= 4" + } + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/enhanced-resolve": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz", + "integrity": "sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg==", + "dependencies": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.5.0", + "tapable": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/enhanced-resolve/node_modules/memory-fs": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz", + "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==", + "dependencies": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + }, + "engines": { + "node": ">=4.3.0 <5.0.0 || >=5.10" + } + }, + "node_modules/entities": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", + "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/envify": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/envify/-/envify-4.1.0.tgz", + "integrity": "sha512-IKRVVoAYr4pIx4yIWNsz9mOsboxlNXiu7TNBnem/K/uTHdkyzXWDzHCK7UTolqBbgaBz0tQHsD3YNls0uIIjiw==", + "dependencies": { + "esprima": "^4.0.0", + "through": "~2.3.4" + }, + "bin": { + "envify": "bin/envify" + } + }, + "node_modules/envinfo": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz", + "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==", + "bin": { + "envinfo": "dist/cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/errno": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", + "dependencies": { + "prr": "~1.0.1" + }, + "bin": { + "errno": "cli.js" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-abstract": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz", + "integrity": "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==", + "dependencies": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "get-intrinsic": "^1.1.1", + "get-symbol-description": "^1.0.0", + "has": "^1.0.3", + "has-symbols": "^1.0.2", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.4", + "is-negative-zero": "^2.0.1", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.1", + "is-string": "^1.0.7", + "is-weakref": "^1.0.1", + "object-inspect": "^1.11.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "string.prototype.trimend": "^1.0.4", + "string.prototype.trimstart": "^1.0.4", + "unbox-primitive": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es6-promise": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", + "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==" + }, + "node_modules/esbuild": { + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.7.tgz", + "integrity": "sha512-+u/msd6iu+HvfysUPkZ9VHm83LImmSNnecYPfFI01pQ7TTcsFR+V0BkybZX7mPtIaI7LCrse6YRj+v3eraJSgw==", + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "optionalDependencies": { + "esbuild-android-arm64": "0.14.7", + "esbuild-darwin-64": "0.14.7", + "esbuild-darwin-arm64": "0.14.7", + "esbuild-freebsd-64": "0.14.7", + "esbuild-freebsd-arm64": "0.14.7", + "esbuild-linux-32": "0.14.7", + "esbuild-linux-64": "0.14.7", + "esbuild-linux-arm": "0.14.7", + "esbuild-linux-arm64": "0.14.7", + "esbuild-linux-mips64le": "0.14.7", + "esbuild-linux-ppc64le": "0.14.7", + "esbuild-netbsd-64": "0.14.7", + "esbuild-openbsd-64": "0.14.7", + "esbuild-sunos-64": "0.14.7", + "esbuild-windows-32": "0.14.7", + "esbuild-windows-64": "0.14.7", + "esbuild-windows-arm64": "0.14.7" + } + }, + "node_modules/esbuild-android-arm64": { + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.7.tgz", + "integrity": "sha512-9/Q1NC4JErvsXzJKti0NHt+vzKjZOgPIjX/e6kkuCzgfT/GcO3FVBcGIv4HeJG7oMznE6KyKhvLrFgt7CdU2/w==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/esbuild-darwin-64": { + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.7.tgz", + "integrity": "sha512-Z9X+3TT/Xj+JiZTVlwHj2P+8GoiSmUnGVz0YZTSt8WTbW3UKw5Pw2ucuJ8VzbD2FPy0jbIKJkko/6CMTQchShQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/esbuild-darwin-arm64": { + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.7.tgz", + "integrity": "sha512-68e7COhmwIiLXBEyxUxZSSU0akgv8t3e50e2QOtKdBUE0F6KIRISzFntLe2rYlNqSsjGWsIO6CCc9tQxijjSkw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/esbuild-freebsd-64": { + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.7.tgz", + "integrity": "sha512-76zy5jAjPiXX/S3UvRgG85Bb0wy0zv/J2lel3KtHi4V7GUTBfhNUPt0E5bpSXJ6yMT7iThhnA5rOn+IJiUcslQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/esbuild-freebsd-arm64": { + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.7.tgz", + "integrity": "sha512-lSlYNLiqyzd7qCN5CEOmLxn7MhnGHPcu5KuUYOG1i+t5A6q7LgBmfYC9ZHJBoYyow3u4CNu79AWHbvVLpE/VQQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/esbuild-linux-32": { + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.7.tgz", + "integrity": "sha512-Vk28u409wVOXqTaT6ek0TnfQG4Ty1aWWfiysIaIRERkNLhzLhUf4i+qJBN8mMuGTYOkE40F0Wkbp6m+IidOp2A==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/esbuild-linux-64": { + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.7.tgz", + "integrity": "sha512-+Lvz6x+8OkRk3K2RtZwO+0a92jy9si9cUea5Zoru4yJ/6EQm9ENX5seZE0X9DTwk1dxJbjmLsJsd3IoowyzgVg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/esbuild-linux-arm": { + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.7.tgz", + "integrity": "sha512-OzpXEBogbYdcBqE4uKynuSn5YSetCvK03Qv1HcOY1VN6HmReuatjJ21dCH+YPHSpMEF0afVCnNfffvsGEkxGJQ==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/esbuild-linux-arm64": { + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.7.tgz", + "integrity": "sha512-kJd5beWSqteSAW086qzCEsH6uwpi7QRIpzYWHzEYwKKu9DiG1TwIBegQJmLpPsLp4v5RAFjea0JAmAtpGtRpqg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/esbuild-linux-mips64le": { + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.7.tgz", + "integrity": "sha512-mFWpnDhZJmj/h7pxqn1GGDsKwRfqtV7fx6kTF5pr4PfXe8pIaTERpwcKkoCwZUkWAOmUEjMIUAvFM72A6hMZnA==", + "cpu": [ + "mips64el" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/esbuild-linux-ppc64le": { + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.7.tgz", + "integrity": "sha512-wM7f4M0bsQXfDL4JbbYD0wsr8cC8KaQ3RPWc/fV27KdErPW7YsqshZZSjDV0kbhzwpNNdhLItfbaRT8OE8OaKA==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/esbuild-netbsd-64": { + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.7.tgz", + "integrity": "sha512-J/afS7woKyzGgAL5FlgvMyqgt5wQ597lgsT+xc2yJ9/7BIyezeXutXqfh05vszy2k3kSvhLesugsxIA71WsqBw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "netbsd" + ] + }, + "node_modules/esbuild-openbsd-64": { + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.7.tgz", + "integrity": "sha512-7CcxgdlCD+zAPyveKoznbgr3i0Wnh0L8BDGRCjE/5UGkm5P/NQko51tuIDaYof8zbmXjjl0OIt9lSo4W7I8mrw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/esbuild-sunos-64": { + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.7.tgz", + "integrity": "sha512-GKCafP2j/KUljVC3nesw1wLFSZktb2FGCmoT1+730zIF5O6hNroo0bSEofm6ZK5mNPnLiSaiLyRB9YFgtkd5Xg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "sunos" + ] + }, + "node_modules/esbuild-windows-32": { + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.7.tgz", + "integrity": "sha512-5I1GeL/gZoUUdTPA0ws54bpYdtyeA2t6MNISalsHpY269zK8Jia/AXB3ta/KcDHv2SvNwabpImeIPXC/k0YW6A==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/esbuild-windows-64": { + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.7.tgz", + "integrity": "sha512-CIGKCFpQOSlYsLMbxt8JjxxvVw9MlF1Rz2ABLVfFyHUF5OeqHD5fPhGrCVNaVrhO8Xrm+yFmtjcZudUGr5/WYQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/esbuild-windows-arm64": { + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.7.tgz", + "integrity": "sha512-eOs1eSivOqN7cFiRIukEruWhaCf75V0N8P0zP7dh44LIhLl8y6/z++vv9qQVbkBm5/D7M7LfCfCTmt1f1wHOCw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-goat": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz", + "integrity": "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==", + "engines": { + "node": ">=8" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/eslint-scope": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", + "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", + "dependencies": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/esm": { + "version": "3.2.25", + "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz", + "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dependencies": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dependencies": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dependencies": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/expand-brackets/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/express": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.2.tgz", + "integrity": "sha512-oxlxJxcQlYwqPWKVJJtvQiwHgosH/LrLSPA+H4UxpyvSS6jC5aH+5MoHFM+KABgTOt0APue4w66Ha8jCUo9QGg==", + "dependencies": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.1", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.4.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.9.6", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.17.2", + "serve-static": "1.14.2", + "setprototypeof": "1.2.0", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/express/node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/qs": { + "version": "6.9.6", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.6.tgz", + "integrity": "sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ==", + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/express/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dependencies": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dependencies": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "engines": [ + "node >=0.6.0" + ] + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "node_modules/fast-glob": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-2.2.7.tgz", + "integrity": "sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw==", + "dependencies": { + "@mrmlnc/readdir-enhanced": "^2.2.1", + "@nodelib/fs.stat": "^1.1.2", + "glob-parent": "^3.1.0", + "is-glob": "^4.0.0", + "merge2": "^1.2.3", + "micromatch": "^3.1.10" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dependencies": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent/node_modules/is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dependencies": { + "is-extglob": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + }, + "node_modules/faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/figgy-pudding": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz", + "integrity": "sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==" + }, + "node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/file-loader": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-3.0.1.tgz", + "integrity": "sha512-4sNIOXgtH/9WZq4NvlfU3Opn5ynUsqBwSLyM+I7UOwdGigTBYfVVQEwe/msZNX/j4pCJTIM14Fsw66Svo1oVrw==", + "dependencies": { + "loader-utils": "^1.0.2", + "schema-utils": "^1.0.0" + }, + "engines": { + "node": ">= 6.9.0" + }, + "peerDependencies": { + "webpack": "^4.0.0" + } + }, + "node_modules/file-loader/node_modules/schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dependencies": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "optional": true + }, + "node_modules/fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dependencies": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/avajs/find-cache-dir?sponsor=1" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/flush-write-stream": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", + "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", + "dependencies": { + "inherits": "^2.0.3", + "readable-stream": "^2.3.6" + } + }, + "node_modules/follow-redirects": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", + "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", + "dependencies": { + "debug": "=3.1.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/foreach": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", + "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=" + }, + "node_modules/forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "engines": { + "node": "*" + } + }, + "node_modules/form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "dependencies": { + "map-cache": "^0.2.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/from2": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", + "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", + "dependencies": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + } + }, + "node_modules/fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/fs-write-stream-atomic": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", + "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", + "dependencies": { + "graceful-fs": "^4.1.2", + "iferr": "^0.1.5", + "imurmurhash": "^0.1.4", + "readable-stream": "1 || 2" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "node_modules/fsevents": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", + "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", + "deprecated": "fsevents 1 will break on node v14+ and could be using insecure binaries. Upgrade to fsevents 2.", + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "dependencies": { + "bindings": "^1.5.0", + "nan": "^2.12.1" + }, + "engines": { + "node": ">= 4.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "node_modules/fuse.js": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-3.6.1.tgz", + "integrity": "sha512-hT9yh/tiinkmirKrlv4KWOjztdoZo1mx9Qh4KvWqC7isoXwdUY3PNWUxceF4/qO9R6riA2C29jdTOeQOIROjgw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dependencies": { + "assert-plus": "^1.0.0" + } + }, + "node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "optional": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz", + "integrity": "sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs=" + }, + "node_modules/global": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/global/-/global-4.4.0.tgz", + "integrity": "sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==", + "dependencies": { + "min-document": "^2.19.0", + "process": "^0.11.10" + } + }, + "node_modules/global-dirs": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-2.1.0.tgz", + "integrity": "sha512-MG6kdOUh/xBnyo9cJFeIKkLEc1AyFq42QTU4XiX51i2NEdxLxLWXIjEjmqKeSuKR7pAZjTqUVoT2b2huxVLgYQ==", + "dependencies": { + "ini": "1.3.7" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/globby": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-9.2.0.tgz", + "integrity": "sha512-ollPHROa5mcxDEkwg6bPt3QbEf4pDQSNtd6JPL1YvOvAo/7/0VAm9TccUeoTmarjPw4pfUthSCqcyfNB1I3ZSg==", + "dependencies": { + "@types/glob": "^7.1.1", + "array-union": "^1.0.2", + "dir-glob": "^2.2.2", + "fast-glob": "^2.2.6", + "glob": "^7.1.3", + "ignore": "^4.0.3", + "pify": "^4.0.1", + "slash": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/got": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", + "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", + "dependencies": { + "@sindresorhus/is": "^0.14.0", + "@szmarczak/http-timer": "^1.1.2", + "cacheable-request": "^6.0.0", + "decompress-response": "^3.3.0", + "duplexer3": "^0.1.4", + "get-stream": "^4.1.0", + "lowercase-keys": "^1.0.1", + "mimic-response": "^1.0.1", + "p-cancelable": "^1.0.0", + "to-readable-stream": "^1.0.0", + "url-parse-lax": "^3.0.0" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.9", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", + "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==" + }, + "node_modules/gray-matter": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.3.tgz", + "integrity": "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==", + "dependencies": { + "js-yaml": "^3.13.1", + "kind-of": "^6.0.2", + "section-matter": "^1.0.0", + "strip-bom-string": "^1.0.0" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/handle-thing": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==" + }, + "node_modules/har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "engines": { + "node": ">=4" + } + }, + "node_modules/har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "deprecated": "this library is no longer supported", + "dependencies": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-bigints": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", + "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "engines": { + "node": ">=4" + } + }, + "node_modules/has-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "dependencies": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "dependencies": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values/node_modules/kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-yarn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", + "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/hash-base": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "dependencies": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/hash-base/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/hash-base/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/hash-sum": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-1.0.2.tgz", + "integrity": "sha1-M7QHd3VMZDJXPBIMw4CLvRDUfwQ=" + }, + "node_modules/hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "bin": { + "he": "bin/he" + } + }, + "node_modules/hex-color-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz", + "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==" + }, + "node_modules/highlight.js": { + "version": "9.18.5", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.18.5.tgz", + "integrity": "sha512-a5bFyofd/BHCX52/8i8uJkjr9DYwXIPnM/plwI6W7ezItLGqzt7X2G2nXuYSfsIJdkwwj/g9DG1LkcGJI/dDoA==", + "deprecated": "Support has ended for 9.x series. Upgrade to @latest", + "hasInstallScript": true, + "engines": { + "node": "*" + } + }, + "node_modules/hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "dependencies": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/hogan.js": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/hogan.js/-/hogan.js-3.0.2.tgz", + "integrity": "sha1-TNnhq9QpQUbnZ55B14mHMrAse/0=", + "dependencies": { + "mkdirp": "0.3.0", + "nopt": "1.0.10" + }, + "bin": { + "hulk": "bin/hulk" + } + }, + "node_modules/hogan.js/node_modules/mkdirp": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz", + "integrity": "sha1-G79asbqCevI1dRQ0kEJkVfSB/h4=", + "deprecated": "Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.)", + "engines": { + "node": "*" + } + }, + "node_modules/hotkeys-js": { + "version": "3.8.7", + "resolved": "https://registry.npmjs.org/hotkeys-js/-/hotkeys-js-3.8.7.tgz", + "integrity": "sha512-ckAx3EkUr5XjDwjEHDorHxRO2Kb7z6Z2Sxul4MbBkN8Nho7XDslQsgMJT+CiJ5Z4TgRxxvKHEpuLE3imzqy4Lg==" + }, + "node_modules/hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", + "dependencies": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + } + }, + "node_modules/hsl-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hsl-regex/-/hsl-regex-1.0.0.tgz", + "integrity": "sha1-1JMwx4ntgZ4nakwNJy3/owsY/m4=" + }, + "node_modules/hsla-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hsla-regex/-/hsla-regex-1.0.0.tgz", + "integrity": "sha1-wc56MWjIxmFAM6S194d/OyJfnDg=" + }, + "node_modules/html-entities": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.4.0.tgz", + "integrity": "sha512-8nxjcBcd8wovbeKx7h3wTji4e6+rhaVuPNpMqwWgnHh+N9ToqsCs6XztWRBPQ+UtzsoMAdKZtUENoVzU/EMtZA==" + }, + "node_modules/html-minifier": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-3.5.21.tgz", + "integrity": "sha512-LKUKwuJDhxNa3uf/LPR/KVjm/l3rBqtYeCOAekvG8F1vItxMUpueGd94i/asDDr8/1u7InxzFA5EeGjhhG5mMA==", + "dependencies": { + "camel-case": "3.0.x", + "clean-css": "4.2.x", + "commander": "2.17.x", + "he": "1.2.x", + "param-case": "2.1.x", + "relateurl": "0.2.x", + "uglify-js": "3.4.x" + }, + "bin": { + "html-minifier": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/html-tags": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.1.0.tgz", + "integrity": "sha512-1qYz89hW3lFDEazhjW0yVAV87lw8lVkrJocr72XmBkMKsoSVJCQx3W8BXsC7hO2qAt8BoVjYjtAcZ9perqGnNg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/htmlparser2": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", + "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0", + "domutils": "^2.5.2", + "entities": "^2.0.0" + } + }, + "node_modules/htmlparser2/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/http-cache-semantics": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", + "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==" + }, + "node_modules/http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=" + }, + "node_modules/http-errors": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", + "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/http-parser-js": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.5.tgz", + "integrity": "sha512-x+JVEkO2PoM8qqpbPbOL3cqHPwerep7OwzK7Ay+sMQjKzaKCqWvjoXm5tqMP9tXWWTnTzAjIhXg+J99XYuPhPA==" + }, + "node_modules/http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dependencies": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/http-proxy-middleware": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-1.3.1.tgz", + "integrity": "sha512-13eVVDYS4z79w7f1+NPllJtOQFx/FdUW4btIvVRMaRlUY9VGstAbo5MOhLEuUgZFRHn3x50ufn25zkj/boZnEg==", + "dependencies": { + "@types/http-proxy": "^1.17.5", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/http-proxy-middleware/node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/http-proxy-middleware/node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/http-proxy-middleware/node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/http-proxy-middleware/node_modules/micromatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "dependencies": { + "braces": "^3.0.1", + "picomatch": "^2.2.3" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/http-proxy-middleware/node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "dependencies": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + }, + "engines": { + "node": ">=0.8", + "npm": ">=1.3.7" + } + }, + "node_modules/https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=" + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/icss-replace-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz", + "integrity": "sha1-Bupvg2ead0njhs/h/oEq5dsiPe0=" + }, + "node_modules/icss-utils": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-4.1.1.tgz", + "integrity": "sha512-4aFq7wvWyMHKgxsH8QQtGpvbASCf+eM3wPRLI6R+MgAnTCZ6STYsRvttLvRWK0Nfif5piF394St3HeJDaljGPA==", + "dependencies": { + "postcss": "^7.0.14" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/iferr": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", + "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=" + }, + "node_modules/ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "engines": { + "node": ">= 4" + } + }, + "node_modules/immediate": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.3.0.tgz", + "integrity": "sha512-HR7EVodfFUdQCTIeySw+WDRFJlPcLOJbXfwwZ7Oom6tjsvZ3bOkCDJHehQC3nxJrv7+f9XecwazynjU8e4Vw3Q==" + }, + "node_modules/import-cwd": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-2.1.0.tgz", + "integrity": "sha1-qmzzbnInYShcs3HsZRn1PiQ1sKk=", + "dependencies": { + "import-from": "^2.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/import-fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", + "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=", + "dependencies": { + "caller-path": "^2.0.0", + "resolve-from": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/import-from": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-from/-/import-from-2.1.0.tgz", + "integrity": "sha1-M1238qev/VOqpHHUuAId7ja387E=", + "dependencies": { + "resolve-from": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/import-lazy": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", + "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=", + "engines": { + "node": ">=4" + } + }, + "node_modules/import-local": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", + "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", + "dependencies": { + "pkg-dir": "^3.0.0", + "resolve-cwd": "^2.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/import-local/node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/import-local/node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/import-local/node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/import-local/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "engines": { + "node": ">=4" + } + }, + "node_modules/import-local/node_modules/pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dependencies": { + "find-up": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indexes-of": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", + "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=" + }, + "node_modules/infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==" + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ini": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.7.tgz", + "integrity": "sha512-iKpRpXP+CrP2jyrxvg1kMUpXDyRUFDWurxbnVT1vQPx+Wz9uCYsMIqYuSBLV+PAaZG/d7kRLKRFc9oDMsH+mFQ==" + }, + "node_modules/internal-ip": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-4.3.0.tgz", + "integrity": "sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg==", + "dependencies": { + "default-gateway": "^4.2.0", + "ipaddr.js": "^1.9.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/internal-slot": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", + "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "dependencies": { + "get-intrinsic": "^1.1.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ip": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", + "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=" + }, + "node_modules/ip-regex": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", + "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=", + "engines": { + "node": ">=4" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-absolute-url": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz", + "integrity": "sha1-UFMN+4T8yap9vnhS6Do3uTufKqY=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-accessor-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" + }, + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "dependencies": { + "binary-extensions": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, + "node_modules/is-callable": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", + "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "dependencies": { + "ci-info": "^2.0.0" + }, + "bin": { + "is-ci": "bin.js" + } + }, + "node_modules/is-ci/node_modules/ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==" + }, + "node_modules/is-color-stop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-color-stop/-/is-color-stop-1.1.0.tgz", + "integrity": "sha1-z/9HGu5N1cnhWFmPvhKWe1za00U=", + "dependencies": { + "css-color-names": "^0.0.4", + "hex-color-regex": "^1.1.0", + "hsl-regex": "^1.0.0", + "hsla-regex": "^1.0.0", + "rgb-regex": "^1.0.1", + "rgba-regex": "^1.0.0" + } + }, + "node_modules/is-core-module": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", + "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-data-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dependencies": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-descriptor/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-directory": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", + "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-expression": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-expression/-/is-expression-4.0.0.tgz", + "integrity": "sha512-zMIXX63sxzG3XrkHkrAPvm/OVZVSCPNkwMHU8oTX7/U3AL78I0QXCEICXUM13BIa8TYGZ68PiTKfQz3yaTNr4A==", + "dependencies": { + "acorn": "^7.1.1", + "object-assign": "^4.1.1" + } + }, + "node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "engines": { + "node": ">=4" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-installed-globally": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.3.2.tgz", + "integrity": "sha512-wZ8x1js7Ia0kecP/CHM/3ABkAmujX7WPvQk6uu3Fly/Mk44pySulQpnHG46OMjHGXApINnV4QhY3SWnECO2z5g==", + "dependencies": { + "global-dirs": "^2.0.1", + "is-path-inside": "^3.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-installed-globally/node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-npm": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-4.0.0.tgz", + "integrity": "sha512-96ECIfh9xtDDlPylNPXhzjsykHsMJZ18ASpaWzQyBr4YRTcVjUvzaHayDAES2oU/3KpljhHUjtSRNiDwi0F0ig==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number-object": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.6.tgz", + "integrity": "sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-path-cwd": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", + "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/is-path-in-cwd": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz", + "integrity": "sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==", + "dependencies": { + "is-path-inside": "^2.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-path-inside": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-2.1.0.tgz", + "integrity": "sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==", + "dependencies": { + "path-is-inside": "^1.0.2" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-plain-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", + "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-promise": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", + "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==" + }, + "node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-resolvable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", + "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==" + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz", + "integrity": "sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, + "node_modules/is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", + "engines": { + "node": ">=4" + } + }, + "node_modules/is-yarn-global": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz", + "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==" + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + }, + "node_modules/javascript-stringify": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/javascript-stringify/-/javascript-stringify-1.6.0.tgz", + "integrity": "sha1-FC0RHzpuPa6PSpr9d9RYVbWpzOM=" + }, + "node_modules/js-base64": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.6.4.tgz", + "integrity": "sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==" + }, + "node_modules/js-stringify": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/js-stringify/-/js-stringify-1.0.2.tgz", + "integrity": "sha1-Fzb939lyTyijaCrcYjCufk6Weds=" + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", + "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=" + }, + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" + }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + }, + "node_modules/json3": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.3.tgz", + "integrity": "sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA==" + }, + "node_modules/json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/jsonp/-/jsonp-0.2.1.tgz", + "integrity": "sha1-pltPoPEL2nGaBUQep7lMVfPhW64=", + "dependencies": { + "debug": "^2.1.3" + } + }, + "node_modules/jsonp/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/jsprim": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", + "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", + "dependencies": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.4.0", + "verror": "1.10.0" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/jstransformer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/jstransformer/-/jstransformer-1.0.0.tgz", + "integrity": "sha1-7Yvwkh4vPx7U1cGkT2hwntJHIsM=", + "dependencies": { + "is-promise": "^2.0.0", + "promise": "^7.0.1" + } + }, + "node_modules/keyv": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", + "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", + "dependencies": { + "json-buffer": "3.0.0" + } + }, + "node_modules/killable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz", + "integrity": "sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg==" + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/last-call-webpack-plugin": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/last-call-webpack-plugin/-/last-call-webpack-plugin-3.0.0.tgz", + "integrity": "sha512-7KI2l2GIZa9p2spzPIVZBYyNKkN+e/SQPpnjlTiPhdbDW3F86tdKKELxKpzJ5sgU19wQWsACULZmpTPYHeWO5w==", + "dependencies": { + "lodash": "^4.17.5", + "webpack-sources": "^1.1.0" + } + }, + "node_modules/latest-version": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz", + "integrity": "sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==", + "dependencies": { + "package-json": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/linkify-it": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-2.2.0.tgz", + "integrity": "sha512-GnAl/knGn+i1U/wjBz3akz2stz+HrHLsxMwHQGofCDfPvlf+gDKN58UtfmUquTY4/MXeE2x7k19KQmeoZi94Iw==", + "dependencies": { + "uc.micro": "^1.0.1" + } + }, + "node_modules/load-script": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/load-script/-/load-script-1.0.0.tgz", + "integrity": "sha1-BJGTngvuVkPuSUp+PaPSuscMbKQ=" + }, + "node_modules/loader-runner": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz", + "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==", + "engines": { + "node": ">=4.3.0 <5.0.0 || >=5.10" + } + }, + "node_modules/loader-utils": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", + "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/lodash._reinterpolate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", + "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=" + }, + "node_modules/lodash.chunk": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.chunk/-/lodash.chunk-4.2.0.tgz", + "integrity": "sha1-ZuXOH3btJ7QwPYxlEujRIW6BBrw=" + }, + "node_modules/lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=" + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=" + }, + "node_modules/lodash.kebabcase": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz", + "integrity": "sha1-hImxyw0p/4gZXM7KRI/21swpXDY=" + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=" + }, + "node_modules/lodash.padstart": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/lodash.padstart/-/lodash.padstart-4.6.1.tgz", + "integrity": "sha1-0uPuv/DZ05rVD1y9G1KnvOa7YRs=" + }, + "node_modules/lodash.sortby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", + "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=" + }, + "node_modules/lodash.template": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz", + "integrity": "sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==", + "dependencies": { + "lodash._reinterpolate": "^3.0.0", + "lodash.templatesettings": "^4.0.0" + } + }, + "node_modules/lodash.templatesettings": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz", + "integrity": "sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==", + "dependencies": { + "lodash._reinterpolate": "^3.0.0" + } + }, + "node_modules/lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=" + }, + "node_modules/loglevel": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.8.0.tgz", + "integrity": "sha512-G6A/nJLRgWOuuwdNuA6koovfEV1YpqqAG4pRUlFaz3jj2QNZ8M4vBqnVA+HBTmU/AMNUtlOsMmSpF6NyOjztbA==", + "engines": { + "node": ">= 0.6.0" + }, + "funding": { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/loglevel" + } + }, + "node_modules/lower-case": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", + "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=" + }, + "node_modules/lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "dependencies": { + "object-visit": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/markdown-it": { + "version": "12.3.2", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz", + "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==", + "dependencies": { + "argparse": "^2.0.1", + "entities": "~2.1.0", + "linkify-it": "^3.0.1", + "mdurl": "^1.0.1", + "uc.micro": "^1.0.5" + }, + "bin": { + "markdown-it": "bin/markdown-it.js" + } + }, + "node_modules/markdown-it-anchor": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-5.3.0.tgz", + "integrity": "sha512-/V1MnLL/rgJ3jkMWo84UR+K+jF1cxNG1a+KwqeXqTIJ+jtA8aWSHuigx8lTzauiIjBDbwF3NcWQMotd0Dm39jA==", + "peerDependencies": { + "markdown-it": "*" + } + }, + "node_modules/markdown-it-attrs": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/markdown-it-attrs/-/markdown-it-attrs-4.1.3.tgz", + "integrity": "sha512-d5yg/lzQV2KFI/4LPsZQB3uxQrf0/l2/RnMPCPm4lYLOZUSmFlpPccyojnzaHkfQpAD8wBHfnfUW0aMhpKOS2g==", + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "markdown-it": ">= 9.0.0 < 13.0.0" + } + }, + "node_modules/markdown-it-chain": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/markdown-it-chain/-/markdown-it-chain-1.3.0.tgz", + "integrity": "sha512-XClV8I1TKy8L2qsT9iX3qiV+50ZtcInGXI80CA+DP62sMs7hXlyV/RM3hfwy5O3Ad0sJm9xIwQELgANfESo8mQ==", + "dependencies": { + "webpack-chain": "^4.9.0" + }, + "engines": { + "node": ">=6.9" + }, + "peerDependencies": { + "markdown-it": ">=5.0.0" + } + }, + "node_modules/markdown-it-chain/node_modules/webpack-chain": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/webpack-chain/-/webpack-chain-4.12.1.tgz", + "integrity": "sha512-BCfKo2YkDe2ByqkEWe1Rw+zko4LsyS75LVr29C6xIrxAg9JHJ4pl8kaIZ396SUSNp6b4815dRZPSTAS8LlURRQ==", + "dependencies": { + "deepmerge": "^1.5.2", + "javascript-stringify": "^1.6.0" + } + }, + "node_modules/markdown-it-container": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/markdown-it-container/-/markdown-it-container-2.0.0.tgz", + "integrity": "sha1-ABm0P9Au7+zi8ZYKKJX7qBpARpU=" + }, + "node_modules/markdown-it-emoji": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/markdown-it-emoji/-/markdown-it-emoji-1.4.0.tgz", + "integrity": "sha1-m+4OmpkKljupbfaYDE/dsF37Tcw=" + }, + "node_modules/markdown-it-table-of-contents": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/markdown-it-table-of-contents/-/markdown-it-table-of-contents-0.4.4.tgz", + "integrity": "sha512-TAIHTHPwa9+ltKvKPWulm/beozQU41Ab+FIefRaQV1NRnpzwcV9QOe6wXQS5WLivm5Q/nlo0rl6laGkMDZE7Gw==", + "engines": { + "node": ">6.4.0" + } + }, + "node_modules/markdown-it/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "node_modules/markdown-it/node_modules/entities": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", + "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/markdown-it/node_modules/linkify-it": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", + "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==", + "dependencies": { + "uc.micro": "^1.0.1" + } + }, + "node_modules/md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/mdn-data": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", + "integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==" + }, + "node_modules/mdurl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", + "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=" + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/memory-fs": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", + "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", + "dependencies": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "node_modules/merge-source-map": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.1.0.tgz", + "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==", + "dependencies": { + "source-map": "^0.6.1" + } + }, + "node_modules/merge-source-map/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/micromatch/node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/micromatch/node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "dependencies": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + }, + "bin": { + "miller-rabin": "bin/miller-rabin" + } + }, + "node_modules/miller-rabin/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mime-db": { + "version": "1.51.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", + "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.34", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", + "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", + "dependencies": { + "mime-db": "1.51.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/min-document": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz", + "integrity": "sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU=", + "dependencies": { + "dom-walk": "^0.1.0" + } + }, + "node_modules/mini-css-extract-plugin": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.6.0.tgz", + "integrity": "sha512-79q5P7YGI6rdnVyIAV4NXpBQJFWdkzJxCim3Kog4078fM0piAaFlwocqbejdWtLW1cEzCexPrh6EdyFsPgVdAw==", + "dependencies": { + "loader-utils": "^1.1.0", + "normalize-url": "^2.0.1", + "schema-utils": "^1.0.0", + "webpack-sources": "^1.1.0" + }, + "engines": { + "node": ">= 6.9.0" + }, + "peerDependencies": { + "webpack": "^4.4.0" + } + }, + "node_modules/mini-css-extract-plugin/node_modules/schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dependencies": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + }, + "node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" + }, + "node_modules/minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", + "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mississippi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", + "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", + "dependencies": { + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^3.0.0", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "dependencies": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/mixin-deep/node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dependencies": { + "minimist": "^1.2.5" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/move-concurrently": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", + "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", + "dependencies": { + "aproba": "^1.1.1", + "copy-concurrently": "^1.0.0", + "fs-write-stream-atomic": "^1.0.8", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.3" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "node_modules/multicast-dns": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", + "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", + "dependencies": { + "dns-packet": "^1.3.1", + "thunky": "^1.0.2" + }, + "bin": { + "multicast-dns": "cli.js" + } + }, + "node_modules/multicast-dns-service-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", + "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=" + }, + "node_modules/nan": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz", + "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==", + "optional": true + }, + "node_modules/nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nanomatch/node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nanomatch/node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" + }, + "node_modules/nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" + }, + "node_modules/no-case": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz", + "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==", + "dependencies": { + "lower-case": "^1.1.1" + } + }, + "node_modules/node-forge": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", + "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==", + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/node-libs-browser": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz", + "integrity": "sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==", + "dependencies": { + "assert": "^1.1.1", + "browserify-zlib": "^0.2.0", + "buffer": "^4.3.0", + "console-browserify": "^1.1.0", + "constants-browserify": "^1.0.0", + "crypto-browserify": "^3.11.0", + "domain-browser": "^1.1.1", + "events": "^3.0.0", + "https-browserify": "^1.0.0", + "os-browserify": "^0.3.0", + "path-browserify": "0.0.1", + "process": "^0.11.10", + "punycode": "^1.2.4", + "querystring-es3": "^0.2.0", + "readable-stream": "^2.3.3", + "stream-browserify": "^2.0.1", + "stream-http": "^2.7.2", + "string_decoder": "^1.0.0", + "timers-browserify": "^2.0.4", + "tty-browserify": "0.0.0", + "url": "^0.11.0", + "util": "^0.11.0", + "vm-browserify": "^1.0.1" + } + }, + "node_modules/node-libs-browser/node_modules/punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + }, + "node_modules/node-releases": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.1.tgz", + "integrity": "sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==" + }, + "node_modules/nopt": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", + "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=", + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "*" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-url": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-2.0.1.tgz", + "integrity": "sha512-D6MUW4K/VzoJ4rJ01JFKxDrtY1v9wrgzCX5f2qj/lzH1m/lW6MhUZFKerVsnyjOhOsYzI9Kqqak+10l4LvLpMw==", + "dependencies": { + "prepend-http": "^2.0.0", + "query-string": "^5.0.1", + "sort-keys": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "dependencies": { + "path-key": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/nprogress": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz", + "integrity": "sha1-y480xTIT2JVyP8urkH6UIq28r7E=" + }, + "node_modules/nth-check": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz", + "integrity": "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/num2fraction": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", + "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=" + }, + "node_modules/oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "engines": { + "node": "*" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dependencies": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz", + "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-is": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", + "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "dependencies": { + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "dependencies": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.getownpropertydescriptors": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.3.tgz", + "integrity": "sha512-VdDoCwvJI4QdC6ndjpqFmoL3/+HxffFBbcJzKi5hwLLqqx3mdbedRpfZDdK0SrOSauj8X4GzBvnDZl4vTN7dOw==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object.values": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.5.tgz", + "integrity": "sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==" + }, + "node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/opencollective-postinstall": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz", + "integrity": "sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==", + "bin": { + "opencollective-postinstall": "index.js" + } + }, + "node_modules/opn": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz", + "integrity": "sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==", + "dependencies": { + "is-wsl": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/optimize-css-assets-webpack-plugin": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/optimize-css-assets-webpack-plugin/-/optimize-css-assets-webpack-plugin-5.0.8.tgz", + "integrity": "sha512-mgFS1JdOtEGzD8l+EuISqL57cKO+We9GcoiQEmdCWRqqck+FGNmYJtx9qfAPzEz+lRrlThWMuGDaRkI/yWNx/Q==", + "dependencies": { + "cssnano": "^4.1.10", + "last-call-webpack-plugin": "^3.0.0" + }, + "peerDependencies": { + "webpack": "^4.0.0" + } + }, + "node_modules/os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=" + }, + "node_modules/p-cancelable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", + "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "engines": { + "node": ">=4" + } + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-map": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", + "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/p-retry": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-3.0.1.tgz", + "integrity": "sha512-XE6G4+YTTkT2a0UWb2kjZe8xNwf8bIbnqpc/IS/idOBVhyves0mK5OJgeocjx7q5pvX/6m23xuzVPYT1uGM73w==", + "dependencies": { + "retry": "^0.12.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/package-json": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz", + "integrity": "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==", + "dependencies": { + "got": "^9.6.0", + "registry-auth-token": "^4.0.0", + "registry-url": "^5.0.0", + "semver": "^6.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" + }, + "node_modules/parallel-transform": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.2.0.tgz", + "integrity": "sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==", + "dependencies": { + "cyclist": "^1.0.1", + "inherits": "^2.0.3", + "readable-stream": "^2.1.5" + } + }, + "node_modules/param-case": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz", + "integrity": "sha1-35T9jPZTHs915r75oIWPvHK+Ikc=", + "dependencies": { + "no-case": "^2.2.0" + } + }, + "node_modules/parse-asn1": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz", + "integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==", + "dependencies": { + "asn1.js": "^5.2.0", + "browserify-aes": "^1.0.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", + "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", + "dependencies": { + "parse5": "^6.0.1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", + "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==" + }, + "node_modules/path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=" + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=" + }, + "node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "engines": { + "node": ">=4" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "node_modules/path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dependencies": { + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/path-type/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "engines": { + "node": ">=4" + } + }, + "node_modules/pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", + "dependencies": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "engines": { + "node": ">=6" + } + }, + "node_modules/pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dependencies": { + "pinkie": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/portfinder": { + "version": "1.0.28", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", + "integrity": "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==", + "dependencies": { + "async": "^2.6.2", + "debug": "^3.1.1", + "mkdirp": "^0.5.5" + }, + "engines": { + "node": ">= 0.12.0" + } + }, + "node_modules/portfinder/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/portfinder/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-calc": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-7.0.5.tgz", + "integrity": "sha512-1tKHutbGtLtEZF6PT4JSihCHfIVldU72mZ8SdZHIYriIZ9fh9k9aWSppaT8rHsyI3dX+KSR+W+Ix9BMY3AODrg==", + "dependencies": { + "postcss": "^7.0.27", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.0.2" + } + }, + "node_modules/postcss-colormin": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-4.0.3.tgz", + "integrity": "sha512-WyQFAdDZpExQh32j0U0feWisZ0dmOtPl44qYmJKkq9xFWY3p+4qnRzCHeNrkeRhwPHz9bQ3mo0/yVkaply0MNw==", + "dependencies": { + "browserslist": "^4.0.0", + "color": "^3.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-colormin/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/postcss-convert-values": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-4.0.1.tgz", + "integrity": "sha512-Kisdo1y77KUC0Jmn0OXU/COOJbzM8cImvw1ZFsBgBgMgb1iL23Zs/LXRe3r+EZqM3vGYKdQ2YJVQ5VkJI+zEJQ==", + "dependencies": { + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-convert-values/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/postcss-discard-comments": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz", + "integrity": "sha512-RJutN259iuRf3IW7GZyLM5Sw4GLTOH8FmsXBnv8Ab/Tc2k4SR4qbV4DNbyyY4+Sjo362SyDmW2DQ7lBSChrpkg==", + "dependencies": { + "postcss": "^7.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-discard-duplicates": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.2.tgz", + "integrity": "sha512-ZNQfR1gPNAiXZhgENFfEglF93pciw0WxMkJeVmw8eF+JZBbMD7jp6C67GqJAXVZP2BWbOztKfbsdmMp/k8c6oQ==", + "dependencies": { + "postcss": "^7.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-discard-empty": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-4.0.1.tgz", + "integrity": "sha512-B9miTzbznhDjTfjvipfHoqbWKwd0Mj+/fL5s1QOz06wufguil+Xheo4XpOnc4NqKYBCNqqEzgPv2aPBIJLox0w==", + "dependencies": { + "postcss": "^7.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-discard-overridden": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-4.0.1.tgz", + "integrity": "sha512-IYY2bEDD7g1XM1IDEsUT4//iEYCxAmP5oDSFMVU/JVvT7gh+l4fmjciLqGgwjdWpQIdb0Che2VX00QObS5+cTg==", + "dependencies": { + "postcss": "^7.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-load-config": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-2.1.2.tgz", + "integrity": "sha512-/rDeGV6vMUo3mwJZmeHfEDvwnTKKqQ0S7OHUi/kJvvtx3aWtyWG2/0ZWnzCt2keEclwN6Tf0DST2v9kITdOKYw==", + "dependencies": { + "cosmiconfig": "^5.0.0", + "import-cwd": "^2.0.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-loader": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-3.0.0.tgz", + "integrity": "sha512-cLWoDEY5OwHcAjDnkyRQzAXfs2jrKjXpO/HQFcc5b5u/r7aa471wdmChmwfnv7x2u840iat/wi0lQ5nbRgSkUA==", + "dependencies": { + "loader-utils": "^1.1.0", + "postcss": "^7.0.0", + "postcss-load-config": "^2.0.0", + "schema-utils": "^1.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/postcss-loader/node_modules/schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dependencies": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/postcss-merge-longhand": { + "version": "4.0.11", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-4.0.11.tgz", + "integrity": "sha512-alx/zmoeXvJjp7L4mxEMjh8lxVlDFX1gqWHzaaQewwMZiVhLo42TEClKaeHbRf6J7j82ZOdTJ808RtN0ZOZwvw==", + "dependencies": { + "css-color-names": "0.0.4", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0", + "stylehacks": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-merge-longhand/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/postcss-merge-rules": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-4.0.3.tgz", + "integrity": "sha512-U7e3r1SbvYzO0Jr3UT/zKBVgYYyhAz0aitvGIYOYK5CPmkNih+WDSsS5tvPrJ8YMQYlEMvsZIiqmn7HdFUaeEQ==", + "dependencies": { + "browserslist": "^4.0.0", + "caniuse-api": "^3.0.0", + "cssnano-util-same-parent": "^4.0.0", + "postcss": "^7.0.0", + "postcss-selector-parser": "^3.0.0", + "vendors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-merge-rules/node_modules/postcss-selector-parser": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", + "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", + "dependencies": { + "dot-prop": "^5.2.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/postcss-minify-font-values": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-4.0.2.tgz", + "integrity": "sha512-j85oO6OnRU9zPf04+PZv1LYIYOprWm6IA6zkXkrJXyRveDEuQggG6tvoy8ir8ZwjLxLuGfNkCZEQG7zan+Hbtg==", + "dependencies": { + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-minify-font-values/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/postcss-minify-gradients": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-4.0.2.tgz", + "integrity": "sha512-qKPfwlONdcf/AndP1U8SJ/uzIJtowHlMaSioKzebAXSG4iJthlWC9iSWznQcX4f66gIWX44RSA841HTHj3wK+Q==", + "dependencies": { + "cssnano-util-get-arguments": "^4.0.0", + "is-color-stop": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-minify-gradients/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/postcss-minify-params": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-4.0.2.tgz", + "integrity": "sha512-G7eWyzEx0xL4/wiBBJxJOz48zAKV2WG3iZOqVhPet/9geefm/Px5uo1fzlHu+DOjT+m0Mmiz3jkQzVHe6wxAWg==", + "dependencies": { + "alphanum-sort": "^1.0.0", + "browserslist": "^4.0.0", + "cssnano-util-get-arguments": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0", + "uniqs": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-minify-params/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/postcss-minify-selectors": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-4.0.2.tgz", + "integrity": "sha512-D5S1iViljXBj9kflQo4YutWnJmwm8VvIsU1GeXJGiG9j8CIg9zs4voPMdQDUmIxetUOh60VilsNzCiAFTOqu3g==", + "dependencies": { + "alphanum-sort": "^1.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-selector-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-minify-selectors/node_modules/postcss-selector-parser": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", + "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", + "dependencies": { + "dot-prop": "^5.2.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/postcss-modules-extract-imports": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz", + "integrity": "sha512-LaYLDNS4SG8Q5WAWqIJgdHPJrDDr/Lv775rMBFUbgjTz6j34lUznACHcdRWroPvXANP2Vj7yNK57vp9eFqzLWQ==", + "dependencies": { + "postcss": "^7.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/postcss-modules-local-by-default": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-2.0.6.tgz", + "integrity": "sha512-oLUV5YNkeIBa0yQl7EYnxMgy4N6noxmiwZStaEJUSe2xPMcdNc8WmBQuQCx18H5psYbVxz8zoHk0RAAYZXP9gA==", + "dependencies": { + "postcss": "^7.0.6", + "postcss-selector-parser": "^6.0.0", + "postcss-value-parser": "^3.3.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/postcss-modules-local-by-default/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/postcss-modules-scope": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-2.2.0.tgz", + "integrity": "sha512-YyEgsTMRpNd+HmyC7H/mh3y+MeFWevy7V1evVhJWewmMbjDHIbZbOXICC2y+m1xI1UVfIT1HMW/O04Hxyu9oXQ==", + "dependencies": { + "postcss": "^7.0.6", + "postcss-selector-parser": "^6.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/postcss-modules-values": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-2.0.0.tgz", + "integrity": "sha512-Ki7JZa7ff1N3EIMlPnGTZfUMe69FFwiQPnVSXC9mnn3jozCRBYIxiZd44yJOV2AmabOo4qFf8s0dC/+lweG7+w==", + "dependencies": { + "icss-replace-symbols": "^1.1.0", + "postcss": "^7.0.6" + } + }, + "node_modules/postcss-normalize-charset": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz", + "integrity": "sha512-gMXCrrlWh6G27U0hF3vNvR3w8I1s2wOBILvA87iNXaPvSNo5uZAMYsZG7XjCUf1eVxuPfyL4TJ7++SGZLc9A3g==", + "dependencies": { + "postcss": "^7.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-normalize-display-values": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.2.tgz", + "integrity": "sha512-3F2jcsaMW7+VtRMAqf/3m4cPFhPD3EFRgNs18u+k3lTJJlVe7d0YPO+bnwqo2xg8YiRpDXJI2u8A0wqJxMsQuQ==", + "dependencies": { + "cssnano-util-get-match": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-normalize-display-values/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/postcss-normalize-positions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-4.0.2.tgz", + "integrity": "sha512-Dlf3/9AxpxE+NF1fJxYDeggi5WwV35MXGFnnoccP/9qDtFrTArZ0D0R+iKcg5WsUd8nUYMIl8yXDCtcrT8JrdA==", + "dependencies": { + "cssnano-util-get-arguments": "^4.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-normalize-positions/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/postcss-normalize-repeat-style": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-4.0.2.tgz", + "integrity": "sha512-qvigdYYMpSuoFs3Is/f5nHdRLJN/ITA7huIoCyqqENJe9PvPmLhNLMu7QTjPdtnVf6OcYYO5SHonx4+fbJE1+Q==", + "dependencies": { + "cssnano-util-get-arguments": "^4.0.0", + "cssnano-util-get-match": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-normalize-repeat-style/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/postcss-normalize-string": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-4.0.2.tgz", + "integrity": "sha512-RrERod97Dnwqq49WNz8qo66ps0swYZDSb6rM57kN2J+aoyEAJfZ6bMx0sx/F9TIEX0xthPGCmeyiam/jXif0eA==", + "dependencies": { + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-normalize-string/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/postcss-normalize-timing-functions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-4.0.2.tgz", + "integrity": "sha512-acwJY95edP762e++00Ehq9L4sZCEcOPyaHwoaFOhIwWCDfik6YvqsYNxckee65JHLKzuNSSmAdxwD2Cud1Z54A==", + "dependencies": { + "cssnano-util-get-match": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-normalize-timing-functions/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/postcss-normalize-unicode": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-4.0.1.tgz", + "integrity": "sha512-od18Uq2wCYn+vZ/qCOeutvHjB5jm57ToxRaMeNuf0nWVHaP9Hua56QyMF6fs/4FSUnVIw0CBPsU0K4LnBPwYwg==", + "dependencies": { + "browserslist": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-normalize-unicode/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/postcss-normalize-url": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-4.0.1.tgz", + "integrity": "sha512-p5oVaF4+IHwu7VpMan/SSpmpYxcJMtkGppYf0VbdH5B6hN8YNmVyJLuY9FmLQTzY3fag5ESUUHDqM+heid0UVA==", + "dependencies": { + "is-absolute-url": "^2.0.0", + "normalize-url": "^3.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-normalize-url/node_modules/normalize-url": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-3.3.0.tgz", + "integrity": "sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/postcss-normalize-url/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/postcss-normalize-whitespace": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-4.0.2.tgz", + "integrity": "sha512-tO8QIgrsI3p95r8fyqKV+ufKlSHh9hMJqACqbv2XknufqEDhDvbguXGBBqxw9nsQoXWf0qOqppziKJKHMD4GtA==", + "dependencies": { + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-normalize-whitespace/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/postcss-ordered-values": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-4.1.2.tgz", + "integrity": "sha512-2fCObh5UanxvSxeXrtLtlwVThBvHn6MQcu4ksNT2tsaV2Fg76R2CV98W7wNSlX+5/pFwEyaDwKLLoEV7uRybAw==", + "dependencies": { + "cssnano-util-get-arguments": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-ordered-values/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/postcss-reduce-initial": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-4.0.3.tgz", + "integrity": "sha512-gKWmR5aUulSjbzOfD9AlJiHCGH6AEVLaM0AV+aSioxUDd16qXP1PCh8d1/BGVvpdWn8k/HiK7n6TjeoXN1F7DA==", + "dependencies": { + "browserslist": "^4.0.0", + "caniuse-api": "^3.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-reduce-transforms": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-4.0.2.tgz", + "integrity": "sha512-EEVig1Q2QJ4ELpJXMZR8Vt5DQx8/mo+dGWSR7vWXqcob2gQLyQGsionYcGKATXvQzMPn6DSN1vTN7yFximdIAg==", + "dependencies": { + "cssnano-util-get-match": "^4.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-reduce-transforms/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/postcss-safe-parser": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-4.0.2.tgz", + "integrity": "sha512-Uw6ekxSWNLCPesSv/cmqf2bY/77z11O7jZGPax3ycZMFU/oi2DMH9i89AdHc1tRwFg/arFoEwX0IS3LCUxJh1g==", + "dependencies": { + "postcss": "^7.0.26" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.0.9", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.9.tgz", + "integrity": "sha512-UO3SgnZOVTwu4kyLR22UQ1xZh086RyNZppb7lLAKBFK8a32ttG5i87Y/P3+2bRSjZNyJ1B7hfFNo273tKe9YxQ==", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-svgo": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-4.0.3.tgz", + "integrity": "sha512-NoRbrcMWTtUghzuKSoIm6XV+sJdvZ7GZSc3wdBN0W19FTtp2ko8NqLsgoh/m9CzNhU3KLPvQmjIwtaNFkaFTvw==", + "dependencies": { + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0", + "svgo": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-svgo/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/postcss-unique-selectors": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-4.0.1.tgz", + "integrity": "sha512-+JanVaryLo9QwZjKrmJgkI4Fn8SBgRO6WXQBJi7KiAVPlmxikB5Jzc4EvXMT2H0/m0RjrVVm9rGNhZddm/8Spg==", + "dependencies": { + "alphanum-sort": "^1.0.0", + "postcss": "^7.0.0", + "uniqs": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "node_modules/postcss/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==" + }, + "node_modules/postcss/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/prepend-http": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", + "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", + "engines": { + "node": ">=4" + } + }, + "node_modules/prettier": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.5.1.tgz", + "integrity": "sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg==", + "optional": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/pretty-error": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.2.tgz", + "integrity": "sha512-EY5oDzmsX5wvuynAByrmY0P0hcp+QpnAKbJng2A2MPjVKXCxrDSUkzghVJ4ZGPIv+JC4gX8fPUWscC0RtjsWGw==", + "dependencies": { + "lodash": "^4.17.20", + "renderkid": "^2.0.4" + } + }, + "node_modules/pretty-time": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pretty-time/-/pretty-time-1.1.0.tgz", + "integrity": "sha512-28iF6xPQrP8Oa6uxE6a1biz+lWeTOAPKggvjB8HAs6nVMKZwf5bG++632Dx614hIWgUPkgivRfG+a8uAXGTIbA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/prismjs": { + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz", + "integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==", + "engines": { + "node": ">=6" + } + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "node_modules/promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "dependencies": { + "asap": "~2.0.3" + } + }, + "node_modules/promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=" + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=" + }, + "node_modules/pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" + }, + "node_modules/psl": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" + }, + "node_modules/public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "dependencies": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/public-encrypt/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "node_modules/pug": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/pug/-/pug-3.0.2.tgz", + "integrity": "sha512-bp0I/hiK1D1vChHh6EfDxtndHji55XP/ZJKwsRqrz6lRia6ZC2OZbdAymlxdVFwd1L70ebrVJw4/eZ79skrIaw==", + "dependencies": { + "pug-code-gen": "^3.0.2", + "pug-filters": "^4.0.0", + "pug-lexer": "^5.0.1", + "pug-linker": "^4.0.0", + "pug-load": "^3.0.0", + "pug-parser": "^6.0.0", + "pug-runtime": "^3.0.1", + "pug-strip-comments": "^2.0.0" + } + }, + "node_modules/pug-attrs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pug-attrs/-/pug-attrs-3.0.0.tgz", + "integrity": "sha512-azINV9dUtzPMFQktvTXciNAfAuVh/L/JCl0vtPCwvOA21uZrC08K/UnmrL+SXGEVc1FwzjW62+xw5S/uaLj6cA==", + "dependencies": { + "constantinople": "^4.0.1", + "js-stringify": "^1.0.2", + "pug-runtime": "^3.0.0" + } + }, + "node_modules/pug-code-gen": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/pug-code-gen/-/pug-code-gen-3.0.2.tgz", + "integrity": "sha512-nJMhW16MbiGRiyR4miDTQMRWDgKplnHyeLvioEJYbk1RsPI3FuA3saEP8uwnTb2nTJEKBU90NFVWJBk4OU5qyg==", + "dependencies": { + "constantinople": "^4.0.1", + "doctypes": "^1.1.0", + "js-stringify": "^1.0.2", + "pug-attrs": "^3.0.0", + "pug-error": "^2.0.0", + "pug-runtime": "^3.0.0", + "void-elements": "^3.1.0", + "with": "^7.0.0" + } + }, + "node_modules/pug-error": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pug-error/-/pug-error-2.0.0.tgz", + "integrity": "sha512-sjiUsi9M4RAGHktC1drQfCr5C5eriu24Lfbt4s+7SykztEOwVZtbFk1RRq0tzLxcMxMYTBR+zMQaG07J/btayQ==" + }, + "node_modules/pug-filters": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pug-filters/-/pug-filters-4.0.0.tgz", + "integrity": "sha512-yeNFtq5Yxmfz0f9z2rMXGw/8/4i1cCFecw/Q7+D0V2DdtII5UvqE12VaZ2AY7ri6o5RNXiweGH79OCq+2RQU4A==", + "dependencies": { + "constantinople": "^4.0.1", + "jstransformer": "1.0.0", + "pug-error": "^2.0.0", + "pug-walk": "^2.0.0", + "resolve": "^1.15.1" + } + }, + "node_modules/pug-lexer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/pug-lexer/-/pug-lexer-5.0.1.tgz", + "integrity": "sha512-0I6C62+keXlZPZkOJeVam9aBLVP2EnbeDw3An+k0/QlqdwH6rv8284nko14Na7c0TtqtogfWXcRoFE4O4Ff20w==", + "dependencies": { + "character-parser": "^2.2.0", + "is-expression": "^4.0.0", + "pug-error": "^2.0.0" + } + }, + "node_modules/pug-linker": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pug-linker/-/pug-linker-4.0.0.tgz", + "integrity": "sha512-gjD1yzp0yxbQqnzBAdlhbgoJL5qIFJw78juN1NpTLt/mfPJ5VgC4BvkoD3G23qKzJtIIXBbcCt6FioLSFLOHdw==", + "dependencies": { + "pug-error": "^2.0.0", + "pug-walk": "^2.0.0" + } + }, + "node_modules/pug-load": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pug-load/-/pug-load-3.0.0.tgz", + "integrity": "sha512-OCjTEnhLWZBvS4zni/WUMjH2YSUosnsmjGBB1An7CsKQarYSWQ0GCVyd4eQPMFJqZ8w9xgs01QdiZXKVjk92EQ==", + "dependencies": { + "object-assign": "^4.1.1", + "pug-walk": "^2.0.0" + } + }, + "node_modules/pug-parser": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/pug-parser/-/pug-parser-6.0.0.tgz", + "integrity": "sha512-ukiYM/9cH6Cml+AOl5kETtM9NR3WulyVP2y4HOU45DyMim1IeP/OOiyEWRr6qk5I5klpsBnbuHpwKmTx6WURnw==", + "dependencies": { + "pug-error": "^2.0.0", + "token-stream": "1.0.0" + } + }, + "node_modules/pug-plain-loader": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pug-plain-loader/-/pug-plain-loader-1.1.0.tgz", + "integrity": "sha512-1nYgIJLaahRuHJHhzSPODV44aZfb00bO7kiJiMkke6Hj4SVZftuvx6shZ4BOokk50dJc2RSFqNUBOlus0dniFQ==", + "dependencies": { + "loader-utils": "^1.1.0" + }, + "peerDependencies": { + "pug": "^2.0.0 || ^3.0.0" + } + }, + "node_modules/pug-runtime": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/pug-runtime/-/pug-runtime-3.0.1.tgz", + "integrity": "sha512-L50zbvrQ35TkpHwv0G6aLSuueDRwc/97XdY8kL3tOT0FmhgG7UypU3VztfV/LATAvmUfYi4wNxSajhSAeNN+Kg==" + }, + "node_modules/pug-strip-comments": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pug-strip-comments/-/pug-strip-comments-2.0.0.tgz", + "integrity": "sha512-zo8DsDpH7eTkPHCXFeAk1xZXJbyoTfdPlNR0bK7rpOMuhBYb0f5qUVCO1xlsitYd3w5FQTK7zpNVKb3rZoUrrQ==", + "dependencies": { + "pug-error": "^2.0.0" + } + }, + "node_modules/pug-walk": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pug-walk/-/pug-walk-2.0.0.tgz", + "integrity": "sha512-yYELe9Q5q9IQhuvqsZNwA5hfPkMJ8u92bQLIMcsMxf/VADjNtEYptU+inlufAFYcWdHlwNfZOEnOOQrZrcyJCQ==" + }, + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/pumpify": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", + "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", + "dependencies": { + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" + } + }, + "node_modules/pumpify/node_modules/pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/pupa": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz", + "integrity": "sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==", + "dependencies": { + "escape-goat": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=", + "engines": { + "node": ">=0.6.0", + "teleport": ">=0.2.0" + } + }, + "node_modules/qs": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", + "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/query-string": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz", + "integrity": "sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==", + "dependencies": { + "decode-uri-component": "^0.2.0", + "object-assign": "^4.1.0", + "strict-uri-encode": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/querystring": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.1.tgz", + "integrity": "sha512-wkvS7mL/JMugcup3/rMitHmd9ecIGd2lhFhK9N3UUQ450h66d1r3Y9nvXzQAW1Lq+wyx61k/1pfKS5KuKiyEbg==", + "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "dependencies": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.2.tgz", + "integrity": "sha512-RPMAFUJP19WIet/99ngh6Iv8fzAbqum4Li7AD6DtGaW2RpMB/11xDoalPiJMTbu6I3hkbMVkATvZrqb9EEqeeQ==", + "dependencies": { + "bytes": "3.1.1", + "http-errors": "1.8.1", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/raw-body/node_modules/bytes": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.1.tgz", + "integrity": "sha512-dWe4nWO/ruEOY7HkUJ5gFt1DCFV9zPRoJr8pV0/ASQermOZjtq8jMjOprC0Kd10GLN+l7xaUPvxzJFWtxGu8Fg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "dependencies": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/reduce": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/reduce/-/reduce-1.0.2.tgz", + "integrity": "sha512-xX7Fxke/oHO5IfZSk77lvPa/7bjMh9BuCk4OOoX5XTXrM7s0Z+MkPfSDfz0q7r91BhhGSs8gii/VEN/7zhCPpQ==", + "dependencies": { + "object-keys": "^1.1.0" + } + }, + "node_modules/regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==" + }, + "node_modules/regenerate-unicode-properties": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-9.0.0.tgz", + "integrity": "sha512-3E12UeNSPfjrgwjkR81m5J7Aw/T55Tu7nUyZVQYCKEOs+2dkxEY+DpPtZzO4YruuiPb7NkYLVcyJC4+zCbk5pA==", + "dependencies": { + "regenerate": "^1.4.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.13.9", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", + "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" + }, + "node_modules/regenerator-transform": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.5.tgz", + "integrity": "sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw==", + "dependencies": { + "@babel/runtime": "^7.8.4" + } + }, + "node_modules/regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dependencies": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/regex-not/node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/regex-not/node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.1.tgz", + "integrity": "sha512-pMR7hBVUUGI7PMA37m2ofIdQCsomVnas+Jn5UPGAHQ+/LlwKm/aTLJHdasmHRzlfeZwHiAOaRSo2rbBDm3nNUQ==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexpu-core": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.8.0.tgz", + "integrity": "sha512-1F6bYsoYiz6is+oz70NWur2Vlh9KWtswuRuzJOfeYUrfPX2o8n74AnUVaOGDbUqVGO9fNHu48/pjJO4sNVwsOg==", + "dependencies": { + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^9.0.0", + "regjsgen": "^0.5.2", + "regjsparser": "^0.7.0", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/registry-auth-token": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.1.tgz", + "integrity": "sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw==", + "dependencies": { + "rc": "^1.2.8" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/registry-url": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz", + "integrity": "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==", + "dependencies": { + "rc": "^1.2.8" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/regjsgen": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.2.tgz", + "integrity": "sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A==" + }, + "node_modules/regjsparser": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.7.0.tgz", + "integrity": "sha512-A4pcaORqmNMDVwUjWoTzuhwMGpP+NykpfqAsEgI1FSH/EzC7lrN5TMd+kN8YCovX+jMpu8eaqXgXPCa0g8FQNQ==", + "dependencies": { + "jsesc": "~0.5.0" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/regjsparser/node_modules/jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", + "bin": { + "jsesc": "bin/jsesc" + } + }, + "node_modules/relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=" + }, + "node_modules/renderkid": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.7.tgz", + "integrity": "sha512-oCcFyxaMrKsKcTY59qnCAtmDVSLfPbrv6A3tVbPdFMMrv5jaK10V6m40cKsoPNhAqN6rmHW9sswW4o3ruSrwUQ==", + "dependencies": { + "css-select": "^4.1.3", + "dom-converter": "^0.2.0", + "htmlparser2": "^6.1.0", + "lodash": "^4.17.21", + "strip-ansi": "^3.0.1" + } + }, + "node_modules/repeat-element": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz", + "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", + "dependencies": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" + }, + "node_modules/resolve": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", + "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", + "dependencies": { + "is-core-module": "^2.8.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", + "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", + "dependencies": { + "resolve-from": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "deprecated": "https://github.com/lydell/resolve-url#deprecated" + }, + "node_modules/responselike": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", + "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", + "dependencies": { + "lowercase-keys": "^1.0.0" + } + }, + "node_modules/ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "engines": { + "node": ">=0.12" + } + }, + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=", + "engines": { + "node": ">= 4" + } + }, + "node_modules/rgb-regex": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/rgb-regex/-/rgb-regex-1.0.1.tgz", + "integrity": "sha1-wODWiC3w4jviVKR16O3UGRX+rrE=" + }, + "node_modules/rgba-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/rgba-regex/-/rgba-regex-1.0.0.tgz", + "integrity": "sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=" + }, + "node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "node_modules/run-queue": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", + "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", + "dependencies": { + "aproba": "^1.1.1" + } + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "dependencies": { + "ret": "~0.1.10" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, + "node_modules/schema-utils": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", + "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "dependencies": { + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 8.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/section-matter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz", + "integrity": "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==", + "dependencies": { + "extend-shallow": "^2.0.1", + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=" + }, + "node_modules/selfsigned": { + "version": "1.10.14", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.14.tgz", + "integrity": "sha512-lkjaiAye+wBZDCBsu5BGi0XiLRxeUlsGod5ZP924CRSEoGuZAw/f7y9RKu28rwTfiHVhdavhB0qH0INV6P1lEA==", + "dependencies": { + "node-forge": "^0.10.0" + } + }, + "node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/semver-diff": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz", + "integrity": "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==", + "dependencies": { + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/send": { + "version": "0.17.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.2.tgz", + "integrity": "sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww==", + "dependencies": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "1.8.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "node_modules/send/node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/serialize-javascript": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", + "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", + "dependencies": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/serve-index/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/serve-index/node_modules/http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "node_modules/serve-index/node_modules/setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" + }, + "node_modules/serve-static": { + "version": "1.14.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.2.tgz", + "integrity": "sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ==", + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, + "node_modules/set-value": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "dependencies": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "node_modules/sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "bin": { + "sha.js": "bin.js" + } + }, + "node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.6.tgz", + "integrity": "sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ==" + }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/simple-swizzle/node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" + }, + "node_modules/sitemap": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/sitemap/-/sitemap-3.2.2.tgz", + "integrity": "sha512-TModL/WU4m2q/mQcrDgNANn0P4LwprM9MMvG4hu5zP4c6IIKs2YLTu6nXXnNr8ODW/WFtxKggiJ1EGn2W0GNmg==", + "dependencies": { + "lodash.chunk": "^4.2.0", + "lodash.padstart": "^4.6.1", + "whatwg-url": "^7.0.0", + "xmlbuilder": "^13.0.0" + }, + "engines": { + "node": ">=6.0.0", + "npm": ">=4.0.0" + } + }, + "node_modules/slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/smoothscroll-polyfill": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/smoothscroll-polyfill/-/smoothscroll-polyfill-0.4.4.tgz", + "integrity": "sha512-TK5ZA9U5RqCwMpfoMq/l1mrH0JAR7y7KRvOBx0n2869aLxch+gT9GhN3yUfjiw+d/DiF1mKo14+hd62JyMmoBg==" + }, + "node_modules/snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "dependencies": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dependencies": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node/node_modules/define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node/node_modules/is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node/node_modules/is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node/node_modules/is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dependencies": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dependencies": { + "kind-of": "^3.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-util/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/snapdragon/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/source-map-resolve": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", + "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", + "deprecated": "See https://github.com/lydell/source-map-resolve#deprecated", + "dependencies": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "node_modules/sockjs": { + "version": "0.3.24", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", + "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", + "dependencies": { + "faye-websocket": "^0.11.3", + "uuid": "^8.3.2", + "websocket-driver": "^0.7.4" + } + }, + "node_modules/sockjs-client": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.5.2.tgz", + "integrity": "sha512-ZzRxPBISQE7RpzlH4tKJMQbHM9pabHluk0WBaxAQ+wm/UieeBVBou0p4wVnSQGN9QmpAZygQ0cDIypWuqOFmFQ==", + "dependencies": { + "debug": "^3.2.6", + "eventsource": "^1.0.7", + "faye-websocket": "^0.11.3", + "inherits": "^2.0.4", + "json3": "^3.3.3", + "url-parse": "^1.5.3" + } + }, + "node_modules/sockjs-client/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/sockjs-client/node_modules/eventsource": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.1.2.tgz", + "integrity": "sha512-xAH3zWhgO2/3KIniEKYPr8plNSzlGINOUqYj0m0u7AB81iRw8b/3E73W6AuU+6klLbaSFmZnaETQ2lXPfAydrA==", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/sockjs-client/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/sockjs/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/sort-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-2.0.0.tgz", + "integrity": "sha1-ZYU1WEhh7JfXMNbPQYIuH1ZoQSg=", + "dependencies": { + "is-plain-obj": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/sort-keys/node_modules/is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-list-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", + "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==" + }, + "node_modules/source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/source-map-resolve": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.6.0.tgz", + "integrity": "sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w==", + "deprecated": "See https://github.com/lydell/source-map-resolve#deprecated", + "dependencies": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-url": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", + "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==", + "deprecated": "See https://github.com/lydell/source-map-url#deprecated" + }, + "node_modules/spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "dependencies": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "dependencies": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + } + }, + "node_modules/spdy-transport/node_modules/debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/spdy-transport/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/spdy-transport/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/spdy/node_modules/debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/spdy/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dependencies": { + "extend-shallow": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/split-string/node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/split-string/node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" + }, + "node_modules/sshpk": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", + "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==", + "dependencies": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + }, + "bin": { + "sshpk-conv": "bin/sshpk-conv", + "sshpk-sign": "bin/sshpk-sign", + "sshpk-verify": "bin/sshpk-verify" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ssri": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.2.tgz", + "integrity": "sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q==", + "dependencies": { + "figgy-pudding": "^3.5.1" + } + }, + "node_modules/stable": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", + "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", + "deprecated": "Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility" + }, + "node_modules/stack-utils": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.5.tgz", + "integrity": "sha512-KZiTzuV3CnSnSvgMRrARVCj+Ht7rMbauGDK0LdVFRGyenwdylpajAp4Q0i6SX8rEmbTpMMf6ryq2gb8pPq2WgQ==", + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "engines": { + "node": ">=8" + } + }, + "node_modules/static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "dependencies": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/std-env": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-2.3.1.tgz", + "integrity": "sha512-eOsoKTWnr6C8aWrqJJ2KAReXoa7Vn5Ywyw6uCXgA/xDhxPoaIsBa5aNJmISY04dLwXPBnDHW4diGM7Sn5K4R/g==", + "dependencies": { + "ci-info": "^3.1.1" + } + }, + "node_modules/stream-browserify": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", + "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", + "dependencies": { + "inherits": "~2.0.1", + "readable-stream": "^2.0.2" + } + }, + "node_modules/stream-each": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz", + "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==", + "dependencies": { + "end-of-stream": "^1.1.0", + "stream-shift": "^1.0.0" + } + }, + "node_modules/stream-http": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", + "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", + "dependencies": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.3.6", + "to-arraybuffer": "^1.0.0", + "xtend": "^4.0.0" + } + }, + "node_modules/stream-shift": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", + "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==" + }, + "node_modules/strict-uri-encode": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", + "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dependencies": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", + "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", + "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-bom-string": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz", + "integrity": "sha1-5SEekiQ2n7uB1jOi8ABE3IztrZI=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stylehacks": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-4.0.3.tgz", + "integrity": "sha512-7GlLk9JwlElY4Y6a/rmbH2MhVlTyVmiJd1PfTCqFaIBEGMYNsrO/v3SeGTdhBThLg4Z+NbOk/qFMwCa+J+3p/g==", + "dependencies": { + "browserslist": "^4.0.0", + "postcss": "^7.0.0", + "postcss-selector-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/stylehacks/node_modules/postcss-selector-parser": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", + "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", + "dependencies": { + "dot-prop": "^5.2.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/stylus": { + "version": "0.56.0", + "resolved": "https://registry.npmjs.org/stylus/-/stylus-0.56.0.tgz", + "integrity": "sha512-Ev3fOb4bUElwWu4F9P9WjnnaSpc8XB9OFHSFZSKMFL1CE1oM+oFXWEgAqPmmZIyhBihuqIQlFsVTypiiS9RxeA==", + "dependencies": { + "css": "^3.0.0", + "debug": "^4.3.2", + "glob": "^7.1.6", + "safer-buffer": "^2.1.2", + "sax": "~1.2.4", + "source-map": "^0.7.3" + }, + "bin": { + "stylus": "bin/stylus" + }, + "engines": { + "node": "*" + } + }, + "node_modules/stylus-loader": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/stylus-loader/-/stylus-loader-3.0.2.tgz", + "integrity": "sha512-+VomPdZ6a0razP+zinir61yZgpw2NfljeSsdUF5kJuEzlo3khXhY19Fn6l8QQz1GRJGtMCo8nG5C04ePyV7SUA==", + "dependencies": { + "loader-utils": "^1.0.2", + "lodash.clonedeep": "^4.5.0", + "when": "~3.6.x" + }, + "peerDependencies": { + "stylus": ">=0.52.4" + } + }, + "node_modules/stylus/node_modules/debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/stylus/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/svg-tags": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz", + "integrity": "sha1-WPcc7jvVGbWdSyqEO2x95krAR2Q=" + }, + "node_modules/svgo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.3.2.tgz", + "integrity": "sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw==", + "deprecated": "This SVGO version is no longer supported. Upgrade to v2.x.x.", + "dependencies": { + "chalk": "^2.4.1", + "coa": "^2.0.2", + "css-select": "^2.0.0", + "css-select-base-adapter": "^0.1.1", + "css-tree": "1.0.0-alpha.37", + "csso": "^4.0.2", + "js-yaml": "^3.13.1", + "mkdirp": "~0.5.1", + "object.values": "^1.1.0", + "sax": "~1.2.4", + "stable": "^0.1.8", + "unquote": "~1.1.1", + "util.promisify": "~1.0.0" + }, + "bin": { + "svgo": "bin/svgo" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/svgo/node_modules/css-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-2.1.0.tgz", + "integrity": "sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^3.2.1", + "domutils": "^1.7.0", + "nth-check": "^1.0.2" + } + }, + "node_modules/svgo/node_modules/css-what": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-3.4.2.tgz", + "integrity": "sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/svgo/node_modules/dom-serializer": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", + "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", + "dependencies": { + "domelementtype": "^2.0.1", + "entities": "^2.0.0" + } + }, + "node_modules/svgo/node_modules/dom-serializer/node_modules/domelementtype": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", + "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/svgo/node_modules/domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" + }, + "node_modules/svgo/node_modules/domutils": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", + "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "dependencies": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "node_modules/svgo/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/svgo/node_modules/nth-check": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", + "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", + "dependencies": { + "boolbase": "~1.0.0" + } + }, + "node_modules/tapable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/term-size": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.1.tgz", + "integrity": "sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/terser": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.1.tgz", + "integrity": "sha512-4GnLC0x667eJG0ewJTa6z/yXrbLGv80D9Ru6HIpCQmO+Q4PfEtBFi0ObSckqwL6VyQv/7ENJieXHo2ANmdQwgw==", + "dependencies": { + "commander": "^2.20.0", + "source-map": "~0.6.1", + "source-map-support": "~0.5.12" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.5.tgz", + "integrity": "sha512-04Rfe496lN8EYruwi6oPQkG0vo8C+HT49X687FZnpPF0qMAIHONI6HEXYPKDOE8e5HjXTyKfqRd/agHtH0kOtw==", + "dependencies": { + "cacache": "^12.0.2", + "find-cache-dir": "^2.1.0", + "is-wsl": "^1.1.0", + "schema-utils": "^1.0.0", + "serialize-javascript": "^4.0.0", + "source-map": "^0.6.1", + "terser": "^4.1.2", + "webpack-sources": "^1.4.0", + "worker-farm": "^1.7.0" + }, + "engines": { + "node": ">= 6.9.0" + }, + "peerDependencies": { + "webpack": "^4.0.0" + } + }, + "node_modules/terser-webpack-plugin/node_modules/find-cache-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", + "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^2.0.0", + "pkg-dir": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/terser-webpack-plugin/node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/terser-webpack-plugin/node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/terser-webpack-plugin/node_modules/make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dependencies": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/terser-webpack-plugin/node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/terser-webpack-plugin/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "engines": { + "node": ">=4" + } + }, + "node_modules/terser-webpack-plugin/node_modules/pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dependencies": { + "find-up": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/terser-webpack-plugin/node_modules/schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dependencies": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/terser-webpack-plugin/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/terser-webpack-plugin/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, + "node_modules/terser/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=" + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + }, + "node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==" + }, + "node_modules/timers-browserify": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.12.tgz", + "integrity": "sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==", + "dependencies": { + "setimmediate": "^1.0.4" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/timsort": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", + "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=" + }, + "node_modules/tiny-cookie": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/tiny-cookie/-/tiny-cookie-2.3.2.tgz", + "integrity": "sha512-qbymkVh+6+Gc/c9sqnvbG+dOHH6bschjphK3SHgIfT6h/t+63GBL37JXNoXEc6u/+BcwU6XmaWUuf19ouLVtPg==" + }, + "node_modules/to-arraybuffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", + "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=" + }, + "node_modules/to-factory": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-factory/-/to-factory-1.0.0.tgz", + "integrity": "sha1-hzivi9lxIK0dQEeXKtpVY7+UebE=" + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "engines": { + "node": ">=4" + } + }, + "node_modules/to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-object-path/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-readable-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", + "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==", + "engines": { + "node": ">=6" + } + }, + "node_modules/to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dependencies": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex/node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex/node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/token-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/token-stream/-/token-stream-1.0.0.tgz", + "integrity": "sha1-zCAOqyYT9BZtJ/+a/HylbUnfbrQ=" + }, + "node_modules/toml": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/toml/-/toml-3.0.0.tgz", + "integrity": "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==" + }, + "node_modules/toposort": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/toposort/-/toposort-1.0.7.tgz", + "integrity": "sha1-LmhELZ9k7HILjMieZEOsbKqVACk=" + }, + "node_modules/tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dependencies": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tr46": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", + "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" + }, + "node_modules/tty-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=" + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + }, + "node_modules/typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dependencies": { + "is-typedarray": "^1.0.0" + } + }, + "node_modules/uc.micro": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", + "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==" + }, + "node_modules/uglify-js": { + "version": "3.4.10", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.10.tgz", + "integrity": "sha512-Y2VsbPVs0FIshJztycsO2SfPk7/KAF/T72qzv9u5EpQ4kB2hQoHlhNQTsNyy6ul7lQtqJN/AoWeS23OzEiEFxw==", + "dependencies": { + "commander": "~2.19.0", + "source-map": "~0.6.1" + }, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/uglify-js/node_modules/commander": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz", + "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==" + }, + "node_modules/uglify-js/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unbox-primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", + "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "dependencies": { + "function-bind": "^1.1.1", + "has-bigints": "^1.0.1", + "has-symbols": "^1.0.2", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", + "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "dependencies": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-value-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz", + "integrity": "sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-property-aliases-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz", + "integrity": "sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/union-value": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "dependencies": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/uniq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", + "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=" + }, + "node_modules/uniqs": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz", + "integrity": "sha1-/+3ks2slKQaW5uFl1KWe25mOawI=" + }, + "node_modules/unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "dependencies": { + "unique-slug": "^2.0.0" + } + }, + "node_modules/unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "dependencies": { + "imurmurhash": "^0.1.4" + } + }, + "node_modules/unique-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", + "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "dependencies": { + "crypto-random-string": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/unquote": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz", + "integrity": "sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ=" + }, + "node_modules/unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "dependencies": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "dependencies": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-value/node_modules/isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dependencies": { + "isarray": "1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/upath": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", + "engines": { + "node": ">=4", + "yarn": "*" + } + }, + "node_modules/update-notifier": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-4.1.3.tgz", + "integrity": "sha512-Yld6Z0RyCYGB6ckIjffGOSOmHXj1gMeE7aROz4MG+XMkmixBX4jUngrGXNYz7wPKBmtoD4MnBa2Anu7RSKht/A==", + "dependencies": { + "boxen": "^4.2.0", + "chalk": "^3.0.0", + "configstore": "^5.0.1", + "has-yarn": "^2.1.0", + "import-lazy": "^2.1.0", + "is-ci": "^2.0.0", + "is-installed-globally": "^0.3.1", + "is-npm": "^4.0.0", + "is-yarn-global": "^0.3.0", + "latest-version": "^5.0.0", + "pupa": "^2.0.1", + "semver-diff": "^3.1.1", + "xdg-basedir": "^4.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/yeoman/update-notifier?sponsor=1" + } + }, + "node_modules/update-notifier/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/update-notifier/node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/update-notifier/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/update-notifier/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/update-notifier/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/update-notifier/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/upper-case": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", + "integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=" + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "deprecated": "Please see https://github.com/lydell/urix#deprecated" + }, + "node_modules/url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "dependencies": { + "punycode": "1.3.2", + "querystring": "0.2.0" + } + }, + "node_modules/url-loader": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-1.1.2.tgz", + "integrity": "sha512-dXHkKmw8FhPqu8asTc1puBfe3TehOCo2+RmOOev5suNCIYBcT626kxiWg1NBVkwc4rO8BGa7gP70W7VXuqHrjg==", + "dependencies": { + "loader-utils": "^1.1.0", + "mime": "^2.0.3", + "schema-utils": "^1.0.0" + }, + "engines": { + "node": ">= 6.9.0" + }, + "peerDependencies": { + "webpack": "^3.0.0 || ^4.0.0" + } + }, + "node_modules/url-loader/node_modules/schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dependencies": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "node_modules/url-parse-lax": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", + "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", + "dependencies": { + "prepend-http": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/url/node_modules/punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" + }, + "node_modules/url/node_modules/querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/util": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", + "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", + "dependencies": { + "inherits": "2.0.3" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "node_modules/util.promisify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.1.tgz", + "integrity": "sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA==", + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.2", + "has-symbols": "^1.0.1", + "object.getownpropertydescriptors": "^2.1.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/util/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "node_modules/utila": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", + "integrity": "sha1-ihagXURWV6Oupe7MWxKk+lN5dyw=" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/v-runtime-template": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/v-runtime-template/-/v-runtime-template-1.10.0.tgz", + "integrity": "sha512-WLlq9jUepSfUrMEenw3mn7FDXX6hhbl11JjC1OKhwLzifHzVrY5a696TUHDPyj9jke3GGnR7b+2T3od/RL5cww==" + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vendors": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.4.tgz", + "integrity": "sha512-/juG65kTL4Cy2su4P8HjtkTxk6VmJDiOPBufWniqQ6wknac6jNiXS9vU+hO3wgusiyqWlzTbVHi0dyJqRONg3w==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "engines": [ + "node >=0.6.0" + ], + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "node_modules/verror/node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "node_modules/vm-browserify": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", + "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==" + }, + "node_modules/void-elements": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", + "integrity": "sha1-YU9/v42AHwu18GYfWy9XhXUOTwk=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/vue": { + "version": "2.6.14", + "resolved": "https://registry.npmjs.org/vue/-/vue-2.6.14.tgz", + "integrity": "sha512-x2284lgYvjOMj3Za7kqzRcUSxBboHqtgRE2zlos1qWaOye5yUmHn42LB1250NJBLRwEcdrB0JRwyPTEPhfQjiQ==" + }, + "node_modules/vue-hot-reload-api": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/vue-hot-reload-api/-/vue-hot-reload-api-2.3.4.tgz", + "integrity": "sha512-BXq3jwIagosjgNVae6tkHzzIk6a8MHFtzAdwhnV5VlvPTFxDCvIttgSiHWjdGoTJvXtmRu5HacExfdarRcFhog==" + }, + "node_modules/vue-loader": { + "version": "15.9.8", + "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-15.9.8.tgz", + "integrity": "sha512-GwSkxPrihfLR69/dSV3+5CdMQ0D+jXg8Ma1S4nQXKJAznYFX14vHdc/NetQc34Dw+rBbIJyP7JOuVb9Fhprvog==", + "dependencies": { + "@vue/component-compiler-utils": "^3.1.0", + "hash-sum": "^1.0.2", + "loader-utils": "^1.1.0", + "vue-hot-reload-api": "^2.3.0", + "vue-style-loader": "^4.1.0" + }, + "peerDependencies": { + "css-loader": "*", + "webpack": "^3.0.0 || ^4.1.0 || ^5.0.0-0" + }, + "peerDependenciesMeta": { + "cache-loader": { + "optional": true + }, + "vue-template-compiler": { + "optional": true + } + } + }, + "node_modules/vue-router": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.5.3.tgz", + "integrity": "sha512-FUlILrW3DGitS2h+Xaw8aRNvGTwtuaxrRkNSHWTizOfLUie7wuYwezeZ50iflRn8YPV5kxmU2LQuu3nM/b3Zsg==" + }, + "node_modules/vue-server-renderer": { + "version": "2.6.14", + "resolved": "https://registry.npmjs.org/vue-server-renderer/-/vue-server-renderer-2.6.14.tgz", + "integrity": "sha512-HifYRa/LW7cKywg9gd4ZtvtRuBlstQBao5ZCWlg40fyB4OPoGfEXAzxb0emSLv4pBDOHYx0UjpqvxpiQFEuoLA==", + "dependencies": { + "chalk": "^1.1.3", + "hash-sum": "^1.0.2", + "he": "^1.1.0", + "lodash.template": "^4.5.0", + "lodash.uniq": "^4.5.0", + "resolve": "^1.2.0", + "serialize-javascript": "^3.1.0", + "source-map": "0.5.6" + } + }, + "node_modules/vue-server-renderer/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/vue-server-renderer/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/vue-server-renderer/node_modules/serialize-javascript": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-3.1.0.tgz", + "integrity": "sha512-JIJT1DGiWmIKhzRsG91aS6Ze4sFUrYbltlkg2onR5OrnNM02Kl/hnY/T4FN2omvyeBbQmMJv+K4cPOpGzOTFBg==", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/vue-server-renderer/node_modules/source-map": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "integrity": "sha1-dc449SvwczxafwwRjYEzSiu19BI=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/vue-server-renderer/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/vue-style-loader": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-4.1.3.tgz", + "integrity": "sha512-sFuh0xfbtpRlKfm39ss/ikqs9AbKCoXZBpHeVZ8Tx650o0k0q/YCM7FRvigtxpACezfq6af+a7JeqVTWvncqDg==", + "dependencies": { + "hash-sum": "^1.0.2", + "loader-utils": "^1.0.2" + } + }, + "node_modules/vue-template-compiler": { + "version": "2.6.14", + "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.6.14.tgz", + "integrity": "sha512-ODQS1SyMbjKoO1JBJZojSw6FE4qnh9rIpUZn2EUT86FKizx9uH5z6uXiIrm4/Nb/gwxTi/o17ZDEGWAXHvtC7g==", + "dependencies": { + "de-indent": "^1.0.2", + "he": "^1.1.0" + } + }, + "node_modules/vue-template-es2015-compiler": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz", + "integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==" + }, + "node_modules/vuepress": { + "version": "1.9.7", + "resolved": "https://registry.npmjs.org/vuepress/-/vuepress-1.9.7.tgz", + "integrity": "sha512-aSXpoJBGhgjaWUsT1Zs/ZO8JdDWWsxZRlVme/E7QYpn+ZB9iunSgPMozJQNFaHzcRq4kPx5A4k9UhzLRcvtdMg==", + "hasInstallScript": true, + "dependencies": { + "@vuepress/core": "1.9.7", + "@vuepress/theme-default": "1.9.7", + "@vuepress/types": "1.9.7", + "cac": "^6.5.6", + "envinfo": "^7.2.0", + "opencollective-postinstall": "^2.0.2", + "update-notifier": "^4.0.0" + }, + "bin": { + "vuepress": "cli.js" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/vuepress-html-webpack-plugin": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/vuepress-html-webpack-plugin/-/vuepress-html-webpack-plugin-3.2.0.tgz", + "integrity": "sha512-BebAEl1BmWlro3+VyDhIOCY6Gef2MCBllEVAP3NUAtMguiyOwo/dClbwJ167WYmcxHJKLl7b0Chr9H7fpn1d0A==", + "dependencies": { + "html-minifier": "^3.2.3", + "loader-utils": "^0.2.16", + "lodash": "^4.17.3", + "pretty-error": "^2.0.2", + "tapable": "^1.0.0", + "toposort": "^1.0.0", + "util.promisify": "1.0.0" + }, + "engines": { + "node": ">=6.9" + }, + "peerDependencies": { + "webpack": "^1.0.0 || ^2.0.0 || ^3.0.0 || ^4.0.0" + } + }, + "node_modules/vuepress-html-webpack-plugin/node_modules/big.js": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz", + "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==", + "engines": { + "node": "*" + } + }, + "node_modules/vuepress-html-webpack-plugin/node_modules/emojis-list": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", + "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/vuepress-html-webpack-plugin/node_modules/json5": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/vuepress-html-webpack-plugin/node_modules/loader-utils": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz", + "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=", + "dependencies": { + "big.js": "^3.1.3", + "emojis-list": "^2.0.0", + "json5": "^0.5.0", + "object-assign": "^4.0.1" + } + }, + "node_modules/vuepress-html-webpack-plugin/node_modules/util.promisify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz", + "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==", + "dependencies": { + "define-properties": "^1.1.2", + "object.getownpropertydescriptors": "^2.0.3" + } + }, + "node_modules/vuepress-plugin-container": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/vuepress-plugin-container/-/vuepress-plugin-container-2.1.5.tgz", + "integrity": "sha512-TQrDX/v+WHOihj3jpilVnjXu9RcTm6m8tzljNJwYhxnJUW0WWQ0hFLcDTqTBwgKIFdEiSxVOmYE+bJX/sq46MA==", + "dependencies": { + "@vuepress/shared-utils": "^1.2.0", + "markdown-it-container": "^2.0.0" + } + }, + "node_modules/vuepress-plugin-google-tag-manager": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/vuepress-plugin-google-tag-manager/-/vuepress-plugin-google-tag-manager-0.0.5.tgz", + "integrity": "sha512-Hm1GNDdNmc4Vs9c3OMfTtHicB/oZWNCmzMFPdlOObVN1OjizIjImdm+LZIwiVKVndT2TQ4BPhMx7HQkovmD2Lg==" + }, + "node_modules/vuepress-plugin-sitemap": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/vuepress-plugin-sitemap/-/vuepress-plugin-sitemap-2.3.1.tgz", + "integrity": "sha512-n+8lbukhrKrsI9H/EX0EBgkE1pn85LAQFvQ5dIvrZP4Kz6JxPOPPNTQmZMhahQV1tXbLZQCEN7A1WZH4x+arJQ==", + "dependencies": { + "sitemap": "^3.0.0" + }, + "bin": { + "vuepress-sitemap": "cli.js" + }, + "peerDependencies": { + "chalk": "^2.0.0", + "commander": "^2.0.0", + "esm": "^3.0.0" + } + }, + "node_modules/vuepress-plugin-smooth-scroll": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/vuepress-plugin-smooth-scroll/-/vuepress-plugin-smooth-scroll-0.0.3.tgz", + "integrity": "sha512-qsQkDftLVFLe8BiviIHaLV0Ea38YLZKKonDGsNQy1IE0wllFpFIEldWD8frWZtDFdx6b/O3KDMgVQ0qp5NjJCg==", + "dependencies": { + "smoothscroll-polyfill": "^0.4.3" + } + }, + "node_modules/vuepress-theme-cosmos": { + "version": "1.0.183", + "resolved": "https://registry.npmjs.org/vuepress-theme-cosmos/-/vuepress-theme-cosmos-1.0.183.tgz", + "integrity": "sha512-nLSL0YF6ar2yhZkDvp6o313xBSu/Zc3O3OxRsgLMZcKyWanNqyyh0jFrUqMZcjz7vylRRDth6C2/E0YeisFCbw==", + "dependencies": { + "@cosmos-ui/vue": "^0.35.0", + "@vuepress/plugin-google-analytics": "1.8.2", + "algoliasearch": "^4.2.0", + "axios": "^0.24.0", + "cheerio": "^1.0.0-rc.3", + "clipboard-copy": "^4.0.1", + "entities": "3.0.1", + "esm": "^3.2.25", + "gray-matter": "^4.0.2", + "hotkeys-js": "3.8.7", + "jsonp": "^0.2.1", + "markdown-it": "^12.0.0", + "markdown-it-attrs": "^4.0.0", + "prismjs": "^1.22.0", + "pug": "^3.0.1", + "pug-plain-loader": "^1.0.0", + "stylus": "^0.56.0", + "stylus-loader": "^3.0.2", + "tiny-cookie": "^2.3.2", + "v-runtime-template": "^1.10.0", + "vuepress": "^1.5.4", + "vuepress-plugin-google-tag-manager": "0.0.5", + "vuepress-plugin-sitemap": "^2.3.1" + } + }, + "node_modules/watchpack-chokidar2": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz", + "integrity": "sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww==", + "optional": true, + "dependencies": { + "chokidar": "^2.1.8" + } + }, + "node_modules/wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "dependencies": { + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/webidl-conversions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", + "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==" + }, + "node_modules/webpack": { + "version": "4.46.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.46.0.tgz", + "integrity": "sha512-6jJuJjg8znb/xRItk7bkT0+Q7AHCYjjFnvKIWQPkNIOyRqoCGvkOs0ipeQzrqz4l5FtN5ZI/ukEHroeX/o1/5Q==", + "dependencies": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-module-context": "1.9.0", + "@webassemblyjs/wasm-edit": "1.9.0", + "@webassemblyjs/wasm-parser": "1.9.0", + "acorn": "^6.4.1", + "ajv": "^6.10.2", + "ajv-keywords": "^3.4.1", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^4.5.0", + "eslint-scope": "^4.0.3", + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^2.4.0", + "loader-utils": "^1.2.3", + "memory-fs": "^0.4.1", + "micromatch": "^3.1.10", + "mkdirp": "^0.5.3", + "neo-async": "^2.6.1", + "node-libs-browser": "^2.2.1", + "schema-utils": "^1.0.0", + "tapable": "^1.1.3", + "terser-webpack-plugin": "^1.4.3", + "watchpack": "^1.7.4", + "webpack-sources": "^1.4.1" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=6.11.5" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + }, + "webpack-command": { + "optional": true + } + } + }, + "node_modules/webpack-chain": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/webpack-chain/-/webpack-chain-6.5.1.tgz", + "integrity": "sha512-7doO/SRtLu8q5WM0s7vPKPWX580qhi0/yBHkOxNkv50f6qB76Zy9o2wRTrrPULqYTvQlVHuvbA8v+G5ayuUDsA==", + "dependencies": { + "deepmerge": "^1.5.2", + "javascript-stringify": "^2.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/webpack-chain/node_modules/javascript-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/javascript-stringify/-/javascript-stringify-2.1.0.tgz", + "integrity": "sha512-JVAfqNPTvNq3sB/VHQJAFxN/sPgKnsKrCwyRt15zwNCdrMMJDdcEOdubuy+DuJYYdm0ox1J4uzEuYKkN+9yhVg==" + }, + "node_modules/webpack-dev-middleware": { + "version": "3.7.3", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.7.3.tgz", + "integrity": "sha512-djelc/zGiz9nZj/U7PTBi2ViorGJXEWo/3ltkPbDyxCXhhEXkW0ce99falaok4TPj+AsxLiXJR0EBOb0zh9fKQ==", + "dependencies": { + "memory-fs": "^0.4.1", + "mime": "^2.4.4", + "mkdirp": "^0.5.1", + "range-parser": "^1.2.1", + "webpack-log": "^2.0.0" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/webpack-dev-server": { + "version": "3.11.3", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.11.3.tgz", + "integrity": "sha512-3x31rjbEQWKMNzacUZRE6wXvUFuGpH7vr0lIEbYpMAG9BOxi0928QU1BBswOAP3kg3H1O4hiS+sq4YyAn6ANnA==", + "dependencies": { + "ansi-html-community": "0.0.8", + "bonjour": "^3.5.0", + "chokidar": "^2.1.8", + "compression": "^1.7.4", + "connect-history-api-fallback": "^1.6.0", + "debug": "^4.1.1", + "del": "^4.1.1", + "express": "^4.17.1", + "html-entities": "^1.3.1", + "http-proxy-middleware": "0.19.1", + "import-local": "^2.0.0", + "internal-ip": "^4.3.0", + "ip": "^1.1.5", + "is-absolute-url": "^3.0.3", + "killable": "^1.0.1", + "loglevel": "^1.6.8", + "opn": "^5.5.0", + "p-retry": "^3.0.1", + "portfinder": "^1.0.26", + "schema-utils": "^1.0.0", + "selfsigned": "^1.10.8", + "semver": "^6.3.0", + "serve-index": "^1.9.1", + "sockjs": "^0.3.21", + "sockjs-client": "^1.5.0", + "spdy": "^4.0.2", + "strip-ansi": "^3.0.1", + "supports-color": "^6.1.0", + "url": "^0.11.0", + "webpack-dev-middleware": "^3.7.2", + "webpack-log": "^2.0.0", + "ws": "^6.2.1", + "yargs": "^13.3.2" + }, + "bin": { + "webpack-dev-server": "bin/webpack-dev-server.js" + }, + "engines": { + "node": ">= 6.11.5" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-dev-server/node_modules/debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/webpack-dev-server/node_modules/http-proxy-middleware": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz", + "integrity": "sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q==", + "dependencies": { + "http-proxy": "^1.17.0", + "is-glob": "^4.0.0", + "lodash": "^4.17.11", + "micromatch": "^3.1.10" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/webpack-dev-server/node_modules/is-absolute-url": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz", + "integrity": "sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==", + "engines": { + "node": ">=8" + } + }, + "node_modules/webpack-dev-server/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/webpack-dev-server/node_modules/schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dependencies": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/webpack-dev-server/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-log": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-2.0.0.tgz", + "integrity": "sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg==", + "dependencies": { + "ansi-colors": "^3.0.0", + "uuid": "^3.3.2" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/webpack-merge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-4.2.2.tgz", + "integrity": "sha512-TUE1UGoTX2Cd42j3krGYqObZbOD+xF7u28WB7tfUordytSjbWTIjK/8V0amkBfTYN4/pB/GIDlJZZ657BGG19g==", + "dependencies": { + "lodash": "^4.17.15" + } + }, + "node_modules/webpack-sources": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", + "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", + "dependencies": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + } + }, + "node_modules/webpack-sources/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack/node_modules/acorn": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", + "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/webpack/node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/webpack/node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "optional": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/webpack/node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "optional": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/webpack/node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "optional": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/webpack/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/webpack/node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "optional": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/webpack/node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "optional": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/webpack/node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "optional": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/webpack/node_modules/schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dependencies": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/webpack/node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "optional": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/webpack/node_modules/watchpack": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.5.tgz", + "integrity": "sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ==", + "dependencies": { + "graceful-fs": "^4.1.2", + "neo-async": "^2.5.0" + }, + "optionalDependencies": { + "chokidar": "^3.4.1", + "watchpack-chokidar2": "^2.0.1" + } + }, + "node_modules/webpackbar": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/webpackbar/-/webpackbar-3.2.0.tgz", + "integrity": "sha512-PC4o+1c8gWWileUfwabe0gqptlXUDJd5E0zbpr2xHP1VSOVlZVPBZ8j6NCR8zM5zbKdxPhctHXahgpNK1qFDPw==", + "dependencies": { + "ansi-escapes": "^4.1.0", + "chalk": "^2.4.1", + "consola": "^2.6.0", + "figures": "^3.0.0", + "pretty-time": "^1.1.0", + "std-env": "^2.2.1", + "text-table": "^0.2.0", + "wrap-ansi": "^5.1.0" + }, + "engines": { + "node": ">= 6.9.0" + }, + "peerDependencies": { + "webpack": "^3.0.0 || ^4.0.0" + } + }, + "node_modules/websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "dependencies": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/whatwg-url": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", + "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", + "dependencies": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" + } + }, + "node_modules/when": { + "version": "3.6.4", + "resolved": "https://registry.npmjs.org/when/-/when-3.6.4.tgz", + "integrity": "sha1-RztRfsFZ4rhQBUl6E5g/CVQS404=" + }, + "node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" + }, + "node_modules/widest-line": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", + "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", + "dependencies": { + "string-width": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/widest-line/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/widest-line/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/widest-line/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/widest-line/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/widest-line/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/with": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/with/-/with-7.0.2.tgz", + "integrity": "sha512-RNGKj82nUPg3g5ygxkQl0R937xLyho1J24ItRCBTr/m1YnZkzJy1hUiHUJrc/VlsDQzsCnInEGSg3bci0Lmd4w==", + "dependencies": { + "@babel/parser": "^7.9.6", + "@babel/types": "^7.9.6", + "assert-never": "^1.2.1", + "babel-walk": "3.0.0-canary-5" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/worker-farm": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz", + "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==", + "dependencies": { + "errno": "~0.1.7" + } + }, + "node_modules/wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dependencies": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "node_modules/write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dependencies": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "node_modules/ws": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz", + "integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==", + "dependencies": { + "async-limiter": "~1.0.0" + } + }, + "node_modules/xdg-basedir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", + "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", + "engines": { + "node": ">=8" + } + }, + "node_modules/xmlbuilder": { + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-13.0.2.tgz", + "integrity": "sha512-Eux0i2QdDYKbdbA6AM6xE4m6ZTZr4G4xF9kahI2ukSEMCzwce2eX9WlTI5J3s+NU7hpasFsr8hWIONae7LluAQ==", + "engines": { + "node": ">=6.0" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==" + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + }, + "node_modules/yargs": { + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "dependencies": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.2" + } + }, + "node_modules/yargs-parser": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + }, + "node_modules/yargs-parser/node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/yargs/node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/yargs/node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/yargs/node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/yargs/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "engines": { + "node": ">=4" + } + }, + "node_modules/zepto": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/zepto/-/zepto-1.2.0.tgz", + "integrity": "sha1-4Se9nmb9hGvl6rSME5SIL3wOT5g=" + } + }, "dependencies": { "@algolia/cache-browser-local-storage": { "version": "4.12.0", @@ -2088,12 +16359,14 @@ "ajv-errors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz", - "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==" + "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==", + "requires": {} }, "ajv-keywords": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==" + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "requires": {} }, "algoliasearch": { "version": "4.12.0", @@ -2326,9 +16599,9 @@ "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=" }, "async": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", - "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", "requires": { "lodash": "^4.17.14" } @@ -2886,7 +17159,8 @@ "bundle-require": { "version": "2.1.8", "resolved": "https://registry.npmjs.org/bundle-require/-/bundle-require-2.1.8.tgz", - "integrity": "sha512-oOEg3A0hy/YzvNWNowtKD0pmhZKseOFweCbgyMqTIih4gRY1nJWsvrOCT27L9NbIyL5jMjTFrAUpGxxpW68Puw==" + "integrity": "sha512-oOEg3A0hy/YzvNWNowtKD0pmhZKseOFweCbgyMqTIih4gRY1nJWsvrOCT27L9NbIyL5jMjTFrAUpGxxpW68Puw==", + "requires": {} }, "bytes": { "version": "3.0.0", @@ -4825,14 +19099,6 @@ "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==" }, - "eventsource": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.1.0.tgz", - "integrity": "sha512-VSJjT5oCNrFvCS6igjzPAt5hBzQ2qPBFIbJ03zLI9SE0mxwZpMw6BfJrbFHm1a141AavMEB8JHmBhWAd66PfCg==", - "requires": { - "original": "^1.0.0" - } - }, "evp_bytestokey": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", @@ -5347,6 +19613,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "optional": true, "requires": { "is-glob": "^4.0.1" } @@ -6673,12 +20940,14 @@ "markdown-it-anchor": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-5.3.0.tgz", - "integrity": "sha512-/V1MnLL/rgJ3jkMWo84UR+K+jF1cxNG1a+KwqeXqTIJ+jtA8aWSHuigx8lTzauiIjBDbwF3NcWQMotd0Dm39jA==" + "integrity": "sha512-/V1MnLL/rgJ3jkMWo84UR+K+jF1cxNG1a+KwqeXqTIJ+jtA8aWSHuigx8lTzauiIjBDbwF3NcWQMotd0Dm39jA==", + "requires": {} }, "markdown-it-attrs": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/markdown-it-attrs/-/markdown-it-attrs-4.1.3.tgz", - "integrity": "sha512-d5yg/lzQV2KFI/4LPsZQB3uxQrf0/l2/RnMPCPm4lYLOZUSmFlpPccyojnzaHkfQpAD8wBHfnfUW0aMhpKOS2g==" + "integrity": "sha512-d5yg/lzQV2KFI/4LPsZQB3uxQrf0/l2/RnMPCPm4lYLOZUSmFlpPccyojnzaHkfQpAD8wBHfnfUW0aMhpKOS2g==", + "requires": {} }, "markdown-it-chain": { "version": "1.3.0", @@ -6906,9 +21175,9 @@ } }, "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", + "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==" }, "mississippi": { "version": "3.0.0", @@ -7305,14 +21574,6 @@ "last-call-webpack-plugin": "^3.0.0" } }, - "original": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz", - "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==", - "requires": { - "url-parse": "^1.4.3" - } - }, "os-browserify": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", @@ -8143,9 +22404,9 @@ "integrity": "sha512-28iF6xPQrP8Oa6uxE6a1biz+lWeTOAPKggvjB8HAs6nVMKZwf5bG++632Dx614hIWgUPkgivRfG+a8uAXGTIbA==" }, "prismjs": { - "version": "1.26.0", - "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.26.0.tgz", - "integrity": "sha512-HUoH9C5Z3jKkl3UunCyiD5jwk0+Hz0fIgQ2nbwU2Oo/ceuTAQAg+pPVnfdt2TJWRVLcxKh9iuoYDUSc8clb5UQ==" + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz", + "integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==" }, "process": { "version": "0.11.10", @@ -9192,6 +23453,11 @@ "ms": "^2.1.1" } }, + "eventsource": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.1.2.tgz", + "integrity": "sha512-xAH3zWhgO2/3KIniEKYPr8plNSzlGINOUqYj0m0u7AB81iRw8b/3E73W6AuU+6klLbaSFmZnaETQ2lXPfAydrA==" + }, "ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -9467,6 +23733,14 @@ "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=" }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + }, "string-width": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", @@ -9510,14 +23784,6 @@ "define-properties": "^1.1.3" } }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - }, "strip-ansi": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", @@ -9711,9 +23977,9 @@ "integrity": "sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==" }, "terser": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz", - "integrity": "sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==", + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.1.tgz", + "integrity": "sha512-4GnLC0x667eJG0ewJTa6z/yXrbLGv80D9Ru6HIpCQmO+Q4PfEtBFi0ObSckqwL6VyQv/7ENJieXHo2ANmdQwgw==", "requires": { "commander": "^2.20.0", "source-map": "~0.6.1", @@ -10322,9 +24588,9 @@ } }, "url-parse": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.4.tgz", - "integrity": "sha512-ITeAByWWoqutFClc/lRZnFplgXgEZr3WJ6XngMM/N9DMIm4K8zXPCZ1Jdu0rERwO84w1WC5wkle2ubwTA4NTBg==", + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", "requires": { "querystringify": "^2.1.1", "requires-port": "^1.0.0" @@ -10658,24 +24924,6 @@ "vuepress-plugin-sitemap": "^2.3.1" } }, - "watchpack": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.3.1.tgz", - "integrity": "sha512-x0t0JuydIo8qCNctdDrn1OzH/qDzk2+rdCOC3YzumZ42fiMqmQ7T3xQurykYMhYfHaPHTp4ZxAx2NfUo1K6QaA==", - "dev": true, - "requires": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" - }, - "dependencies": { - "glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "dev": true - } - } - }, "watchpack-chokidar2": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz", diff --git a/docs/package.json b/docs/package.json index 500868b7237..89dfa64033f 100644 --- a/docs/package.json +++ b/docs/package.json @@ -13,11 +13,6 @@ "author": "", "license": "ISC", "dependencies": { - "glob-parent": "^5.1.2", - "vue": "^2.6.14", "vuepress-theme-cosmos": "^1.0.183" - }, - "devDependencies": { - "watchpack": "^2.2.0" } } diff --git a/docs/apps/interchain-accounts/requirements.md b/docs/requirements/ics27-requirements.md similarity index 88% rename from docs/apps/interchain-accounts/requirements.md rename to docs/requirements/ics27-requirements.md index fff919f8088..ed9d274b518 100644 --- a/docs/apps/interchain-accounts/requirements.md +++ b/docs/requirements/ics27-requirements.md @@ -55,65 +55,64 @@ Using Interchain Accounts, a service could be built in which a user sends tokens | ID | Description | Verification | Status | | --- | ----------- | ------------ | ------ | -| 1.01 | A chain shall have the ability to enable or disable Interchain Accounts controller functionality in the genesis state. | The controller parameters have a [flag](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/host/types/host.pb.go#L30) to enable/disable the controller submodule, and this flag [is stored during genesis initialization](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/controller/keeper/params.go#L24). | `Implemented` | -| 1.02 | A chain shall have the ability to export the Interchain Accounts controller genesis state. | [Acceptance tests](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/controller/keeper/genesis_test.go#L47) | `Implemented` | -| 1.03 | A chain shall have the ability to initialize the Interchain Accounts controller genesis state. | [Acceptance tests](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/controller/keeper/genesis_test.go#L10) | `Implemented` | -| 1.04 | A chain shall have the ability to set the Interchain Accounts controller parameters when upgrading or via proposal. | [Acceptance tests](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/module_test.go#L33) | `Implemented` | -| 1.05 | A chain shall have the ability to enable or disable Interchain Accounts host functionality in the genesis state. | The host parameters have a [flag](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/host/types/host.pb.go#L30) to enable/disable the host submodule, and this flag [is stored during genesis initialization](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/host/keeper/params.go#L31) | `Implemented` | -| 1.06 | A chain shall have the ability to export the Interchain Accounts host genesis state. | [Acceptance tests](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/host/keeper/genesis_test.go#L46) | `Implemented` | -| 1.07 | A chain shall have the ability to initialize the Interchain Accounts host genesis state. | [Acceptance tests](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/host/keeper/genesis_test.go#L10) | `Implemented` | -| 1.08 | A chain shall have the ability to set the Interchain Accounts host parameters when upgrading or via proposal. | [Acceptance tests](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/module_test.go#L33) | `Implemented` | -| 1.09 | The host chain shall have the ability to whitelist what types of messages or transactions that it chooses to facilitate (e.g. it can decide that registered interchain accounts cannot execute staking messages). | [Acceptance tests](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/host/keeper/params_test.go#L5) | `Implemented` | +| 1.01 | A chain shall have the ability to enable or disable Interchain Accounts controller functionality in the genesis state. | The controller parameters have a [flag](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/host/types/host.pb.go#L30) to enable/disable the controller submodule, and this flag [is stored during genesis initialization](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/controller/keeper/params.go#L24). | `Verified` | +| 1.02 | A chain shall have the ability to export the Interchain Accounts controller genesis state. | [Acceptance tests](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/controller/keeper/genesis_test.go#L47) | `Verified` | +| 1.03 | A chain shall have the ability to initialize the Interchain Accounts controller genesis state. | [Acceptance tests](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/controller/keeper/genesis_test.go#L10) | `Verified` | +| 1.04 | A chain shall have the ability to set the Interchain Accounts controller parameters when upgrading or via proposal. | [Acceptance tests](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/module_test.go#L33) | `Verified` | +| 1.05 | A chain shall have the ability to enable or disable Interchain Accounts host functionality in the genesis state. | The host parameters have a [flag](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/host/types/host.pb.go#L30) to enable/disable the host submodule, and this flag [is stored during genesis initialization](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/host/keeper/params.go#L31) | `Verified` | +| 1.06 | A chain shall have the ability to export the Interchain Accounts host genesis state. | [Acceptance tests](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/host/keeper/genesis_test.go#L46) | `Verified` | +| 1.07 | A chain shall have the ability to initialize the Interchain Accounts host genesis state. | [Acceptance tests](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/host/keeper/genesis_test.go#L10) | `Verified` | +| 1.08 | A chain shall have the ability to set the Interchain Accounts host parameters when upgrading or via proposal. | [Acceptance tests](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/module_test.go#L33) | `Verified` | +| 1.09 | The host chain shall have the ability to whitelist what types of messages or transactions that it chooses to facilitate (e.g. it can decide that registered interchain accounts cannot execute staking messages). | [Acceptance tests](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/host/keeper/params_test.go#L5) | `Verified` | ### 2 - Registration | ID | Description | Verification | Status | | --- | ----------- | ------------ | ------ | -| 2.01 | The controller chain can programmatically create interchain accounts on the host chain that shall be controlled only by the owner account on the controller chain. | [Acceptance tests](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/host/keeper/account_test.go#L10) | `Implemented` | -| 2.02 | An interchain account shall be created by any actor without the approval of a third party (e.g. chain governance). | [Acceptance tests](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/host/keeper/account_test.go#L10) | `Implemented` | +| 2.01 | The controller chain can programmatically create interchain accounts on the host chain that shall be controlled only by the owner account on the controller chain. | [Acceptance tests](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/host/keeper/account_test.go#L10) | `Verified` | +| 2.02 | An interchain account shall be created by any actor without the approval of a third party (e.g. chain governance). | [Acceptance tests](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/host/keeper/account_test.go#L10) | `Verified` | ### 3 - Control -| ID | Description | Verification | Status | -| --- | ----------- | ------------ | ------ | -| 3.01 | The controller chain can programmatically control the interchain account by submitting transactions to be executed on the host chain on the behalf of the interchain account. | [Acceptance tests](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/controller/keeper/relay_test.go#L29) | `Implemented` | -| 3.02 | Under no circumstances shall the owner account on the controller chain irretrievably lose control over the registered interchain account on the host chain. | If the channel between controller and host closes, then [a relayer can open a new channel on the existing controller port](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/controller/keeper/account.go#L16-L17). | `Implemented` | +| ID | Description | Verification | Status | +| --- | ----------- | ------------ | ------ | +| 3.01 | The controller chain can programmatically control the interchain account by submitting transactions to be executed on the host chain on the behalf of the interchain account. | [Acceptance tests](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/controller/keeper/relay_test.go#L29) | `Verified` | +| 3.02 | Under no circumstances shall the owner account on the controller chain irretrievably lose control over the registered interchain account on the host chain. | If the channel between controller and host closes, then [a relayer can open a new channel on the existing controller port](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/controller/keeper/account.go#L16-L17). | `Verified` | ### 4 - Host execution -| ID | Description | Verification | Status | -| --- | ----------- | ------------ | ------ | -| 4.01 | Transactions shall be executed by an interchain account on the host chain in exactly the same order in which they are submitted by the controller chain. | IBC packets with SDK messages will be sent from the controller to the host over an [ordered channel](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/controller/keeper/account.go#L60). | `Implemented` | -| 4.02 | The host shall execute only messages in the allow list. | [Acceptance tests](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/host/keeper/relay_test.go#L340) | `Implemented` | -| 4.03 | The controller chain shall know how the host chain will handle the transaction bytes in advance. | [Acceptance tests](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/controller/keeper/handshake_test.go#L109-L133) | `Implemented` | -| 4.04 | Each transaction submitted by the controller chain shall be executed only once by the interchain account on the host chain. | [Acceptance tests](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/host/keeper/relay_test.go#L248) | `Implemented` | +| ID | Description | Verification | Status | +| --- | ----------- | ------------ | ------ | +| 4.01 | Transactions shall be executed by an interchain account on the host chain in exactly the same order in which they are submitted by the controller chain. | IBC packets with SDK messages will be sent from the controller to the host over an [ordered channel](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/controller/keeper/account.go#L60). | `Verified` | +| 4.02 | The host shall execute only messages in the allow list. | [Acceptance tests](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/host/keeper/relay_test.go#L340) | `Verified` | +| 4.03 | The controller chain shall know how the host chain will handle the transaction bytes in advance. | [Acceptance tests](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/controller/keeper/handshake_test.go#L109-L133) | `Verified` | +| 4.04 | Each transaction submitted by the controller chain shall be executed only once by the interchain account on the host chain. | [Acceptance tests](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/host/keeper/relay_test.go#L248) | `Verified` | # Non-functional requirements ## 5 - Security | ID | Description | Verification | Status | -| -- | ----------- | ------------ | ------ | -| 5.01 | There shall be no means for the interchain account to execute transactions that have not been submitted first by the respective owner account on the controller chain. |[Acceptance tests](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/host/keeper/relay_test.go#L361) | `Implemented` | -| 5.02 | Every interchain account on the host chain shall have one and only one respective owner account on the controller chain. | The interchain account on the host [is generated using the host connection ID and the address of the owner on the controller](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/host/keeper/handshake.go#L73-L76). | `Implemented` | -| 5.03 | The owner account on a controller chain shall not be able to control interchain accounts registered by other owner accounts on the same controller chain. | Before the host logic executes the received messages, it [retrieves the interchain account associated with the port ID](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/host/keeper/relay.go#L94) over which it received the message. For owner address B to be able to execute a message with the interchain account registered with owner address A, it would need to send the messages over a channel that binds to a port ID that contains the owner address A, and since we have assumption number 3, this should not be allowed by applications. | `Implemented` | -| 5.04 | A controller chain shall not be able to control interchain accounts registered by owner accounts on different controller chains. | Same as 5.03. | `Implemented` | | -| 5.05 | Each interchain account on the host chain is owned by a single owner account on the controller chain. It shall not be possible to register a second interchain account with the same owner account on the controller chain. | [Acceptance tests](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/controller/keeper/account_test.go#L42) | `Implemented` | +| -- | ----------- | ------------ | ------ | +| 5.01 | There shall be no means for the interchain account to execute transactions that have not been submitted first by the respective owner account on the controller chain. |[Acceptance tests](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/host/keeper/relay_test.go#L361) | `Verified` | +| 5.02 | Every interchain account on the host chain shall have one and only one respective owner account on the controller chain. | The interchain account on the host [is generated using the host connection ID and the address of the owner on the controller](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/host/keeper/handshake.go#L73-L76). | `Verified` | +| 5.03 | The owner account on a controller chain shall not be able to control interchain accounts registered by other owner accounts on the same controller chain. | Before the host logic executes the received messages, it [retrieves the interchain account associated with the port ID](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/host/keeper/relay.go#L94) over which it received the message. For owner address B to be able to execute a message with the interchain account registered with owner address A, it would need to send the messages over a channel that binds to a port ID that contains the owner address A, and since we have assumption number 3, this should not be allowed by applications. | `Verified` | +| 5.04 | A controller chain shall not be able to control interchain accounts registered by owner accounts on different controller chains. | Same as 5.03. | `Verified` | | +| 5.05 | Each interchain account on the host chain is owned by a single owner account on the controller chain. It shall not be possible to register a second interchain account with the same owner account on the controller chain. | [Acceptance tests](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/controller/keeper/account_test.go#L42) | `Verified` | # External interface requirements ## 6 - CLI -| ID | Description | Verification | Status | -| -- | ----------- | ------------ | ------ | -| 6.01 | There shall be a CLI command available to query the host parameters. | [Acceptance tests](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/host/client/cli/query.go#L22) | `Implemented` | -| 6.02 | There shall be a CLI command available to query the receive packet events on the host chain to check the result of the execution of the message on the host. | [Acceptance tests](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/host/client/cli/query.go#L51) | `Implemented` | -| 6.03 | There shall be a CLI command available to query the controller parameters. | [Acceptance tests](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/controller/client/cli/query.go#L15) | `Implemented` | - +| ID | Description | Verification | Status | +| -- | ----------- | ------------ | ------ | +| 6.01 | There shall be a CLI command available to query the host parameters. | [Acceptance tests](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/host/client/cli/query.go#L22) | `Verified` | +| 6.02 | There shall be a CLI command available to query the receive packet events on the host chain to check the result of the execution of the message on the host. | [Acceptance tests](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/host/client/cli/query.go#L51) | `Verified` | +| 6.03 | There shall be a CLI command available to query the controller parameters. | [Acceptance tests](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/controller/client/cli/query.go#L15) | `Verified` | ## 7 - Application developers -| ID | Description | Verification | Status | +| ID | Description | Verification | Status | | -- | ----------- | ------------ | ------ | -| 7.01 | An IBC application developer shall be able to develop an Interchain Accounts authentication module that can register interchain accounts. | The [`RegisterInterchainAccount` function](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/controller/keeper/account.go#L18) is the entry point to registering an interchain account. | `Implemented` | -| 7.02 | An IBC application developer shall be able to develop an Interchain Accounts authentication module that can send messages from the controller to the host. | The [`SendTx` function](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/controller/keeper/relay.go#L18) takes pre-built packet data containing messages to be executed on the host chain from an authentication module and attempts to send the packet. | `Implemented` | \ No newline at end of file +| 7.01 | An IBC application developer shall be able to develop an Interchain Accounts authentication module that can register interchain accounts. | The [`RegisterInterchainAccount` function](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/controller/keeper/account.go#L18) is the entry point to registering an interchain account. | `Verified` | +| 7.02 | An IBC application developer shall be able to develop an Interchain Accounts authentication module that can send messages from the controller to the host. | The [`SendTx` function](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/controller/keeper/relay.go#L18) takes pre-built packet data containing messages to be executed on the host chain from an authentication module and attempts to send the packet. | `Verified` | diff --git a/docs/requirements/ics29-v1-requirements.md b/docs/requirements/ics29-v1-requirements.md new file mode 100644 index 00000000000..65c76dc4cc9 --- /dev/null +++ b/docs/requirements/ics29-v1-requirements.md @@ -0,0 +1,177 @@ +# Business requirements + +> **TL;DR**: There is no general incentivization mechanism to reward relayers for their service ensuring Interchain liveness. + +## Problem + +The Interchain dream will never scale unless there is a clear way to incentivize relayers. An interface that can be easily adopted by any applications is needed, without precluding chains that do not use tokens. + +Though IBC does not depend on relayer operators for transaction verification, 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 incentivization mechanism for relayers. + +## Objectives + +Provide a general fee payment design that can be adopted by any ICS application protocol as middleware. + +## Scope + +| Features | Release | +| --------- | ------- | +| Incentivize timely delivery of a packet (successfully submit `MsgRecvPacket`). | v1 | +| Incentivize relaying acknowledgement for a packet (successfully submit `MsgAcknowledgement`). | v1 | +| Incentivize relaying timeout for a packet when the timeout has expired before packet is delivered (successfully submit `MsgTimeout`). | v1 | +| Enable permissionless or permissioned relaying. | v1 | +| Allow opt-in for chains (an application with fee support on chain A could connect to the couterparty application without fee support on chain B). | v1 | + +# User requirements + +## Use cases + +- A packet already sent may be incentivized for one or more of the messages `MsgRecvPacket`, `MsgAcknowledgement`, `MsgTimeout`. +- The next packet to be sent may be incentivized for one or more of the messages `MsgRecvPacket`, `MsgAcknowledgement`, `MsgTimeout`. + +# Functional requirements + +## Assumptions + +1. Relayer addresses should not be forgeable. + +## Known limitations + +1. Without channel upgradability it is not possible to use fee incentivization on an existing channel. + +## Terminology + +See section [Definitions](https://github.com/cosmos/ibc/tree/main/spec/app/ics-029-fee-payment#definitions) in ICS 29 spec. + +## Features + +### 1 - Configuration + +| ID | Description | Verification | Status | +| --- | ----------- | ------------ | ------ | +| 1.02 | A chain shall have the ability to export the fee middleware genesis state. | [Acceptance test](https://github.com/cosmos/ibc-go/blob/v4.0.0/modules/apps/29-fee/keeper/genesis_test.go#L69) | `Verified` | +| 1.03 | A chain shall have the ability to initialize the fee middleware genesis state. | [Acceptance test](https://github.com/cosmos/ibc-go/blob/v4.0.0/modules/apps/29-fee/keeper/genesis_test.go#L9) | `Verified` | + +### 2 - Payee registration + +| ID | Description | Verification | Status | +| --- | ----------- | ------------ | ------ | +| 2.01 | A relayer shall have the ability to register for a channel an optional payee address to which fees shall be distributed for successful relaying `MsgAcknowledgement` and `MsgTimeout`. | [Acceptance test](https://github.com/cosmos/ibc-go/blob/v4.0.0/modules/apps/29-fee/keeper/msg_server_test.go#L23) | `Verified` | +| 2.02 | The payee address shall only be registered if the channel exist. | [Acceptance test](https://github.com/cosmos/ibc-go/blob/v4.0.0/modules/apps/29-fee/keeper/msg_server_test.go#L28) | `Verified` | +| 2.03 | The payee address shall only be registered if the channel is fee enabled. | [Acceptance test](https://github.com/cosmos/ibc-go/blob/v4.0.0/modules/apps/29-fee/keeper/msg_server_test.go#L35) | `Verified` | +| 2.04 | The payee address shall only be registered if the address is a valid `Bech32` address. | [Acceptance test](https://github.com/cosmos/ibc-go/blob/v4.0.0/modules/apps/29-fee/keeper/msg_server_test.go#L42) | `Verified` | +| 2.05 | The payee address shall only be registered if the address is not blocked. | [Acceptance test](https://github.com/cosmos/ibc-go/blob/v4.0.0/modules/apps/29-fee/keeper/msg_server_test.go#L49) | `Verified` | + +### 3 - Counterparty payee registration + +| ID | Description | Verification | Status | +| --- | ----------- | ------------ | ------ | +| 3.01 | A relayer shall have the ability to register a counterparty payee to which fees shall be distributed for successful relaying of `MsgRecvPacket`. | [Acceptance test](https://github.com/cosmos/ibc-go/blob/v4.0.0/modules/apps/29-fee/keeper/msg_server_test.go#L102) | `Verified` | +| 3.02 | The counterparty payee address shall only be registered if the channel exists. | [Acceptance test](https://github.com/cosmos/ibc-go/blob/v4.0.0/modules/apps/29-fee/keeper/msg_server_test.go#L115) | `Verified` | +| 3.03 | The counterparty payee address shall only be registered if the channel is fee enabled. | [Acceptance test](https://github.com/cosmos/ibc-go/blob/v4.0.0/modules/apps/29-fee/keeper/msg_server_test.go#L122) | `Verified` | +| 3.04 | The counterparty payee address may be an arbitrary string (since the counterparty chain may not be Cosmos-SDK based). | [Acceptance test](https://github.com/cosmos/ibc-go/blob/v4.0.0/modules/apps/29-fee/keeper/msg_server_test.go#L42) | `Verified` | + +### 4 - Fee incentivization + +#### Synchronously + +| ID | Description | Verification | Status | +| --- | ----------- | ------------ | ------ | +| 4.01 | If a channel is fee-enabled, the next sequence packet may be incentivized. | [Acceptance test](https://github.com/cosmos/ibc-go/blob/v4.0.0/modules/apps/29-fee/keeper/msg_server_test.go#L178) | `Verified` | +| 4.02 | If the fee module is not locked, the next sequence packet may be incentivized. | [Acceptance test](https://github.com/cosmos/ibc-go/blob/v4.0.0/modules/apps/29-fee/keeper/msg_server_test.go#L210) | `Verified` | +| 4.03 | The next sequence packet may be incentivized for some or all of `MsgRecvPacket`, `MsgAcknowledgement`, `MsgTimeout`. | Fees for any of the messages may be zero, but [not all of them](https://github.com/cosmos/ibc-go/blob/v4.0.0/modules/apps/29-fee/types/fee.go#L87). | `Verified` | +| 4.04 | A blocked account address shall not be allowed to incentivize the next sequence packet. | [Acceptance test](https://github.com/cosmos/ibc-go/blob/v4.0.0/modules/apps/29-fee/keeper/msg_server_test.go#L239) | `Verified` | +| 4.05 | A non-valid `Bech32` account address shall not be allowed to incentivize the next sequence packet. | [Acceptance test](https://github.com/cosmos/ibc-go/blob/v4.0.0/modules/apps/29-fee/keeper/msg_server_test.go#L225) | `Verified` | +| 4.06 | The next sequence packet may be incentivized by multiple account addresses. | [Acceptance test](https://github.com/cosmos/ibc-go/blob/v4.0.0/modules/apps/29-fee/keeper/msg_server_test.go#L183) | `Verified` | +| 4.07 | The next sequence packet shall only be incentivized with valid coin denominations. | [Acceptance test](https://github.com/cosmos/ibc-go/blob/v4.0.0/modules/apps/29-fee/keeper/msg_server_test.go#L246-L266) | `Verified` | + +#### Aynchronously + +| ID | Description | Verification | Status | +| --- | ----------- | ------------ | ------ | +| 4.08 | Only packets that have been sent may be incentivized. | [Acceptance test](https://github.com/cosmos/ibc-go/blob/v4.0.0/modules/apps/29-fee/keeper/msg_server_test.go#L376) | `Verified` | +| 4.09 | A packet sent may be incentivized for some or all of `MsgRecvPacket`, `MsgAcknowledgement`, `MsgTimeout`. | Fees for any of the messages may be zero, but [not all of them](https://github.com/cosmos/ibc-go/blob/v4.0.0/modules/apps/29-fee/types/fee.go#L87). | `Verified` | +| 4.10 | Only packets that have not gone through the packet life cycle (i.e. have not been acknowledged or timed out yet) may be incentivized. | [Acceptance test](https://github.com/cosmos/ibc-go/blob/v4.0.0/modules/apps/29-fee/keeper/msg_server_test.go#L382-L410) | `Verified` | +| 4.11 | If a channel exists, a packet sent may be incentivized. | [Acceptance test](https://github.com/cosmos/ibc-go/blob/v4.0.0/modules/apps/29-fee/keeper/msg_server_test.go#L365) | +| 4.12 | If a channel is fee-enabled, a packet sent may be incentivized. | [Acceptance test](https://github.com/cosmos/ibc-go/blob/v4.0.0/modules/apps/29-fee/keeper/msg_server_test.go#L357) | `Verified` | +| 4.13 | If the fee module is not locked, a packet sent may be incentivized. | [Acceptance test](https://github.com/cosmos/ibc-go/blob/v4.0.0/modules/apps/29-fee/keeper/msg_server_test.go#L350) | `Verified` | +| 4.14 | A non-valid `Bech32` account address shall not be allowed to incentivize a packet sent. | [Acceptance test](https://github.com/cosmos/ibc-go/blob/v4.0.0/modules/apps/29-fee/keeper/msg_server_test.go#L412) | `Verified` | +| 4.15 | An account that does not exist shall not be allowed to incentivize a packet sent. | [Acceptance test](https://github.com/cosmos/ibc-go/blob/v4.0.0/modules/apps/29-fee/keeper/msg_server_test.go#L419) | `Verified` | +| 4.16 | A blocked account shall not be allowed to incentivize a packet sent. | [Acceptance test](https://github.com/cosmos/ibc-go/blob/v4.0.0/modules/apps/29-fee/keeper/msg_server_test.go#L426) | `Verified` | + +### 5 - Fee distribution + +| ID | Description | Verification | Status | +| --- | ----------- | ------------ | ------ | +| 5.01 | Fee distribution shall occurr on the source chain from which packets originate. | Either in [`OnAcknowledgementPacket`](https://github.com/cosmos/ibc-go/blob/v4.0.0/modules/apps/29-fee/ibc_middleware.go#L288) or in [`OnTimeoutPacket`](https://github.com/cosmos/ibc-go/blob/v4.0.0/modules/apps/29-fee/ibc_middleware.go#L330). | `Verified` | + +#### `OnAcknowledgementPacket` + +| ID | Description | Verification | Status | +| --- | ----------- | ------------ | ------ | +| 5.02 | Fees for successful relaying of `MsgAcknowledgement` shall be distributed to the relayer address (or its associated payee address if one has been registered). | If a payee address exists for the relayer address, then [fees are distributed to the payee address](https://github.com/cosmos/ibc-go/blob/v4.0.0/modules/apps/29-fee/ibc_middleware.go#L288); otherwise [fees are distributed to the relayer address](https://github.com/cosmos/ibc-go/blob/v4.0.0/modules/apps/29-fee/ibc_middleware.go#L277). | `Verified` | +| 5.03 | Fees for successful relaying of `MsgRecvPacket` shall be distributed to the payee address of the relayer address. | [Fees are distributed to the payee address registered on the counterparty chain](https://github.com/cosmos/ibc-go/blob/v4.0.0/modules/apps/29-fee/keeper/escrow.go#L93) if it is a valid `Bech32` address and is not a blocked address. | `Verified` | + +#### `OnTimeoutPacket` + +| ID | Description | Verification | Status | +| --- | ----------- | ------------ | ------ | +| 5.05 | Fees for successful relaying of `MsgTimeout` shall be distributed to the relayer address (or its associated payee address if one has been registered). | If a payee address exists for the relayer address, then [fees are distributed to the payee address](https://github.com/cosmos/ibc-go/blob/v4.0.0/modules/apps/29-fee/ibc_middleware.go#L330); otherwise [fees are distributed to the relayer address](https://github.com/cosmos/ibc-go/blob/v4.0.0/modules/apps/29-fee/ibc_middleware.go#L319). | `Verified` | + +### 6 - Fee refunding + +#### `OnAcknowledgementPacket` + +| ID | Description | Verification | Status | +| --- | ----------- | ------------ | ------ | +| 6.01 | On successful processing of `MsgAcknowledgement`, fees for successful relay of `MsgTimeout` shall be refunded. | [Fees are refunded is the refund address is a valid `Bech32` address](https://github.com/cosmos/ibc-go/blob/v4.0.0/modules/apps/29-fee/keeper/escrow.go#L103). | `Verified` | +| 6.02 | On successful processing of `MsgAcknowledgement`, if fees for successful relay of `MsgRecvPacket` cannot be distributed, then they should be refunded. | [Fees are refunded](https://github.com/cosmos/ibc-go/blob/v4.0.0/modules/apps/29-fee/keeper/escrow.go#L96) if the payee address registered on the counterparty chain for the relayer is either [invalid](https://github.com/cosmos/ibc-go/blob/v4.0.0/modules/apps/29-fee/keeper/escrow_test.go#L105) or a [blocked address](https://github.com/cosmos/ibc-go/blob/v4.0.0/modules/apps/29-fee/keeper/escrow_test.go#L120). | `Verified` | + +#### `OnTimeoutPacket` + +| ID | Description | Verification | Status | +| --- | ----------- | ------------ | ------ | +| 6.03 | On successful processing of `MsgTimeout`, fees for successful relay of `MsgRecvPacket` shall be refunded. | [Fees are refunded is the refund address is a valid `Bech32` address](https://github.com/cosmos/ibc-go/blob/v4.0.0/modules/apps/29-fee/keeper/escrow.go#L146). | `Verified` | +| 6.04 | On successful processing of `MsgTimeout`, fees for successful relay of `MsgAcknowledgement` shall be refunded. | [Fees are refunded is the refund address is a valid `Bech32` address](https://github.com/cosmos/ibc-go/blob/v4.0.0/modules/apps/29-fee/keeper/escrow.go#L149). | `Verified` | + +#### Channel closure + +| ID | Description | Verification | Status | +| --- | ----------- | ------------ | ------ | +| 6.04 | Packet fees are refunded on channel closure. | Both on [`OnChanCloseInit`](https://github.com/cosmos/ibc-go/blob/v4.0.0/modules/apps/29-fee/ibc_middleware.go#L182) and [`OnChanCloseConfirm`](https://github.com/cosmos/ibc-go/blob/v4.0.0/modules/apps/29-fee/ibc_middleware.go#L207). | `Verified` | + +# Non-functional requirements + +## 7 - Security + +| ID | Description | Verification | Status | +| -- | ----------- | ------------ | ------ | +| 7.01 | If the escrow account does not have sufficient funds while distributing fees in `OnAcknowledgementPacket`, the fee module shall become locked. | [Acceptance test](https://github.com/cosmos/ibc-go/blob/v4.0.0/modules/apps/29-fee/keeper/escrow_test.go#L85) | `Verified` | +| 7.02 | If the escrow account does not have sufficient funds while distributing fees in `OnTimeoutPacket`, the fee module shall become locked. | [Acceptance test](https://github.com/cosmos/ibc-go/blob/v4.0.0/modules/apps/29-fee/keeper/escrow_test.go#L236) | `Verified` | +| 7.03 | If the escrow account does not have sufficient funds while refunding fees in channel closure, the fee module shall become locked. | [Acceptance test](https://github.com/cosmos/ibc-go/blob/v4.0.0/modules/apps/29-fee/keeper/escrow_test.go#L377) | `Verified` | + +# External interface requirements + +## 8 - CLI + +### Query + +| ID | Description | Verification | Status | +| -- | ----------- | ------------ | ------ | +| 8.01 | There shall be a CLI command available to query for the incentivization of an unrelayed packet by port ID, channel ID and packet sequence. | [CLI](https://github.com/cosmos/ibc-go/blob/v4.0.0/modules/apps/29-fee/client/cli/query.go#L64) | `Verified` | +| 8.02 | There shall be a CLI command to query for the incentivization of all unrelayed packets on all open channels. | [CLI](https://github.com/cosmos/ibc-go/blob/v4.0.0/modules/apps/29-fee/client/cli/query.go#L64) | `Verified` | +| 8.03 | There shall be a CLI command available to query for the total fees for `MsgRecvPacket` for a given port ID, channel ID and packet sequence. | [CLI](https://github.com/cosmos/ibc-go/blob/v4.0.0/modules/apps/29-fee/client/cli/query.go#L105) | `Verified` | +| 8.04 | There shall be a CLI command available to query for the total fees for `MsgAcknowledgement` for a given port ID, channel ID and packet sequence. | [CLI](https://github.com/cosmos/ibc-go/blob/v4.0.0/modules/apps/29-fee/client/cli/query.go#L151) | `Verified` | +| 8.05 | There shall be a CLI command available to query for the total fees for `MsgTimeout` for a given port ID, channel ID and packet sequence. | [CLI](https://github.com/cosmos/ibc-go/blob/v4.0.0/modules/apps/29-fee/client/cli/query.go#L197) | `Verified` | +| 8.06 | There shall be a CLI command available to query for the payee address registered for a relayer for a specific channel ID. | [CLI](https://github.com/cosmos/ibc-go/blob/v4.0.0/modules/apps/29-fee/client/cli/query.go#243) | `Verified` | +| 8.07 | There shall be a CLI command available to query for the counterparty payee address registered for a relayer for a specific channel ID. | [CLI](https://github.com/cosmos/ibc-go/blob/v4.0.0/modules/apps/29-fee/client/cli/query.go#282) | `Verified` | +| 8.08 | There shall be a CLI command available to query if a channel is fee-enabled for a port ID and channel ID. | [CLI](https://github.com/cosmos/ibc-go/blob/v4.0.0/modules/apps/29-fee/client/cli/query.go#L362) | `Verified` | +| 8.09 | There shall be a CLI command available to query for all the unrelayed incentivized packets in a channel by a port ID and channel ID. | [CLI](https://github.com/cosmos/ibc-go/blob/v4.0.0/modules/apps/29-fee/client/cli/query.go#L397) | `Verified` | + +### Transaction + +| ID | Description | Verification | Status | +| -- | ----------- | ------------ | ------ | +| 8.10 | There shall be a CLI command available to register a payee address for a channel by port ID and channel ID. | [CLI](https://github.com/cosmos/ibc-go/blob/v4.0.0/modules/apps/29-fee/client/cli/tx.go#L26) | `Verified` | +| 8.11 | There shall be a CLI command available to register a counterparty payee address for a channel by port ID and channel ID. | [CLI](https://github.com/cosmos/ibc-go/blob/v4.0.0/modules/apps/29-fee/client/cli/tx.go#L51) | `Verified` | +| 8.12 | There shall be a CLI command available to incentivize a packet by port ID, channel ID and packet sequence. | [CLI](https://github.com/cosmos/ibc-go/blob/v4.0.0/modules/apps/29-fee/client/cli/tx.go#L76) | `Verified` | diff --git a/docs/requirements/requirements-template.md b/docs/requirements/requirements-template.md new file mode 100644 index 00000000000..6afd9961bf8 --- /dev/null +++ b/docs/requirements/requirements-template.md @@ -0,0 +1,53 @@ + + +# Business requirements + + + + + +## Problem + + + +## Objectives + + + +## Scope + + + + + +# User requirements + +## Use cases + + + +# Functional requirements + + + +## Assumptions and dependencies + + + +## Features + + + +# External interface requirements + + + +# Non-functional requirements + + \ No newline at end of file diff --git a/docs/roadmap/roadmap.md b/docs/roadmap/roadmap.md index eaf416adfb9..241cb5c16ab 100644 --- a/docs/roadmap/roadmap.md +++ b/docs/roadmap/roadmap.md @@ -4,54 +4,68 @@ order: 1 # Roadmap ibc-go -_Lastest update: July 7, 2022_ +_Lastest update: December 21st, 2022_ This document endeavours to inform the wider IBC community about plans and priorities for work on ibc-go by the team at Interchain GmbH. It is intended to broadly inform all users of ibc-go, including developers and operators of IBC, relayer, chain and wallet applications. -This roadmap should be read as a high-level guide, rather than a commitment to schedules and deliverables. The degree of specificity is inversely proportional to the timeline. We will update this document periodically to reflect the status and plans. +This roadmap should be read as a high-level guide, rather than a commitment to schedules and deliverables. The degree of specificity is inversely proportional to the timeline. We will update this document periodically to reflect the status and plans. For the latest expected release timelines, please check [here](https://github.com/cosmos/ibc-go/wiki/Release-timeline). -## Q3 - 2022 +## v7.0.0 -At a high level we will focus on: +### 02-client refactor -### Features +This refactor will make the development of light clients easier. The ibc-go implementation will finally align with the spec and light clients will be required to set their own client and consensus states. This will allow more flexibility for light clients to manage their own internal storage and do batch updates. See [ADR 006](../architecture/adr-006-02-client-refactor.md) for more information. -- Releasing [v4.0.0](https://github.com/cosmos/ibc-go/milestone/26), which includes the ICS-29 Fee Middleware module. -- Finishing and releasing the [refactoring of 02-client](https://github.com/cosmos/ibc-go/milestone/16). This refactor will make the development of light clients easier. -- Starting the implementation of channel upgradability (see [epic](https://github.com/cosmos/ibc-go/issues/1599) and [alpha milestone](https://github.com/cosmos/ibc-go/milestone/29)) with the goal of cutting an alpha1 pre-release by the end of the quarter. Channel upgradability will allow chains to renegotiate an existing channel to take advantage of new features without having to create a new channel, thus preserving all existing packet state processed on the channel. -- Implementing the new [`ORDERED_ALLOW_TIMEOUT` channel type](https://github.com/cosmos/ibc-go/milestone/31) and hopefully releasing it as well. This new channel type will allow packets on an ordered channel to timeout without causing the closure of the channel. +Follow the progress with the [beta](https://github.com/cosmos/ibc-go/milestone/25) and [RC](https://github.com/cosmos/ibc-go/milestone/27) milestones or in the [project board](https://github.com/orgs/cosmos/projects/7/views/14). -### Testing and infrastructure +### Upgrade Cosmos SDK v0.47 -- Adding [automated e2e tests](https://github.com/cosmos/ibc-go/milestone/32) to the repo's CI. +Follow the progress with the [milestone](https://github.com/cosmos/ibc-go/milestone/36). -### Documentation and backlog +### Add `authz` support to 20-transfer -- Finishing and releasing the upgrade to Cosmos SDK v0.46. -- Writing the [light client implementation guide](https://github.com/cosmos/ibc-go/issues/59). -- Working on [core backlog issues](https://github.com/cosmos/ibc-go/milestone/28). -- Depending on the timeline of the Cosmos SDK, implementing and testing the changes needed to support the [transtion to SMT storage](https://github.com/cosmos/ibc-go/milestone/21). +Authz goes cross chain: users can grant permission for their tokens to be transferred to another chain on their behalf. See [this issue](https://github.com/cosmos/ibc-go/issues/2431) for more details. -We have also received a lot of feedback to improve Interchain Accounts and we might also work on a few things, but will depend on priorities and availability. +## v7.1.0 -For a detail view of each iteration's planned work, please check out our [project board](https://github.com/orgs/cosmos/projects/7). +Because it is so important to have an ibc-go release compatible with the latest Cosmos SDK release, a couple of features will take a little longer and be released in [v7.1.0](https://github.com/cosmos/ibc-go/milestone/37). -### Release schedule +### Localhost connection -#### **July** +This feature will add support for applications on a chain to communicate with applications on the same chain using the existing standard interface to communicate with applications on remote chains. This is a powerful UX improvement, particularly for those users interested in interacting with multiple smart contracts on a single chain through one interface. -We will probably cut at least one more release candidate of v4.0.0 before the final release, which should happen around the end of the month. +For more details, see the design proposal and discussion [here](https://github.com/cosmos/ibc-go/discussions/2191). -For the Rho upgrade of the Cosmos Hub we will also release a new minor version of v3 with SDK 0.46. +A special shout out to Strangelove for their substantial contribution on this feature. -#### **August** +### Support for Wasm light clients -In the first half we will probably start cutting release candidates for the 02-client refactor. Final release would most likely come out at the end of the month or beginning of September. +We will add support for Wasm light clients. The first Wasm client developed with ibc-go/v7 02-client refactor and stored as Wasm bytecode will be the GRANDPA light client used for Cosmos <> Substrate IBC connections. This feature will be used also for a NEAR light client in the future. -#### **September** +This feature was developed by Composable and Strangelove but will be upstreamed into ibc-go. -We might cut some pre-releases for the new channel type, and by the end of the month we expect to cut the first alpha pre-release for channel upgradability. +## v8.0.0 -## Q4 - 2022 +### Channel upgradability -We will continue the implementation and cut the final release of [channel upgradability](https://github.com/cosmos/ibc/blob/master/spec/core/ics-004-channel-and-packet-semantics/UPGRADES.md). At the end of Q3 or maybe beginning of Q4 we might also work on designing the implementation and scoping the engineering work to add support for [multihop channels](https://github.com/cosmos/ibc/pull/741/files), so that we could start the implementation of this feature during Q4 (but this is still be decided). +Channel upgradability will allow chains to renegotiate an existing channel to take advantage of new features without having to create a new channel, thus preserving all existing packet state processed on the channel. + +Follow the progress with the [alpha milestone](https://github.com/cosmos/ibc-go/milestone/29) or the [project board](https://github.com/orgs/cosmos/projects/7/views/17). + +### Path unwinding + +This feature will allow tokens with non-native denoms to be sent back automatically to their native chains before being sent to a final destination chain. This will allow tokens to reach a final destination with the least amount possible of hops from their native chain. + +For more details, see this [discussion](https://github.com/cosmos/ibc/discussions/824). + +--- + +This roadmap is also available as a [project board](https://github.com/orgs/cosmos/projects/7/views/25). + +For the latest expected release timelines, please check [here](https://github.com/cosmos/ibc-go/wiki/Release-timeline). + +For the latest information on the progress of the work or the decisions made that might influence the roadmap, please follow our [engineering updates](https://github.com/cosmos/ibc-go/wiki/Engineering-updates). + +--- + +**Note**: release version numbers may be subject to change. diff --git a/docs/versions b/docs/versions index 7771cabc308..265e01a0bfe 100644 --- a/docs/versions +++ b/docs/versions @@ -1,5 +1,19 @@ +release/v6.1.x v6.1.0 +release/v6.0.x v6.0.0 +release/v5.2.x v5.2.0 +release/v5.1.x v5.1.0 +release/v5.0.x v5.0.0 +release/v4.3.x v4.3.0 +release/v4.2.x v4.2.0 +release/v4.1.x v4.1.0 +release/v4.0.x v4.0.0 +release/v3.4.x v3.4.0 +release/v3.3.x v3.3.0 +release/v3.2.x v3.2.0 release/v3.1.x v3.1.0 release/v3.0.x v3.0.0 +release/v2.5.x v2.5.0 +release/v2.4.x v2.4.0 release/v2.3.x v2.3.0 release/v2.2.x v2.2.0 release/v2.1.x v2.1.0 diff --git a/e2e/Makefile b/e2e/Makefile index 95a0fa5b6a2..0b9802d06e1 100644 --- a/e2e/Makefile +++ b/e2e/Makefile @@ -8,6 +8,9 @@ cleanup-ibc-test-containers: done e2e-test: cleanup-ibc-test-containers - ./scripts/run-e2e.sh $(suite) $(test) + ./scripts/run-e2e.sh $(entrypoint) $(test) -.PHONY: cleanup-ibc-test-containers e2e-test +compatibility-tests: + ./scripts/run-compatibility-tests.sh $(release_branch) + +.PHONY: cleanup-ibc-test-containers e2e-test compatibility-tests diff --git a/e2e/README.md b/e2e/README.md new file mode 100644 index 00000000000..1080f6a41a4 --- /dev/null +++ b/e2e/README.md @@ -0,0 +1,354 @@ +# Table of Contents + +1. [How to write tests](#how-to-write-tests) + - a. [Adding a new test](#adding-a-new-test) + - b. [Running the tests with custom images](#running-tests-with-custom-images) + - b. [Code samples](#code-samples) + - [Setup](#setup) + - [Creating test users](#creating-test-users) + - [Waiting](#waiting) + - [Query wallet balances](#query-wallet-balances) + - [Broadcasting messages](#broadcasting-messages) + - [Starting the relayer](#starting-the-relayer) + - [Arbitrary commands](#arbitrary-commands) + - [IBC transfer](#ibc-transfer) +2. [Test design](#test-design) + - a. [interchaintest](#interchaintest) + - b. [CI configuration](#ci-configuration) +3. [Github Workflows](#github-workflows) +4. [Running Compatibility Tests](#running-compatibility-tests) +5. [Troubleshooting](#troubleshooting) + +# How to write tests + +## Adding a new test + +All tests should go under the [e2e](https://github.com/cosmos/ibc-go/tree/main/e2e) directory. When adding a new test, either add a new test function +to an existing test suite **_in the same file_**, or create a new test suite in a new file and add test functions there. +New test files should follow the convention of `module_name_test.go`. + +New test suites should be composed of `testsuite.E2ETestSuite`. This type has lots of useful helper functionality that will +be quite common in most tests. + +> Note: see [here](#how-tests-are-run) for details about these requirements. + +## Running tests with custom images + +Tests can be run using a Makefile target under the e2e directory. `e2e/Makefile` + +There are several envinronment variables that alter the behaviour of the make target. + +| Environment Variable | Description | Default Value | +|----------------------|-------------------------------------------|---------------| +| CHAIN_IMAGE | The image that will be used for the chain | ibc-go-simd | +| CHAIN_A_TAG | The tag used for chain B | latest | +| CHAIN_B_TAG | The tag used for chain A | latest | +| CHAIN_BINARY | The binary used in the container | simd | +| RELAYER_TAG | The tag used for the relayer | main | +| RELAYER_TYPE | The type of relayer to use (rly/hermes) | rly | + +> Note: when running tests locally, **no images are pushed** to the `ghcr.io/cosmos/ibc-go-simd` registry. +The images which are used only exist on your machine. + +These environment variables allow us to run tests with arbitrary verions (from branches or released) of simd +and the go relayer. + +Every time changes are pushed to a branch or to `main`, a new `simd` image is built and pushed [here](https://github.com/cosmos/ibc-go/pkgs/container/ibc-go-simd). + +### Example Command + +```sh +export CHAIN_IMAGE="ghcr.io/cosmos/ibc-go-simd" +export CHAIN_A_TAG="main" +export CHAIN_BINARY="simd" + +# We can also specify different values for the chains if needed. +# they will default to the same as chain a. +# export CHAIN_B_TAG="main" +# export CHAIN_BINARY="icad" + +export RELAYER_TAG="v2.0.0" +make e2e-test entrypoint=TestInterchainAccountsTestSuite test=TestMsgSubmitTx_SuccessfulTransfer +``` + +> Note: sometimes it can be useful to make changes to [ibctest](https://github.com/strangelove-ventures/interchaintest) when running tests locally. In order to do this, add the following line to +e2e/go.mod + +`replace github.com/strangelove-ventures/interchaintest => ../ibctest` + +Or point it to any local checkout you have. + +### Running tests in CI + +To run tests in CI, you can checkout the ibc-go repo and provide these environment variables +to the CI task. + +[This repo](https://github.com/chatton/ibc-go-e2e-demo) contains an example of how to do this with Github Actions. + +## Code samples + +### Setup + +Every standard test will start with this. This creates two chains and a relayer, +initializes relayer accounts on both chains, establishes a connection and a channel +between the chains. + +Both chains have started, but the relayer is not yet started. + +The relayer should be started as part of the test if required. See [Starting the Relayer](#starting-the-relayer) + +```go +relayer, channelA := s.SetupChainsRelayerAndChannel(ctx, feeMiddlewareChannelOptions()) +chainA, chainB := s.GetChains() +``` + +### Creating test users + +There are helper functions to easily create users on both chains. + +```go +chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) +chainBWallet := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount) +``` + +### Waiting + +We can wait for some number of blocks on the specified chains if required. + +```go +chainA, chainB := s.GetChains() +err := test.WaitForBlocks(ctx, 1, chainA, chainB) +s.Require().NoError(err) +``` + +### Query wallet balances + +We can fetch balances of wallets on specific chains. + +```go +chainABalance, err := s.GetChainANativeBalance(ctx, chainAWallet) +s.Require().NoError(err) +``` + +### Broadcasting messages + +We can broadcast arbitrary messages which are signed on behalf of users created in the test. + +This example shows a multi message transaction being broadcast on chainA and signed on behalf of chainAWallet. + +```go +relayer, channelA := s.SetupChainsRelayerAndChannel(ctx, feeMiddlewareChannelOptions()) +chainA, chainB := s.GetChains() + +chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) +chainBWallet := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount) + +t.Run("broadcast multi message transaction", func(t *testing.T){ + payPacketFeeMsg := feetypes.NewMsgPayPacketFee(testFee, channelA.PortID, channelA.ChannelID, chainAWallet.Bech32Address(chainA.Config().Bech32Prefix), nil) + transferMsg := transfertypes.NewMsgTransfer(channelA.PortID, channelA.ChannelID, transferAmount, chainAWallet.Bech32Address(chainA.Config().Bech32Prefix), chainBWallet.Bech32Address(chainB.Config().Bech32Prefix), clienttypes.NewHeight(1, 1000), 0) + resp, err := s.BroadcastMessages(ctx, chainA, chainAWallet, payPacketFeeMsg, transferMsg) + + s.AssertValidTxResponse(resp) + s.Require().NoError(err) +}) +``` + +### Starting the relayer + +The relayer can be started with the following. + +```go +t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer) +}) +``` + +### Arbitrary commands + +Arbitrary commands can be executed on a given chain. + +> Note: these commands will be fully configured to run on the chain executed on (home directory, ports configured etc.) + +However, it is preferable to [broadcast messages](#broadcasting-messages) or use a gRPC query if possible. + +```go +stdout, stderr, err := chainA.Exec(ctx, []string{"tx", "..."}, nil) +``` + +### IBC transfer + +It is possible to send an IBC transfer in two ways. + +Use the ibctest `Chain` interface (this ultimately does a docker exec) + +```go +t.Run("send IBC transfer", func(t *testing.T) { + chainATx, err = chainA.SendIBCTransfer(ctx, channelA.ChannelID, chainAWallet.KeyName, walletAmount, nil) + s.Require().NoError(err) + s.Require().NoError(chainATx.Validate(), "chain-a ibc transfer tx is invalid") +}) +``` + +Broadcast a `MsgTransfer`. + +```go +t.Run("send IBC transfer", func(t *testing.T){ + transferMsg := transfertypes.NewMsgTransfer(channelA.PortID, channelA.ChannelID, transferAmount, chainAWallet.Bech32Address(chainA.Config().Bech32Prefix), chainBWallet.Bech32Address(chainB.Config().Bech32Prefix), clienttypes.NewHeight(1, 1000), 0) + resp, err := s.BroadcastMessages(ctx, chainA, chainAWallet, transferMsg) + s.AssertValidTxResponse(resp) + s.Require().NoError(err) +}) +``` + +## Test design + +### interchaintest + +These E2E tests use the [interchaintest framework](https://github.com/strangelove-ventures/interchaintest). This framework creates chains and relayers in containers and allows for arbitrary commands to be executed in the chain containers, +as well as allowing us to broadcast arbitrary messages which are signed on behalf of a user created in the test. + +### CI configuration + +There are two main github actions for e2e tests. + +[e2e.yaml](https://github.com/cosmos/ibc-go/blob/main/.github/workflows/e2e.yaml) which runs when collaborators create branches. + +[e2e-fork.yaml](https://github.com/cosmos/ibc-go/blob/main/.github/workflows/e2e-fork.yml) which runs when forks are created. + +In `e2e.yaml`, the `simd` image is built and pushed to [a registry](https://github.com/cosmos/ibc-go/pkgs/container/ibc-go-simd) and every test +that is run uses the image that was built. + +In `e2e-fork.yaml`, images are not pushed to this registry, but instead remain local to the host runner. + +## How tests are run + +The tests use the `matrix` feature of Github Actions. The matrix is +dynamically generated using [this command](https://github.com/cosmos/ibc-go/blob/main/cmd/build_test_matrix/main.go). + +> Note: there is currently a limitation that all tests belonging to a test suite must be in the same file. +In order to support test functions spread in different files, we would either need to manually maintain a matrix +or update the script to account for this. The script assumes there is a single test suite per test file to avoid an overly complex +generation process. + +Which looks under the `e2e` directory, and creates a task for each test suite function. + +### Example + +```go +// e2e/file_one_test.go +package e2e + +func TestFeeMiddlewareTestSuite(t *testing.T) { + suite.Run(t, new(FeeMiddlewareTestSuite)) +} + +type FeeMiddlewareTestSuite struct { + testsuite.E2ETestSuite +} + +func (s *FeeMiddlewareTestSuite) TestA() {} +func (s *FeeMiddlewareTestSuite) TestB() {} +func (s *FeeMiddlewareTestSuite) TestC() {} + +``` + +```go +// e2e/file_two_test.go +package e2e + +func TestTransferTestSuite(t *testing.T) { + suite.Run(t, new(TransferTestSuite)) +} + +type TransferTestSuite struct { + testsuite.E2ETestSuite +} + +func (s *TransferTestSuite) TestD() {} +func (s *TransferTestSuite) TestE() {} +func (s *TransferTestSuite) TestF() {} + +``` + +In the above example, the following would be generated. + +```json +{ + "include": [ + { + "entrypoint": "TestFeeMiddlewareTestSuite", + "test": "TestA" + }, + { + "entrypoint": "TestFeeMiddlewareTestSuite", + "test": "TestB" + }, + { + "entrypoint": "TestFeeMiddlewareTestSuite", + "test": "TestC" + }, + { + "entrypoint": "TestTransferTestSuite", + "test": "TestD" + }, + { + "entrypoint": "TestTransferTestSuite", + "test": "TestE" + }, + { + "entrypoint": "TestTransferTestSuite", + "test": "TestF" + } + ] +} +``` + +This string is used to generate a test matrix in the Github Action that runs the E2E tests. + +All tests will be run on different hosts. + +### Misceleneous: + +## GitHub Workflows + +### Building and pushing a `simd` image + +If we ever need to manually build and push an image, we can do so with the [Build Simd Image](../.github/workflows/build-simd-image-from-tag.yml) GitHub workflow. + +This can be triggered manually from the UI by navigating to + +`Actions` -> `Build Simd Image` -> `Run Workflow` + +And providing the git tag. + +Alternatively, the [gh](https://cli.github.com/) CLI tool can be used to trigger this workflow. + +```bash +gh workflow run "Build Simd Image" -f tag=v3.0.0 +``` + +## Running Compatibility Tests + +To trigger the compatibility tests for a release branch, you can use the following command. + +```bash +make compatibility-tests release_branch=release/v5.0.x +``` + +This will build an image from the tip of the release branch and run all tests specified in the corresponding +json matrix files under .github/compatibility-test-matrices and is equivalent to going to the Github UI and navigating to + +`Actions` -> `Compatibility E2E` -> `Run Workflow` -> `release/v5.0.x` + +## Troubleshooting + +- On Mac, after running a lot of tests, it can happen that containers start failing. To fix this, you can try clearing existing containers and restarting the docker daemon. + + This generally manifests itself as relayer or simd containers timing out during setup stages of the test. This doesn't happen in CI. + + ```bash + # delete all images + docker system prune -af + ``` + + This issue doesn't seem to occur on other operating systems. diff --git a/e2e/fee_middleware_test.go b/e2e/fee_middleware_test.go deleted file mode 100644 index e6207f5d729..00000000000 --- a/e2e/fee_middleware_test.go +++ /dev/null @@ -1,439 +0,0 @@ -package e2e - -import ( - "context" - "testing" - "time" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/strangelove-ventures/ibctest/broadcast" - "github.com/strangelove-ventures/ibctest/chain/cosmos" - "github.com/strangelove-ventures/ibctest/ibc" - "github.com/strangelove-ventures/ibctest/test" - "github.com/stretchr/testify/suite" - - "e2e/testsuite" - "e2e/testvalues" - - feetypes "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" -) - -func TestFeeMiddlewareTestSuite(t *testing.T) { - suite.Run(t, new(FeeMiddlewareTestSuite)) -} - -type FeeMiddlewareTestSuite struct { - testsuite.E2ETestSuite -} - -// RegisterCounterPartyPayee broadcasts a MsgRegisterCounterpartyPayee message. -func (s *FeeMiddlewareTestSuite) RegisterCounterPartyPayee(ctx context.Context, chain *cosmos.CosmosChain, - user broadcast.User, portID, channelID, relayerAddr, counterpartyPayeeAddr string) (sdk.TxResponse, error) { - msg := feetypes.NewMsgRegisterCounterpartyPayee(portID, channelID, relayerAddr, counterpartyPayeeAddr) - return s.BroadcastMessages(ctx, chain, user, msg) -} - -// QueryCounterPartyPayee queries the counterparty payee of the given chain and relayer address on the specified channel. -func (s *FeeMiddlewareTestSuite) QueryCounterPartyPayee(ctx context.Context, chain ibc.Chain, relayerAddress, channelID string) (string, error) { - queryClient := s.GetChainGRCPClients(chain).FeeQueryClient - res, err := queryClient.CounterpartyPayee(ctx, &feetypes.QueryCounterpartyPayeeRequest{ - ChannelId: channelID, - Relayer: relayerAddress, - }) - - if err != nil { - return "", err - } - return res.CounterpartyPayee, nil -} - -// PayPacketFeeAsync broadcasts a MsgPayPacketFeeAsync message. -func (s *FeeMiddlewareTestSuite) PayPacketFeeAsync( - ctx context.Context, - chain *cosmos.CosmosChain, - user broadcast.User, - packetID channeltypes.PacketId, - packetFee feetypes.PacketFee, -) (sdk.TxResponse, error) { - msg := feetypes.NewMsgPayPacketFeeAsync(packetID, packetFee) - return s.BroadcastMessages(ctx, chain, user, msg) -} - -// QueryIncentivizedPacketsForChannel queries the incentivized packets on the specified channel. -func (s *FeeMiddlewareTestSuite) QueryIncentivizedPacketsForChannel( - ctx context.Context, - chain *cosmos.CosmosChain, - portId, - channelId string, -) ([]*feetypes.IdentifiedPacketFees, error) { - queryClient := s.GetChainGRCPClients(chain).FeeQueryClient - res, err := queryClient.IncentivizedPacketsForChannel(ctx, &feetypes.QueryIncentivizedPacketsForChannelRequest{ - PortId: portId, - ChannelId: channelId, - }) - if err != nil { - return nil, err - } - return res.IncentivizedPackets, err -} - -func (s *FeeMiddlewareTestSuite) TestMsgPayPacketFee_AsyncSingleSender_Succeeds() { - t := s.T() - ctx := context.TODO() - - relayer, channelA := s.SetupChainsRelayerAndChannel(ctx, feeMiddlewareChannelOptions()) - chainA, chainB := s.GetChains() - - var ( - chainADenom = chainA.Config().Denom - testFee = testvalues.DefaultFee(chainADenom) - chainATx ibc.Tx - payPacketFeeTxResp sdk.TxResponse - ) - - chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) - - t.Run("relayer wallets recovered", func(t *testing.T) { - err := s.RecoverRelayerWallets(ctx, relayer) - s.Require().NoError(err) - }) - - chainARelayerWallet, chainBRelayerWallet, err := s.GetRelayerWallets(relayer) - t.Run("relayer wallets fetched", func(t *testing.T) { - s.Require().NoError(err) - }) - - s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB), "failed to wait for blocks") - - _, chainBRelayerUser := s.GetRelayerUsers(ctx) - - t.Run("register counter party payee", func(t *testing.T) { - resp, err := s.RegisterCounterPartyPayee(ctx, chainB, chainBRelayerUser, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, chainBRelayerWallet.Address, chainARelayerWallet.Address) - s.Require().NoError(err) - s.AssertValidTxResponse(resp) - }) - - t.Run("verify counter party payee", func(t *testing.T) { - address, err := s.QueryCounterPartyPayee(ctx, chainB, chainBRelayerWallet.Address, channelA.Counterparty.ChannelID) - s.Require().NoError(err) - s.Require().Equal(chainARelayerWallet.Address, address) - }) - - walletAmount := ibc.WalletAmount{ - Address: chainAWallet.Bech32Address(chainB.Config().Bech32Prefix), // destination address - Denom: chainADenom, - Amount: testvalues.IBCTransferAmount, - } - - t.Run("send IBC transfer", func(t *testing.T) { - chainATx, err = chainA.SendIBCTransfer(ctx, channelA.ChannelID, chainAWallet.KeyName, walletAmount, nil) - s.Require().NoError(err) - s.Require().NoError(chainATx.Validate(), "chain-a ibc transfer tx is invalid") - }) - - t.Run("tokens are escrowed", func(t *testing.T) { - actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet) - s.Require().NoError(err) - - expected := testvalues.StartingTokenAmount - walletAmount.Amount - s.Require().Equal(expected, actualBalance) - }) - - t.Run("pay packet fee", func(t *testing.T) { - t.Run("no incentivized packets", func(t *testing.T) { - packets, err := s.QueryIncentivizedPacketsForChannel(ctx, chainA, channelA.PortID, channelA.ChannelID) - s.Require().NoError(err) - s.Require().Empty(packets) - }) - - packetId := channeltypes.NewPacketId(channelA.PortID, channelA.ChannelID, 1) - packetFee := feetypes.NewPacketFee(testFee, chainAWallet.Bech32Address(chainA.Config().Bech32Prefix), nil) - - t.Run("should succeed", func(t *testing.T) { - payPacketFeeTxResp, err = s.PayPacketFeeAsync(ctx, chainA, chainAWallet, packetId, packetFee) - s.Require().NoError(err) - s.AssertValidTxResponse(payPacketFeeTxResp) - }) - - t.Run("there should be incentivized packets", func(t *testing.T) { - packets, err := s.QueryIncentivizedPacketsForChannel(ctx, chainA, channelA.PortID, channelA.ChannelID) - s.Require().NoError(err) - s.Require().Len(packets, 1) - actualFee := packets[0].PacketFees[0].Fee - - s.Require().True(actualFee.RecvFee.IsEqual(testFee.RecvFee)) - s.Require().True(actualFee.AckFee.IsEqual(testFee.AckFee)) - s.Require().True(actualFee.TimeoutFee.IsEqual(testFee.TimeoutFee)) - }) - - t.Run("balance should be lowered by sum of recv ack and timeout", func(t *testing.T) { - // The balance should be lowered by the sum of the recv, ack and timeout fees. - actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet) - s.Require().NoError(err) - - expected := testvalues.StartingTokenAmount - walletAmount.Amount - testFee.Total().AmountOf(chainADenom).Int64() - s.Require().Equal(expected, actualBalance) - }) - }) - - t.Run("start relayer", func(t *testing.T) { - s.StartRelayer(relayer) - }) - - t.Run("packets are relayed", func(t *testing.T) { - packets, err := s.QueryIncentivizedPacketsForChannel(ctx, chainA, channelA.PortID, channelA.ChannelID) - s.Require().NoError(err) - s.Require().Empty(packets) - }) - - t.Run("timeout fee is refunded", func(t *testing.T) { - actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet) - s.Require().NoError(err) - - // once the relayer has relayed the packets, the timeout fee should be refunded. - expected := testvalues.StartingTokenAmount - walletAmount.Amount - testFee.AckFee.AmountOf(chainADenom).Int64() - testFee.RecvFee.AmountOf(chainADenom).Int64() - s.Require().Equal(expected, actualBalance) - }) -} - -func (s *FeeMiddlewareTestSuite) TestMultiMsg_MsgPayPacketFeeSingleSender() { - t := s.T() - ctx := context.TODO() - - relayer, channelA := s.SetupChainsRelayerAndChannel(ctx, feeMiddlewareChannelOptions()) - - chainA, chainB := s.GetChains() - - var ( - chainADenom = chainA.Config().Denom - testFee = testvalues.DefaultFee(chainADenom) - multiMsgTxResponse sdk.TxResponse - ) - - transferAmount := testvalues.DefaultTransferAmount(chainA.Config().Denom) - - chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) - chainBWallet := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount) - - t.Run("relayer wallets recovered", func(t *testing.T) { - err := s.RecoverRelayerWallets(ctx, relayer) - s.Require().NoError(err) - }) - - chainARelayerWallet, chainBRelayerWallet, err := s.GetRelayerWallets(relayer) - t.Run("relayer wallets fetched", func(t *testing.T) { - s.Require().NoError(err) - }) - - s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB), "failed to wait for blocks") - - chainARelayerUser, chainBRelayerUser := s.GetRelayerUsers(ctx) - - relayerAStartingBalance, err := s.GetChainANativeBalance(ctx, chainARelayerUser) - s.Require().NoError(err) - t.Logf("relayer A user starting with balance: %d", relayerAStartingBalance) - - t.Run("register counter party payee", func(t *testing.T) { - multiMsgTxResponse, err = s.RegisterCounterPartyPayee(ctx, chainB, chainBRelayerUser, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, chainBRelayerWallet.Address, chainARelayerWallet.Address) - s.Require().NoError(err) - s.AssertValidTxResponse(multiMsgTxResponse) - }) - - t.Run("verify counter party payee", func(t *testing.T) { - address, err := s.QueryCounterPartyPayee(ctx, chainB, chainBRelayerWallet.Address, channelA.Counterparty.ChannelID) - s.Require().NoError(err) - s.Require().Equal(chainARelayerWallet.Address, address) - }) - - t.Run("no incentivized packets", func(t *testing.T) { - packets, err := s.QueryIncentivizedPacketsForChannel(ctx, chainA, channelA.PortID, channelA.ChannelID) - s.Require().NoError(err) - s.Require().Empty(packets) - }) - - payPacketFeeMsg := feetypes.NewMsgPayPacketFee(testFee, channelA.PortID, channelA.ChannelID, chainAWallet.Bech32Address(chainA.Config().Bech32Prefix), nil) - transferMsg := transfertypes.NewMsgTransfer(channelA.PortID, channelA.ChannelID, transferAmount, chainAWallet.Bech32Address(chainA.Config().Bech32Prefix), chainBWallet.Bech32Address(chainB.Config().Bech32Prefix), clienttypes.NewHeight(1, 1000), 0) - resp, err := s.BroadcastMessages(ctx, chainA, chainAWallet, payPacketFeeMsg, transferMsg) - - t.Run("transfer successful", func(t *testing.T) { - s.AssertValidTxResponse(resp) - s.Require().NoError(err) - }) - - t.Run("there should be incentivized packets", func(t *testing.T) { - packets, err := s.QueryIncentivizedPacketsForChannel(ctx, chainA, channelA.PortID, channelA.ChannelID) - s.Require().NoError(err) - s.Require().Len(packets, 1) - actualFee := packets[0].PacketFees[0].Fee - - s.Require().True(actualFee.RecvFee.IsEqual(testFee.RecvFee)) - s.Require().True(actualFee.AckFee.IsEqual(testFee.AckFee)) - s.Require().True(actualFee.TimeoutFee.IsEqual(testFee.TimeoutFee)) - }) - - t.Run("balance should be lowered by sum of recv ack and timeout", func(t *testing.T) { - // The balance should be lowered by the sum of the recv, ack and timeout fees. - actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet) - s.Require().NoError(err) - - expected := testvalues.StartingTokenAmount - testvalues.IBCTransferAmount - testFee.Total().AmountOf(chainADenom).Int64() - s.Require().Equal(expected, actualBalance) - }) - - t.Run("start relayer", func(t *testing.T) { - s.StartRelayer(relayer) - }) - - t.Run("packets are relayed", func(t *testing.T) { - packets, err := s.QueryIncentivizedPacketsForChannel(ctx, chainA, channelA.PortID, channelA.ChannelID) - s.Require().NoError(err) - s.Require().Empty(packets) - }) - - t.Run("timeout fee is refunded", func(t *testing.T) { - actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet) - s.Require().NoError(err) - - // once the relayer has relayed the packets, the timeout fee should be refunded. - expected := testvalues.StartingTokenAmount - testvalues.IBCTransferAmount - testFee.AckFee.AmountOf(chainADenom).Int64() - testFee.RecvFee.AmountOf(chainADenom).Int64() - s.Require().Equal(expected, actualBalance) - }) - - t.Run("relayerA is paid ack and recv fee", func(t *testing.T) { - actualBalance, err := s.GetChainANativeBalance(ctx, chainARelayerUser) - s.Require().NoError(err) - expected := relayerAStartingBalance + testFee.AckFee.AmountOf(chainADenom).Int64() + testFee.RecvFee.AmountOf(chainADenom).Int64() - s.Require().Equal(expected, actualBalance) - }) -} - -func (s *FeeMiddlewareTestSuite) TestMsgPayPacketFee_SingleSender_TimesOut() { - t := s.T() - ctx := context.TODO() - - relayer, channelA := s.SetupChainsRelayerAndChannel(ctx, feeMiddlewareChannelOptions()) - chainA, chainB := s.GetChains() - - var ( - chainADenom = chainA.Config().Denom - testFee = testvalues.DefaultFee(chainADenom) - chainATx ibc.Tx - payPacketFeeTxResp sdk.TxResponse - ) - - chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) - chainBWallet := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount) - - t.Run("relayer wallets recovered", func(t *testing.T) { - s.Require().NoError(s.RecoverRelayerWallets(ctx, relayer)) - }) - - chainARelayerWallet, chainBRelayerWallet, err := s.GetRelayerWallets(relayer) - t.Run("relayer wallets fetched", func(t *testing.T) { - s.Require().NoError(err) - }) - - _, chainBRelayerUser := s.GetRelayerUsers(ctx) - - t.Run("register counter party payee", func(t *testing.T) { - resp, err := s.RegisterCounterPartyPayee(ctx, chainB, chainBRelayerUser, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, chainBRelayerWallet.Address, chainARelayerWallet.Address) - s.Require().NoError(err) - s.AssertValidTxResponse(resp) - }) - - t.Run("verify counter party payee", func(t *testing.T) { - address, err := s.QueryCounterPartyPayee(ctx, chainB, chainBRelayerWallet.Address, channelA.Counterparty.ChannelID) - s.Require().NoError(err) - s.Require().Equal(chainARelayerWallet.Address, address) - }) - - chainBWalletAmount := ibc.WalletAmount{ - Address: chainBWallet.Bech32Address(chainB.Config().Bech32Prefix), // destination address - Denom: chainA.Config().Denom, - Amount: testvalues.IBCTransferAmount, - } - - t.Run("Send IBC transfer", func(t *testing.T) { - chainATx, err = chainA.SendIBCTransfer(ctx, channelA.ChannelID, chainAWallet.KeyName, chainBWalletAmount, testvalues.ImmediatelyTimeout()) - s.Require().NoError(err) - s.Require().NoError(chainATx.Validate(), "source ibc transfer tx is invalid") - time.Sleep(time.Nanosecond * 1) // want it to timeout immediately - }) - - t.Run("tokens are escrowed", func(t *testing.T) { - actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet) - s.Require().NoError(err) - - expected := testvalues.StartingTokenAmount - chainBWalletAmount.Amount - s.Require().Equal(expected, actualBalance) - }) - - t.Run("pay packet fee", func(t *testing.T) { - - packetId := channeltypes.NewPacketId(channelA.PortID, channelA.ChannelID, 1) - packetFee := feetypes.NewPacketFee(testFee, chainAWallet.Bech32Address(chainA.Config().Bech32Prefix), nil) - - t.Run("no incentivized packets", func(t *testing.T) { - packets, err := s.QueryIncentivizedPacketsForChannel(ctx, chainA, channelA.PortID, channelA.ChannelID) - s.Require().NoError(err) - s.Require().Empty(packets) - }) - - t.Run("should succeed", func(t *testing.T) { - payPacketFeeTxResp, err = s.PayPacketFeeAsync(ctx, chainA, chainAWallet, packetId, packetFee) - s.Require().NoError(err) - s.AssertValidTxResponse(payPacketFeeTxResp) - }) - - t.Run("there should be incentivized packets", func(t *testing.T) { - packets, err := s.QueryIncentivizedPacketsForChannel(ctx, chainA, channelA.PortID, channelA.ChannelID) - s.Require().NoError(err) - s.Require().Len(packets, 1) - actualFee := packets[0].PacketFees[0].Fee - - s.Require().True(actualFee.RecvFee.IsEqual(testFee.RecvFee)) - s.Require().True(actualFee.AckFee.IsEqual(testFee.AckFee)) - s.Require().True(actualFee.TimeoutFee.IsEqual(testFee.TimeoutFee)) - }) - - t.Run("balance should be lowered by sum of recv ack and timeout", func(t *testing.T) { - // The balance should be lowered by the sum of the recv, ack and timeout fees. - actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet) - s.Require().NoError(err) - - expected := testvalues.StartingTokenAmount - chainBWalletAmount.Amount - testFee.Total().AmountOf(chainADenom).Int64() - s.Require().Equal(expected, actualBalance) - }) - - }) - - t.Run("start relayer", func(t *testing.T) { - s.StartRelayer(relayer) - }) - - t.Run("packets are relayed", func(t *testing.T) { - packets, err := s.QueryIncentivizedPacketsForChannel(ctx, chainA, channelA.PortID, channelA.ChannelID) - s.Require().NoError(err) - s.Require().Empty(packets) - }) - - t.Run("recv and ack should be refunded", func(t *testing.T) { - actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet) - s.Require().NoError(err) - - expected := testvalues.StartingTokenAmount - testFee.TimeoutFee.AmountOf(chainADenom).Int64() - s.Require().Equal(expected, actualBalance) - }) -} - -// feeMiddlewareChannelOptions configures both of the chains to have fee middleware enabled. -func feeMiddlewareChannelOptions() func(options *ibc.CreateChannelOptions) { - return func(opts *ibc.CreateChannelOptions) { - opts.Version = "{\"fee_version\":\"ics29-1\",\"app_version\":\"ics20-1\"}" - opts.DestPortName = "transfer" - opts.SourcePortName = "transfer" - } -} diff --git a/e2e/go.mod b/e2e/go.mod index feb8f7f43c1..cb2b3872974 100644 --- a/e2e/go.mod +++ b/e2e/go.mod @@ -1,61 +1,104 @@ -module e2e +module github.com/cosmos/ibc-go/e2e -go 1.18 - -replace github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alpha.regen.1 +go 1.19 require ( - github.com/cosmos/cosmos-sdk v0.45.6 - github.com/cosmos/ibc-go/v4 v4.0.0-rc0 - github.com/docker/docker v20.10.17+incompatible - github.com/strangelove-ventures/ibctest v0.0.0-20220713213153-930886d8db30 - github.com/stretchr/testify v1.8.0 - go.uber.org/zap v1.21.0 - google.golang.org/grpc v1.47.0 + github.com/cosmos/cosmos-sdk v0.47.0-rc2 + github.com/cosmos/gogoproto v1.4.6 + github.com/cosmos/ibc-go/v7 v7.0.0-20230120105519-ae96bf3d5ee9 + github.com/cosmos/interchain-accounts v0.4.1-0.20230116203650-08d2a4529a5d + github.com/docker/docker v20.10.19+incompatible + github.com/strangelove-ventures/interchaintest/v7 v7.0.0-20230201060741-e0e4e0f9283b + github.com/stretchr/testify v1.8.2 + github.com/tendermint/tendermint v0.37.0-rc2 + go.uber.org/zap v1.23.0 + golang.org/x/mod v0.8.0 + google.golang.org/grpc v1.53.0 ) require ( - filippo.io/edwards25519 v1.0.0-beta.2 // indirect - github.com/99designs/keyring v1.1.6 // indirect - github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d // indirect - github.com/DataDog/zstd v1.4.5 // indirect - github.com/Microsoft/go-winio v0.5.1 // indirect - github.com/armon/go-metrics v0.4.0 // indirect + cloud.google.com/go v0.107.0 // indirect + cloud.google.com/go/compute v1.15.1 // indirect + cloud.google.com/go/compute/metadata v0.2.3 // indirect + cloud.google.com/go/iam v0.8.0 // indirect + cloud.google.com/go/storage v1.27.0 // indirect + cosmossdk.io/api v0.3.1 // indirect + cosmossdk.io/core v0.3.2 // indirect + cosmossdk.io/depinject v1.0.0-alpha.3 // indirect + cosmossdk.io/errors v1.0.0-beta.7 // indirect + cosmossdk.io/math v1.0.0-beta.6 // indirect + cosmossdk.io/tools/rosetta v0.2.0 // indirect + filippo.io/edwards25519 v1.0.0-rc.1 // indirect + github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect + github.com/99designs/keyring v1.2.1 // indirect + github.com/BurntSushi/toml v1.2.1 // indirect + github.com/ChainSafe/go-schnorrkel v1.0.0 // indirect + github.com/ChainSafe/go-schnorrkel/1 v0.0.0-00010101000000-000000000000 // indirect + github.com/Microsoft/go-winio v0.6.0 // indirect + github.com/StirlingMarketingGroup/go-namecase v1.0.0 // indirect + github.com/armon/go-metrics v0.4.1 // indirect github.com/avast/retry-go/v4 v4.0.4 // indirect - github.com/benbjohnson/clock v1.1.0 // indirect + github.com/aws/aws-sdk-go v1.40.45 // indirect + github.com/benbjohnson/clock v1.3.0 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/bgentry/speakeasy v0.1.0 // indirect - github.com/btcsuite/btcd v0.22.0-beta // indirect + github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect + github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 // indirect + github.com/btcsuite/btcd v0.22.3 // indirect + github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect + github.com/cenkalti/backoff/v4 v4.1.3 // indirect + github.com/centrifuge/go-substrate-rpc-client/v4 v4.0.10 // indirect github.com/cespare/xxhash v1.1.0 // indirect - github.com/cespare/xxhash/v2 v2.1.2 // indirect - github.com/confio/ics23/go v0.7.0 // indirect - github.com/cosmos/btcutil v1.0.4 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e // indirect + github.com/cockroachdb/apd/v2 v2.0.2 // indirect + github.com/confio/ics23/go v0.9.0 // indirect + github.com/cosmos/btcutil v1.0.5 // indirect + github.com/cosmos/cosmos-proto v1.0.0-beta.2 // indirect github.com/cosmos/go-bip39 v1.0.0 // indirect - github.com/cosmos/iavl v0.17.3 // indirect - github.com/cosmos/ledger-cosmos-go v0.11.1 // indirect - github.com/cosmos/ledger-go v0.9.2 // indirect - github.com/danieljoos/wincred v1.0.2 // indirect + github.com/cosmos/gogogateway v1.2.0 // indirect + github.com/cosmos/gorocksdb v1.2.0 // indirect + github.com/cosmos/iavl v0.19.5-rc.1 // indirect + github.com/cosmos/ics23/go v0.9.1-0.20221207100636-b1abd8678aab // indirect + github.com/cosmos/ledger-cosmos-go v0.12.1 // indirect + github.com/cosmos/rosetta-sdk-go v0.9.0 // indirect + github.com/creachadair/taskgroup v0.3.2 // indirect + github.com/danieljoos/wincred v1.1.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/dgraph-io/badger/v2 v2.2007.2 // indirect - github.com/dgraph-io/ristretto v0.0.3 // indirect + github.com/deckarep/golang-set v1.8.0 // indirect + github.com/decred/base58 v1.0.4 // indirect + github.com/decred/dcrd/crypto/blake256 v1.0.0 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v2 v2.0.0 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect + github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect + github.com/dgraph-io/badger/v2 v2.2007.4 // indirect + github.com/dgraph-io/ristretto v0.1.1 // indirect github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect github.com/docker/distribution v2.8.1+incompatible // indirect github.com/docker/go-connections v0.4.0 // indirect - github.com/docker/go-units v0.4.0 // indirect + github.com/docker/go-units v0.5.0 // indirect github.com/dustin/go-humanize v1.0.0 // indirect - github.com/dvsekhvalnov/jose2go v0.0.0-20200901110807-248326c1351b // indirect - github.com/felixge/httpsnoop v1.0.1 // indirect - github.com/fsnotify/fsnotify v1.5.4 // indirect + github.com/dvsekhvalnov/jose2go v1.5.0 // indirect + github.com/ethereum/go-ethereum v1.10.21 // indirect + github.com/felixge/httpsnoop v1.0.2 // indirect + github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/go-kit/kit v0.12.0 // indirect - github.com/go-kit/log v0.2.0 // indirect + github.com/go-kit/log v0.2.1 // indirect github.com/go-logfmt/logfmt v0.5.1 // indirect + github.com/go-stack/stack v1.8.1 // indirect github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect - github.com/gogo/gateway v1.1.0 // indirect + github.com/gogo/googleapis v1.4.1-0.20201022092350-68b0159b7869 // indirect github.com/gogo/protobuf v1.3.3 // indirect + github.com/golang/glog v1.0.0 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/golang/mock v1.6.0 // indirect github.com/golang/protobuf v1.5.2 // indirect - github.com/golang/snappy v0.0.3 // indirect - github.com/google/btree v1.0.0 // indirect + github.com/golang/snappy v0.0.4 // indirect + github.com/google/btree v1.1.2 // indirect + github.com/google/go-cmp v0.5.9 // indirect + github.com/google/orderedcode v0.0.1 // indirect github.com/google/uuid v1.3.0 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.2.1 // indirect + github.com/googleapis/gax-go/v2 v2.7.0 // indirect github.com/gorilla/handlers v1.5.1 // indirect github.com/gorilla/mux v1.8.0 // indirect github.com/gorilla/websocket v1.5.0 // indirect @@ -64,71 +107,107 @@ require ( github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect github.com/gtank/merlin v0.1.1 // indirect github.com/gtank/ristretto255 v0.1.2 // indirect + github.com/hashicorp/go-cleanhttp v0.5.2 // indirect + github.com/hashicorp/go-getter v1.6.2 // indirect github.com/hashicorp/go-immutable-radix v1.3.1 // indirect - github.com/hashicorp/golang-lru v0.5.4 // indirect + github.com/hashicorp/go-safetemp v1.0.0 // indirect + github.com/hashicorp/go-version v1.6.0 // indirect + github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d // indirect github.com/hashicorp/hcl v1.0.0 // indirect - github.com/hdevalence/ed25519consensus v0.0.0-20210204194344-59a8610d2b87 // indirect - github.com/inconshreveable/mousetrap v1.0.0 // indirect + github.com/hdevalence/ed25519consensus v0.0.0-20220222234857-c00d1f31bab3 // indirect + github.com/huandu/skiplist v1.2.0 // indirect + github.com/icza/dyno v0.0.0-20220812133438-f0b6f8a18845 // indirect + github.com/improbable-eng/grpc-web v0.15.0 // indirect + github.com/inconshreveable/mousetrap v1.0.1 // indirect + github.com/ipfs/go-cid v0.0.7 // indirect + github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/jmhodges/levigo v1.0.0 // indirect github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect - github.com/keybase/go-keychain v0.0.0-20190712205309-48d3d31d256d // indirect - github.com/libp2p/go-buffer-pool v0.0.2 // indirect - github.com/magiconair/properties v1.8.6 // indirect - github.com/mattn/go-isatty v0.0.14 // indirect + github.com/klauspost/compress v1.15.12 // indirect + github.com/klauspost/cpuid/v2 v2.0.9 // indirect + github.com/lib/pq v1.10.7 // indirect + github.com/libp2p/go-buffer-pool v0.1.0 // indirect + github.com/libp2p/go-libp2p-core v0.15.1 // indirect + github.com/libp2p/go-openssl v0.0.7 // indirect + github.com/magiconair/properties v1.8.7 // indirect + github.com/manifoldco/promptui v0.9.0 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.16 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect - github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643 // indirect + github.com/mimoo/StrobeGo v0.0.0-20220103164710-9a04d6ca976b // indirect + github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 // indirect + github.com/minio/highwayhash v1.0.2 // indirect + github.com/minio/sha256-simd v1.0.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/mitchellh/go-testing-interface v1.0.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/mr-tron/base58 v1.2.0 // indirect github.com/mtibben/percent v0.2.1 // indirect + github.com/multiformats/go-base32 v0.0.3 // indirect + github.com/multiformats/go-base36 v0.1.0 // indirect + github.com/multiformats/go-multiaddr v0.4.1 // indirect + github.com/multiformats/go-multibase v0.0.3 // indirect + github.com/multiformats/go-multicodec v0.4.1 // indirect + github.com/multiformats/go-multihash v0.1.0 // indirect + github.com/multiformats/go-varint v0.0.6 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect - github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 // indirect - github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pelletier/go-toml/v2 v2.0.1 // indirect + github.com/opencontainers/image-spec v1.1.0-rc2 // indirect + github.com/pelletier/go-toml/v2 v2.0.6 // indirect github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 // indirect + github.com/pierrec/xxHash v0.1.5 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_golang v1.12.1 // indirect - github.com/prometheus/client_model v0.2.0 // indirect - github.com/prometheus/common v0.32.1 // indirect - github.com/prometheus/procfs v0.7.3 // indirect + github.com/prometheus/client_golang v1.14.0 // indirect + github.com/prometheus/client_model v0.3.0 // indirect + github.com/prometheus/common v0.37.0 // indirect + github.com/prometheus/procfs v0.8.0 // indirect github.com/rakyll/statik v0.1.7 // indirect - github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 // indirect - github.com/regen-network/cosmos-proto v0.3.1 // indirect + github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect - github.com/sasha-s/go-deadlock v0.2.1-0.20190427202633-1595213edefa // indirect - github.com/sirupsen/logrus v1.8.1 // indirect - github.com/spf13/afero v1.8.2 // indirect + github.com/rs/cors v1.8.2 // indirect + github.com/rs/zerolog v1.28.0 // indirect + github.com/sasha-s/go-deadlock v0.3.1 // indirect + github.com/sirupsen/logrus v1.9.0 // indirect + github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 // indirect + github.com/spaolacci/murmur3 v1.1.0 // indirect + github.com/spf13/afero v1.9.3 // indirect github.com/spf13/cast v1.5.0 // indirect - github.com/spf13/cobra v1.5.0 // indirect + github.com/spf13/cobra v1.6.1 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/spf13/viper v1.12.0 // indirect - github.com/subosito/gotenv v1.3.0 // indirect - github.com/syndtr/goleveldb v1.0.1-0.20200815110645-5c35d600f0ca // indirect - github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c // indirect - github.com/tendermint/btcd v0.1.1 // indirect - github.com/tendermint/crypto v0.0.0-20191022145703-50d29ede1e15 // indirect + github.com/spf13/viper v1.15.0 // indirect + github.com/subosito/gotenv v1.4.2 // indirect + github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a // indirect github.com/tendermint/go-amino v0.16.0 // indirect - github.com/tendermint/tendermint v0.34.19 // indirect - github.com/tendermint/tm-db v0.6.6 // indirect - github.com/zondax/hid v0.9.0 // indirect + github.com/tendermint/tm-db v0.6.7 // indirect + github.com/tidwall/btree v1.5.2 // indirect + github.com/ulikunitz/xz v0.5.8 // indirect + github.com/vedhavyas/go-subkey v1.0.3 // indirect + github.com/zondax/hid v0.9.1 // indirect + github.com/zondax/ledger-go v0.14.0 // indirect go.etcd.io/bbolt v1.3.6 // indirect - go.uber.org/atomic v1.9.0 // indirect - go.uber.org/multierr v1.7.0 // indirect - golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 // indirect - golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect - golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2 // indirect - golang.org/x/sync v0.0.0-20220513210516-0976fa681c29 // indirect - golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect - golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect - golang.org/x/text v0.3.7 // indirect - golang.org/x/tools v0.1.10 // indirect - golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df // indirect - google.golang.org/genproto v0.0.0-20220519153652-3a47de7e79bd // indirect - google.golang.org/protobuf v1.28.0 // indirect - gopkg.in/ini.v1 v1.66.4 // indirect + go.opencensus.io v0.24.0 // indirect + go.uber.org/atomic v1.10.0 // indirect + go.uber.org/multierr v1.8.0 // indirect + golang.org/x/crypto v0.4.0 // indirect + golang.org/x/exp v0.0.0-20230131160201-f062dba9d201 // indirect + golang.org/x/net v0.6.0 // indirect + golang.org/x/oauth2 v0.4.0 // indirect + golang.org/x/sync v0.1.0 // indirect + golang.org/x/sys v0.5.0 // indirect + golang.org/x/term v0.5.0 // indirect + golang.org/x/text v0.7.0 // indirect + golang.org/x/tools v0.6.0 // indirect + golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect + google.golang.org/api v0.107.0 // indirect + google.golang.org/appengine v1.6.7 // indirect + google.golang.org/genproto v0.0.0-20230202175211-008b39050e57 // indirect + google.golang.org/protobuf v1.28.2-0.20220831092852-f930b1dc76e8 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect + lukechampine.com/blake3 v1.1.6 // indirect lukechampine.com/uint128 v1.1.1 // indirect modernc.org/cc/v3 v3.36.0 // indirect modernc.org/ccgo/v3 v3.16.6 // indirect @@ -139,4 +218,19 @@ require ( modernc.org/sqlite v1.17.3 // indirect modernc.org/strutil v1.1.1 // indirect modernc.org/token v1.0.0 // indirect + nhooyr.io/websocket v1.8.6 // indirect + pgregory.net/rapid v0.5.5 // indirect + sigs.k8s.io/yaml v1.3.0 // indirect +) + +// TODO: using version v1.0.0 causes a build failure. This is the previous version which compiles successfully. +replace ( + github.com/ChainSafe/go-schnorrkel => github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d + github.com/ChainSafe/go-schnorrkel/1 => github.com/ChainSafe/go-schnorrkel v1.0.0 + github.com/vedhavyas/go-subkey => github.com/strangelove-ventures/go-subkey v1.0.7 ) + +// uncomment to use the local version of ibc-go, you will need to run `go mod tidy` in e2e directory. +replace github.com/cosmos/ibc-go/v7 => ../ + +replace github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alpha.regen.1 diff --git a/e2e/go.sum b/e2e/go.sum index 6d00db2a409..f6490f008d2 100644 --- a/e2e/go.sum +++ b/e2e/go.sum @@ -1,4 +1,3 @@ -bazil.org/fuse v0.0.0-20200407214033-5883e5a4b512/go.mod h1:FbcW6z/2VytnFDhZfumh8Ss8zxHE6qpMP5sHTRe0EaM= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= @@ -18,27 +17,23 @@ cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHOb cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= -cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= -cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= -cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= -cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= -cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= -cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= -cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= -cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= -cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= -cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= -cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= +cloud.google.com/go v0.107.0 h1:qkj22L7bgkl6vIeZDlOY2po43Mx/TIa2Wsa7VR+PEww= +cloud.google.com/go v0.107.0/go.mod h1:wpc2eNrD7hXUTy8EKS10jkxpZBjASrORK7goS+3YX2I= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/compute v1.15.1 h1:7UGq3QknM33pw5xATlpzeoomNxsacIVvTqTTvbfajmE= +cloud.google.com/go/compute v1.15.1/go.mod h1:bjjoF/NtFUrkD/urWfdHaKuOPDR5nWIs63rR+SXhcpA= +cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= +cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= -cloud.google.com/go/firestore v1.6.1/go.mod h1:asNXNOzBdyVQmEU+ggO8UPodTkEVFW5Qx+rwHnAz+EY= +cloud.google.com/go/iam v0.8.0 h1:E2osAkZzxI/+8pZcxVLcDtAQx/u+hZXVryUaYQ5O0Kk= +cloud.google.com/go/iam v0.8.0/go.mod h1:lga0/y3iH6CX7sYqypWJ33hf7kkfXJag67naqGESjkE= +cloud.google.com/go/longrunning v0.3.0 h1:NjljC+FYPV3uh5/OwWT6pVU+doBqMg2x/rZlE+CamDs= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= @@ -49,49 +44,53 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= +cloud.google.com/go/storage v1.27.0 h1:YOO045NZI9RKfCj1c5A/ZtuuENUc8OAW+gHdGnDgyMQ= +cloud.google.com/go/storage v1.27.0/go.mod h1:x9DOL8TK/ygDUMieqwfhdpQryTeEkhGKMi80i/iqR2s= +cosmossdk.io/api v0.3.1 h1:NNiOclKRR0AOlO4KIqeaG6PS6kswOMhHD0ir0SscNXE= +cosmossdk.io/api v0.3.1/go.mod h1:DfHfMkiNA2Uhy8fj0JJlOCYOBp4eWUUJ1te5zBGNyIw= +cosmossdk.io/core v0.3.2 h1:KlQIufpJHJvOs7YLGTZsZcCo1WlkencDXepsr8STKZQ= +cosmossdk.io/core v0.3.2/go.mod h1:CO7vbe+evrBvHc0setFHL/u7nlY7HJGzdRSBkT/sirc= +cosmossdk.io/depinject v1.0.0-alpha.3 h1:6evFIgj//Y3w09bqOUOzEpFj5tsxBqdc5CfkO7z+zfw= +cosmossdk.io/depinject v1.0.0-alpha.3/go.mod h1:eRbcdQ7MRpIPEM5YUJh8k97nxHpYbc3sMUnEtt8HPWU= +cosmossdk.io/errors v1.0.0-beta.7 h1:gypHW76pTQGVnHKo6QBkb4yFOJjC+sUGRc5Al3Odj1w= +cosmossdk.io/errors v1.0.0-beta.7/go.mod h1:mz6FQMJRku4bY7aqS/Gwfcmr/ue91roMEKAmDUDpBfE= +cosmossdk.io/math v1.0.0-beta.6 h1:WF29SiFYNde5eYvqO2kdOM9nYbDb44j3YW5B8M1m9KE= +cosmossdk.io/math v1.0.0-beta.6/go.mod h1:gUVtWwIzfSXqcOT+lBVz2jyjfua8DoBdzRsIyaUAT/8= +cosmossdk.io/tools/rosetta v0.2.0 h1:Ae499UiZ9yPNCXvjOBO/R9I1pksCJfxoqWauEZgA/gs= +cosmossdk.io/tools/rosetta v0.2.0/go.mod h1:3mn8QuE2wLUdTi77/gbDXdFqXZdBdiBJhgAWUTSXPv8= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -filippo.io/edwards25519 v1.0.0-beta.2 h1:/BZRNzm8N4K4eWfK28dL4yescorxtO7YG1yun8fy+pI= -filippo.io/edwards25519 v1.0.0-beta.2/go.mod h1:X+pm78QAUPtFLi1z9PYIlS/bdDnvbCOGKtZ+ACWEf7o= -github.com/99designs/keyring v1.1.6 h1:kVDC2uCgVwecxCk+9zoCt2uEL6dt+dfVzMvGgnVcIuM= -github.com/99designs/keyring v1.1.6/go.mod h1:16e0ds7LGQQcT59QqkTg72Hh5ShM51Byv5PEmW6uoRU= -github.com/Azure/azure-sdk-for-go/sdk/azcore v0.19.0/go.mod h1:h6H6c8enJmmocHUbLiiGY6sx7f9i+X3m1CHdd5c6Rdw= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v0.11.0/go.mod h1:HcM1YX14R7CJcghJGOYCgdezslRSVzqwLf/q+4Y2r/0= -github.com/Azure/azure-sdk-for-go/sdk/internal v0.7.0/go.mod h1:yqy467j36fJxcRV2TzfVZ1pCb5vxm4BtZPUdYWe/Xo8= -github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= +filippo.io/edwards25519 v1.0.0-rc.1 h1:m0VOOB23frXZvAOK44usCgLWvtsxIoMCTBGJZlpmGfU= +filippo.io/edwards25519 v1.0.0-rc.1/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= +github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 h1:/vQbFIOMbk2FiG/kXiLl8BRyzTWDw7gX/Hz7Dd5eDMs= +github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4/go.mod h1:hN7oaIRCjzsZ2dE+yG5k+rsdt3qcwykqK6HVGcKwsw4= +github.com/99designs/keyring v1.2.1 h1:tYLp1ULvO7i3fI5vE21ReQuj99QFSs7lGm0xWyJo87o= +github.com/99designs/keyring v1.2.1/go.mod h1:fc+wB5KTk9wQ9sDx0kFXB3A0MaeGHM9AwRStKOQ5vOA= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= +github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d h1:nalkkPQcITbvhmL4+C4cKA87NW0tfm3Kl9VXRoPywFg= github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d/go.mod h1:URdX5+vg25ts3aCh8H5IFZybJYKWhJHYMTnf+ULtoC4= -github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= +github.com/ChainSafe/go-schnorrkel v1.0.0 h1:3aDA67lAykLaG1y3AOjs88dMxC88PgUuHRrLeDnvGIM= +github.com/ChainSafe/go-schnorrkel v1.0.0/go.mod h1:dpzHYVxLZcp8pjlV+O+UR8K0Hp/z7vcchBSbMBEhCw4= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= -github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= -github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= -github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= -github.com/HdrHistogram/hdrhistogram-go v1.1.0/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= -github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= -github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= -github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= -github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= +github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= +github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= +github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= +github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 h1:fLjPD/aNc3UIOA6tDi6QXUemppXK3P9BI7mr2hd6gx8= +github.com/StirlingMarketingGroup/go-namecase v1.0.0 h1:2CzaNtCzc4iNHirR+5ru9OzGg8rQp860gqLBFqRI02Y= +github.com/StirlingMarketingGroup/go-namecase v1.0.0/go.mod h1:ZsoSKcafcAzuBx+sndbxHu/RjDcDTrEdT4UvhniHfio= github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= -github.com/Workiva/go-datastructures v1.0.52/go.mod h1:Z+F2Rca0qCsVYDS8z7bAGm8f3UkzuWYS/oBZz5a7VVA= -github.com/Workiva/go-datastructures v1.0.53 h1:J6Y/52yX10Xc5JjXmGtWoSSxs3mZnGSaq37xZZh7Yig= -github.com/Workiva/go-datastructures v1.0.53/go.mod h1:1yZL+zfsztete+ePzZz/Zb1/t5BnDuE2Ya2MMGhzP6A= -github.com/adlio/schema v1.1.13/go.mod h1:L5Z7tw+7lRK1Fnpi/LT/ooCP1elkXn0krMWBQHUhEDE= -github.com/adlio/schema v1.3.0/go.mod h1:51QzxkpeFs6lRY11kPye26IaFPOV+HqEj01t5aXXKfs= -github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= +github.com/adlio/schema v1.3.3 h1:oBJn8I02PyTB466pZO1UZEn1TV5XLlifBSyMrmHl/1I= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= -github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= +github.com/alecthomas/participle/v2 v2.0.0-alpha7 h1:cK4vjj0VSgb3lN1nuKA5F7dw+1s1pWBe5bx7nNCnN+c= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= @@ -103,157 +102,159 @@ github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-metrics v0.3.9/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= -github.com/armon/go-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= -github.com/armon/go-metrics v0.4.0 h1:yCQqn7dwca4ITXb+CbubHmedzaQYHhNhrEXLYUeEe8Q= -github.com/armon/go-metrics v0.4.0/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= +github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA= +github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= github.com/avast/retry-go/v4 v4.0.4 h1:38hLf0DsRXh+hOF6HbTni0+5QGTNdw9zbaMD7KAO830= github.com/avast/retry-go/v4 v4.0.4/go.mod h1:HqmLvS2VLdStPCGDFjSuZ9pzlTqVRldCI4w2dO4m1Ms= github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= +github.com/aws/aws-sdk-go v1.15.78/go.mod h1:E3/ieXAlvM0XWO57iftYVDLLvQ824smPP3ATZkfNZeM= github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go v1.40.45 h1:QN1nsY27ssD/JmW4s83qmSb+uL6DG4GmCDzjmJB4xUI= github.com/aws/aws-sdk-go v1.40.45/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= -github.com/aws/aws-sdk-go-v2 v1.9.1/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4= -github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.8.1/go.mod h1:CM+19rL1+4dFWnOQKwDc7H1KwXTz+h61oUSHyhV0b3o= -github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= -github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= -github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= +github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= +github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas= +github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= -github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= -github.com/btcsuite/btcd v0.0.0-20190115013929-ed77733ec07d/go.mod h1:d3C0AkH6BRcvO8T0UEPu53cnw4IbV63x1bEjildYhO0= -github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= -github.com/btcsuite/btcd v0.21.0-beta/go.mod h1:ZSWyehm27aAuS9bvkATT+Xte3hjHZ+MRgMY/8NJ7K94= -github.com/btcsuite/btcd v0.22.0-beta h1:LTDpDKUM5EeOFBPM8IXpinEcmZ6FWfNZbE3lfrfdnWo= -github.com/btcsuite/btcd v0.22.0-beta/go.mod h1:9n5ntfhhHQBIhUvlhDvD3Qg6fRUj4jkN0VB8L8svzOA= -github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= -github.com/btcsuite/btcutil v0.0.0-20180706230648-ab6388e0c60a/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= -github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= -github.com/btcsuite/btcutil v1.0.2/go.mod h1:j9HUFwoQRsZL3V4n+qG+CUnEGHOarIxfC3Le2Yhbcts= +github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 h1:41iFGWnSlI2gVpmOtVTJZNodLdLQLn/KsJqFvXwnd/s= +github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/btcsuite/btcd v0.22.3 h1:kYNaWFvOw6xvqP0vR20RP1Zq1DVMBxEO8QN5d1/EfNg= +github.com/btcsuite/btcd v0.22.3/go.mod h1:wqgTSL29+50LRkmOVknEdmt8ZojIzhuWvgu/iptuN7Y= +github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U= +github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= +github.com/btcsuite/btcd/btcutil v1.1.2 h1:XLMbX8JQEiwMcYft2EGi8zPUkoa0abKIU6/BJSRsjzQ= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U= github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce h1:YtWJF7RHm2pYCvA5t0RPmAaLUhREsKuKd+SLhxFbFeQ= -github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce/go.mod h1:0DVlHczLPewLcPGEIeUEzfOJhqGPQ0mJJRDBtD307+o= -github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= -github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= -github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I= -github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= -github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= -github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= -github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= -github.com/casbin/casbin/v2 v2.37.0/go.mod h1:vByNa/Fchek0KZUgG5wEsl7iFsiviAYKRtgrQfcJqHg= +github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= +github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4= +github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/centrifuge/go-substrate-rpc-client/v4 v4.0.10 h1:HW3XP9G3mXr0gYPfxCAQLD29u+Ys0uIeotv9RWfnhrM= +github.com/centrifuge/go-substrate-rpc-client/v4 v4.0.10/go.mod h1:5g1oM4Zu3BOaLpsKQ+O8PAv2kNuq+kPcA1VzFbsSqxE= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cheggaaa/pb v1.0.27/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s= +github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= -github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng= github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cockroachdb/apd/v2 v2.0.2 h1:weh8u7Cneje73dDh+2tEVLUvyBc89iwepWCD8b8034E= +github.com/cockroachdb/apd/v2 v2.0.2/go.mod h1:DDxRlzC2lo3/vSlmSoS7JkqbbrARPuFOGr0B9pvN3Gw= +github.com/cockroachdb/apd/v3 v3.1.0 h1:MK3Ow7LH0W8zkd5GMKA1PvS9qG3bWFI95WaVNfyZJ/w= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= -github.com/coinbase/rosetta-sdk-go v0.7.0 h1:lmTO/JEpCvZgpbkOITL95rA80CPKb5CtMzLaqF2mCNg= -github.com/confio/ics23/go v0.6.6/go.mod h1:E45NqnlpxGnpfTWL/xauN7MRwEE28T4Dd4uraToOaKg= -github.com/confio/ics23/go v0.7.0 h1:00d2kukk7sPoHWL4zZBZwzxnpA2pec1NPdwbSokJ5w8= -github.com/confio/ics23/go v0.7.0/go.mod h1:E45NqnlpxGnpfTWL/xauN7MRwEE28T4Dd4uraToOaKg= -github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ= -github.com/containerd/continuity v0.0.0-20190827140505-75bee3e2ccb6/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= -github.com/containerd/continuity v0.2.1/go.mod h1:wCYX+dRqZdImhGucXOqTQn05AhX6EUDaGEMUzTFFpLg= -github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/confio/ics23/go v0.9.0 h1:cWs+wdbS2KRPZezoaaj+qBleXgUk5WOQFMP3CQFGTr4= +github.com/confio/ics23/go v0.9.0/go.mod h1:4LPZ2NYqnYIVRklaozjNR1FScgDJ2s5Xrp+e/mYVRak= +github.com/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvAXZJ/eg= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/cosmos/btcutil v1.0.4 h1:n7C2ngKXo7UC9gNyMNLbzqz7Asuf+7Qv4gnX/rOdQ44= -github.com/cosmos/btcutil v1.0.4/go.mod h1:Ffqc8Hn6TJUdDgHBwIZLtrLQC1KdJ9jGJl/TvgUaxbU= -github.com/cosmos/cosmos-sdk v0.45.6 h1:bnYLOcDp0cKWMLeUTTJIttq6xxRep52ulPxXC3BCfuQ= -github.com/cosmos/cosmos-sdk v0.45.6/go.mod h1:bPeeVMEtVvH3y3xAGHVbK+/CZlpaazzh77hG8ZrcJpI= +github.com/cosmos/btcutil v1.0.5 h1:t+ZFcX77LpKtDBhjucvnOH8C2l2ioGsBNEQ3jef8xFk= +github.com/cosmos/btcutil v1.0.5/go.mod h1:IyB7iuqZMJlthe2tkIFL33xPyzbFYP0XVdS8P5lUPis= +github.com/cosmos/cosmos-proto v1.0.0-beta.2 h1:X3OKvWgK9Gsejo0F1qs5l8Qn6xJV/AzgIWR2wZ8Nua8= +github.com/cosmos/cosmos-proto v1.0.0-beta.2/go.mod h1:+XRCLJ14pr5HFEHIUcn51IKXD1Fy3rkEQqt4WqmN4V0= +github.com/cosmos/cosmos-sdk v0.47.0-rc2 h1:BwQC41zQXG/pN9DdLaWzYJrC911St5lYOQIoW4Hf5wQ= +github.com/cosmos/cosmos-sdk v0.47.0-rc2/go.mod h1:e0ZEpY/nhVoXAkijdHPdFOJNOXCddfvyFrFLp2QmCCY= github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d/go.mod h1:tSxLoYXyBmiFeKpvmq4dzayMdCjCnu8uqmCysIGBT2Y= github.com/cosmos/go-bip39 v1.0.0 h1:pcomnQdrdH22njcAatO0yWojsUnCO3y2tNoV1cb6hHY= github.com/cosmos/go-bip39 v1.0.0/go.mod h1:RNJv0H/pOIVgxw6KS7QeX2a0Uo0aKUlfhZ4xuwvCdJw= -github.com/cosmos/iavl v0.17.3 h1:s2N819a2olOmiauVa0WAhoIJq9EhSXE9HDBAoR9k+8Y= -github.com/cosmos/iavl v0.17.3/go.mod h1:prJoErZFABYZGDHka1R6Oay4z9PrNeFFiMKHDAMOi4w= -github.com/cosmos/ibc-go/v4 v4.0.0-rc0 h1:zeMr6PNE7L300AcGkrMwRvtp62/RpGc7qU1LwhUcPKc= -github.com/cosmos/ibc-go/v4 v4.0.0-rc0/go.mod h1:4LK+uPycPhebJrJ8ebIqvsMEZ0lVRVNTiEyeI9zfB0U= -github.com/cosmos/ledger-cosmos-go v0.11.1 h1:9JIYsGnXP613pb2vPjFeMMjBI5lEDsEaF6oYorTy6J4= -github.com/cosmos/ledger-cosmos-go v0.11.1/go.mod h1:J8//BsAGTo3OC/vDLjMRFLW6q0WAaXvHnVc7ZmE8iUY= -github.com/cosmos/ledger-go v0.9.2 h1:Nnao/dLwaVTk1Q5U9THldpUMMXU94BOTWPddSmVB6pI= -github.com/cosmos/ledger-go v0.9.2/go.mod h1:oZJ2hHAZROdlHiwTg4t7kP+GKIIkBT+o6c9QWFanOyI= +github.com/cosmos/gogogateway v1.2.0 h1:Ae/OivNhp8DqBi/sh2A8a1D0y638GpL3tkmLQAiKxTE= +github.com/cosmos/gogogateway v1.2.0/go.mod h1:iQpLkGWxYcnCdz5iAdLcRBSw3h7NXeOkZ4GUkT+tbFI= +github.com/cosmos/gogoproto v1.4.2/go.mod h1:cLxOsn1ljAHSV527CHOtaIP91kK6cCrZETRBrkzItWU= +github.com/cosmos/gogoproto v1.4.6 h1:Ee7z15dWJaGlgM2rWrK8N2IX7PQcuccu8oG68jp5RL4= +github.com/cosmos/gogoproto v1.4.6/go.mod h1:VS/ASYmPgv6zkPKLjR9EB91lwbLHOzaGCirmKKhncfI= +github.com/cosmos/gorocksdb v1.2.0 h1:d0l3jJG8M4hBouIZq0mDUHZ+zjOx044J3nGRskwTb4Y= +github.com/cosmos/gorocksdb v1.2.0/go.mod h1:aaKvKItm514hKfNJpUJXnnOWeBnk2GL4+Qw9NHizILw= +github.com/cosmos/iavl v0.19.5-rc.1 h1:4PjF2PdScyPbN1WbXpiQU21YtyonnrMU31xN74g8Rkg= +github.com/cosmos/iavl v0.19.5-rc.1/go.mod h1:X9PKD3J0iFxdmgNLa7b2LYWdsGd90ToV5cAONApkEPw= +github.com/cosmos/ics23/go v0.9.1-0.20221207100636-b1abd8678aab h1:I9ialKTQo7248V827Bba4OuKPmk+FPzmTVHsLXaIJWw= +github.com/cosmos/ics23/go v0.9.1-0.20221207100636-b1abd8678aab/go.mod h1:2CwqasX5dSD7Hbp/9b6lhK6BwoBDCBldx7gPKRukR60= +github.com/cosmos/interchain-accounts v0.4.1-0.20230116203650-08d2a4529a5d h1:YR8OGEZKf0xveWZ2B/3+r9Q0dbtypXmwwAcolFNY8Fw= +github.com/cosmos/interchain-accounts v0.4.1-0.20230116203650-08d2a4529a5d/go.mod h1:O4DXede5LGQ7ycy4JLjAksuiku6PsFbP2K/x0KgAcuk= +github.com/cosmos/ledger-cosmos-go v0.12.1 h1:sMBxza5p/rNK/06nBSNmsI/WDqI0pVJFVNihy1Y984w= +github.com/cosmos/ledger-cosmos-go v0.12.1/go.mod h1:dhO6kj+Y+AHIOgAe4L9HL/6NDdyyth4q238I9yFpD2g= +github.com/cosmos/rosetta-sdk-go v0.9.0 h1:3mj2naR+GUhUXabtb96WWSsPFZDCYkdtp6r0jffgugg= +github.com/cosmos/rosetta-sdk-go v0.9.0/go.mod h1:2v41yXL25xxAXrczVSnbDHcQH9CgildruDlGQGKW/JU= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/creachadair/taskgroup v0.3.2 h1:zlfutDS+5XG40AOxcHDSThxKzns8Tnr9jnr6VqkYlkM= +github.com/creachadair/taskgroup v0.3.2/go.mod h1:wieWwecHVzsidg2CsUnFinW1faVN4+kq+TDlRJQ0Wbk= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= -github.com/danieljoos/wincred v1.0.2 h1:zf4bhty2iLuwgjgpraD2E9UbvO+fe54XXGJbOwe23fU= -github.com/danieljoos/wincred v1.0.2/go.mod h1:SnuYRW9lp1oJrZX/dXJqr0cPK5gYXqx3EJbmjhLdK9U= -github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/cucumber/common/gherkin/go/v22 v22.0.0 h1:4K8NqptbvdOrjL9DEea6HFjSpbdT9+Q5kgLpmmsHYl0= +github.com/cucumber/common/messages/go/v17 v17.1.1 h1:RNqopvIFyLWnKv0LfATh34SWBhXeoFTJnSrgm9cT/Ts= +github.com/danieljoos/wincred v1.1.2 h1:QLdCxFs1/Yl4zduvBdcHB8goaYk9RARS2SgLLRuAyr0= +github.com/danieljoos/wincred v1.1.2/go.mod h1:GijpziifJoIBfYh+S7BbkdUTU4LfM+QnGqR5Vl2tAx0= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= -github.com/denisenkom/go-mssqldb v0.12.0/go.mod h1:iiK0YP1ZeepvmBQk/QpLEhhTNJgfzrpArPY/aFvc9yU= +github.com/deckarep/golang-set v1.8.0 h1:sk9/l/KqpunDwP7pSjUg0keiOOLEnOBHzykLrsPppp4= +github.com/deckarep/golang-set v1.8.0/go.mod h1:5nI87KwE7wgsBU1F4GKAw2Qod7p5kyS383rP6+o6qqo= +github.com/decred/base58 v1.0.4 h1:QJC6B0E0rXOPA8U/kw2rP+qiRJsUaE2Er+pYb3siUeA= +github.com/decred/base58 v1.0.4/go.mod h1:jJswKPEdvpFpvf7dsDvFZyLT22xZ9lWqEByX38oGd9E= +github.com/decred/dcrd/chaincfg/chainhash v1.0.2 h1:rt5Vlq/jM3ZawwiacWjPa+smINyLRN07EO0cNBV6DGU= +github.com/decred/dcrd/chaincfg/chainhash v1.0.2/go.mod h1:BpbrGgrPTr3YJYRN3Bm+D9NuaFd+zGyNeIKgrhCXK60= +github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= +github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= +github.com/decred/dcrd/dcrec/secp256k1/v2 v2.0.0 h1:3GIJYXQDAKpLEFriGFN8SbSffak10UXHGdIcFaMPykY= +github.com/decred/dcrd/dcrec/secp256k1/v2 v2.0.0/go.mod h1:3s92l0paYkZoIHuj4X93Teg/HB7eGM9x/zokGw+u4mY= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 h1:HbphB4TFFXpv7MNrT52FGrrgVXF1owhMVTHFZIlnvd4= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0/go.mod h1:DZGJHZMqrU4JJqFAWUS2UO1+lbSKsdiOoYi9Zzey7Fc= github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f h1:U5y3Y5UE0w7amNe7Z5G/twsBW0KEalRQXZzf8ufSh9I= -github.com/dgraph-io/badger/v2 v2.2007.2 h1:EjjK0KqwaFMlPin1ajhP943VPENHJdEz1KLIegjaI3k= -github.com/dgraph-io/badger/v2 v2.2007.2/go.mod h1:26P/7fbL4kUZVEVKLAKXkBXKOydDmM2p1e+NhhnBCAE= +github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f/go.mod h1:xH/i4TFMt8koVQZ6WFms69WAsDWr2XsYL3Hkl7jkoLE= +github.com/dgraph-io/badger/v2 v2.2007.4 h1:TRWBQg8UrlUhaFdco01nO2uXwzKS7zd+HVdwV/GHc4o= +github.com/dgraph-io/badger/v2 v2.2007.4/go.mod h1:vSw/ax2qojzbN6eXHIx6KPKtCSHJN/Uz0X0VPruTIhk= github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= -github.com/dgraph-io/ristretto v0.0.3 h1:jh22xisGBjrEVnRZ1DVTpBVQm0Xndu8sMl0CWDzSIBI= -github.com/dgraph-io/ristretto v0.0.3/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= +github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8= +github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y= github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= -github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68= github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v20.10.17+incompatible h1:JYCuMrWaVNophQTOrMMoSwudOVEfcegoZZrleKc1xwE= -github.com/docker/docker v20.10.17+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v20.10.19+incompatible h1:lzEmjivyNHFHMNAFLXORMBXyGIhw/UP4DvJwvyKYq64= +github.com/docker/docker v20.10.19+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= -github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= -github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/dvsekhvalnov/jose2go v0.0.0-20200901110807-248326c1351b h1:HBah4D48ypg3J7Np4N+HY/ZR76fx3HEUGxDU6Uk39oQ= -github.com/dvsekhvalnov/jose2go v0.0.0-20200901110807-248326c1351b/go.mod h1:7BvyPhdbLxMXIYTFPLsyJRFMsKmOZnQmzh6Gb+uquuM= +github.com/dvsekhvalnov/jose2go v1.5.0 h1:3j8ya4Z4kMCwT5nXIKFSV84YS+HdqSSO0VsTQxaLAeM= +github.com/dvsekhvalnov/jose2go v1.5.0/go.mod h1:QsHjhyTlD/lAVqn/NSbVZmSCGeDehTB/mPZadG+mhXU= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= @@ -264,39 +265,32 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= -github.com/envoyproxy/go-control-plane v0.10.1/go.mod h1:AY7fTTXNdv/aJ2O5jwpxAPOWUZ7hQAEvzN5Pf27BkQQ= github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/envoyproxy/protoc-gen-validate v0.6.2/go.mod h1:2t7qjJNvHPx8IjnBOzl9E9/baC+qXE/TeeyBRzgJDws= -github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51 h1:0JZ+dUmQeA8IIVUMzysrX4/AKuQwWhV2dYQuPZdvdSQ= -github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64= +github.com/ethereum/go-ethereum v1.10.21 h1:5lqsEx92ZaZzRyOqBEXux4/UR06m296RGzN3ol3teJY= +github.com/ethereum/go-ethereum v1.10.21/go.mod h1:EYFyF19u3ezGLD4RqOkLq+ZCXzYbLoNDdZlMt7kyKFg= +github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c h1:8ISkoahWXwZR41ois5lSJBSVw4D0OV19Ht/JSTzvSv0= github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojtoVVWjGfOF9635RETekkoH6Cc9SX0A= -github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqLaRiH3MsBH8va0n7s1pQYcu3uTb8G4tygF4Zg= -github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870 h1:E2s37DuLxFhQDg5gKsWoLBOB0n+ZW8s599zru8FJ2/Y= -github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0= +github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4 h1:7HZCaLC5+BZpmbhCOZJ293Lz68O7PYrF2EzeiFMwCLk= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= -github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= -github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ= github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= +github.com/felixge/httpsnoop v1.0.2 h1:+nS9g82KMXccJ/wp0zyRW9ZBHFETmMGtkk+2CTTrW4o= +github.com/felixge/httpsnoop v1.0.2/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= -github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= -github.com/franela/goblin v0.0.0-20210519012713-85d372ac71e2/go.mod h1:VzmDKDJVZI3aJmnRI9VjAn9nJ8qPPsN1fqzr9dqInIo= github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= -github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= -github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= +github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= +github.com/gin-gonic/gin v1.7.0 h1:jGB9xAJQ12AIGNB4HguylppmDK1Am9ppF7XnGXXJuoU= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -306,34 +300,48 @@ github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgO github.com/go-kit/kit v0.12.0 h1:e4o3o3IsBfAKQh5Qbbiqyfu97Ku7jrO/JbohvztANh4= github.com/go-kit/kit v0.12.0/go.mod h1:lHd+EkCZPIwYItmGDDRdhinkzX2A1sj+M9biaEaizzs= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= -github.com/go-kit/log v0.2.0 h1:7i2K3eKTos3Vc0enKCfnVcgHh2olr/MyfboYq7cAcFw= github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= +github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= +github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA= github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= +github.com/go-ole/go-ole v1.2.1 h1:2lOsA72HgjxAuMlKpFiCbHTvu44PIVkZ5hqm3RSdI/E= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= +github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= +github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY= +github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= -github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= +github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= -github.com/go-zookeeper/zk v1.0.2/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw= +github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0= +github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= +github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8= +github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo= +github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 h1:ZpnhV/YsD2/4cESfV5+Hoeu/iUR3ruzNvZ+yQfO03a0= github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/gogo/gateway v1.1.0 h1:u0SuhL9+Il+UbjM9VIE3ntfRujKbvVpFvNB4HbjeVQ0= -github.com/gogo/gateway v1.1.0/go.mod h1:S7rR8FRQyG3QFESeSv4l2WnsyzlCLG0CzBbUUo/mbic= +github.com/gofrs/uuid v4.2.0+incompatible h1:yyYWMnhkhrKwwr8gAOcOCYxOOscHgDS9yZgBrnJfGa0= github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= -github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= -github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= -github.com/golang-sql/sqlexp v0.0.0-20170517235910-f1bb20e5a188/go.mod h1:vXjM/+wXQnTPR4KqTKDgJukSZ6amVRtWMPEjE6sQoK8= -github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= +github.com/gogo/googleapis v1.4.1-0.20201022092350-68b0159b7869 h1:kRpU4zq+Pzh4feET49aEWPOzwQy3U2SsbZEQ7QEcif0= +github.com/gogo/googleapis v1.4.1-0.20201022092350-68b0159b7869/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ= +github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= @@ -342,7 +350,6 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -361,16 +368,16 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= +github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -383,14 +390,16 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= +github.com/google/martian/v3 v3.2.1 h1:d8MncMlErDFTwQGBK1xhv026j9kqhvw1Qv9IbWT1VLQ= github.com/google/orderedcode v0.0.1 h1:UzfcAexk9Vhv8+9pNOgRu41f16lHq725vPwnSeiG/Us= github.com/google/orderedcode v0.0.1/go.mod h1:iVyU4/qPKHY5h/wSd6rZZCDcLJNxiWO6dvsYES2Sb20= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= @@ -403,20 +412,18 @@ github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/enterprise-certificate-proxy v0.2.1 h1:RY7tHKZcRlk788d5WSo/e83gOyyy742E8GSs771ySpg= +github.com/googleapis/enterprise-certificate-proxy v0.2.1/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= -github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= +github.com/googleapis/gax-go/v2 v2.7.0 h1:IcsPKeInNvYi7eqSaDjiZqDDKu5rsmunY0Y1YupQSSQ= +github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= @@ -427,18 +434,14 @@ github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2z github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gotestyourself/gotestyourself v2.2.0+incompatible/go.mod h1:zZKM6oeNM8k+FRljX1mnzVYeS8wiGgQyvST1/GafPbY= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-middleware v1.2.2/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI= github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw= github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= @@ -449,194 +452,210 @@ github.com/gtank/merlin v0.1.1 h1:eQ90iG7K9pOhtereWsmyRJ6RAwcP4tHTDBHXNg+u5is= github.com/gtank/merlin v0.1.1/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s= github.com/gtank/ristretto255 v0.1.2 h1:JEqUCPA1NvLq5DwYtuzigd7ss8fwbYay9fi4/5uMzcc= github.com/gtank/ristretto255 v0.1.2/go.mod h1:Ph5OpO6c7xKUGROZfWVLiJf9icMDwUeIvY4OmlYW69o= -github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= -github.com/hashicorp/consul/api v1.10.1/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M= -github.com/hashicorp/consul/api v1.12.0/go.mod h1:6pVBMo0ebnYdt2S3H87XhekM/HHrUoTD2XXb/VrZVy0= -github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= -github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= -github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= -github.com/hashicorp/go-hclog v0.16.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= -github.com/hashicorp/go-hclog v1.0.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-getter v1.6.2 h1:7jX7xcB+uVCliddZgeKyNxv0xoT7qL5KDtH7rU4IqIk= +github.com/hashicorp/go-getter v1.6.2/go.mod h1:IZCrswsZPeWv9IkVnLElzRU/gz/QPi6pZHn4tv6vbwA= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= -github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= +github.com/hashicorp/go-safetemp v1.0.0 h1:2HR189eFNrjHQyENnQMMpCiBAsRxzbTMIgBhEyExpmo= +github.com/hashicorp/go-safetemp v1.0.0/go.mod h1:oaerMy3BhqiTbVye6QuFhFtIceqFoDHxNAB65b+Rj1I= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= +github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= -github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d h1:dg1dEPuWpEqDnvIw251EVy4zlP8gWbsGj4BsUKCRpYs= +github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= -github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY= -github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= -github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= -github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= -github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= -github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4= -github.com/hdevalence/ed25519consensus v0.0.0-20210204194344-59a8610d2b87 h1:uUjLpLt6bVvZ72SQc/B4dXcPBw4Vgd7soowdRl52qEM= -github.com/hdevalence/ed25519consensus v0.0.0-20210204194344-59a8610d2b87/go.mod h1:XGsKKeXxeRr95aEOgipvluMPlgjr7dGlk9ZTWOjcUcg= +github.com/hdevalence/ed25519consensus v0.0.0-20220222234857-c00d1f31bab3 h1:aSVUgRRRtOrZOC1fYmY9gV0e9z/Iu+xNVSASWjsuyGU= +github.com/hdevalence/ed25519consensus v0.0.0-20220222234857-c00d1f31bab3/go.mod h1:5PC6ZNPde8bBqU/ewGZig35+UIZtw9Ytxez8/q5ZyFE= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/huandu/go-assert v1.1.5 h1:fjemmA7sSfYHJD7CUqs9qTwwfdNAx7/j2/ZlHXzNB3c= +github.com/huandu/go-assert v1.1.5/go.mod h1:yOLvuqZwmcHIC5rIzrBhT7D3Q9c3GFnd0JrPVhn/06U= +github.com/huandu/skiplist v1.2.0 h1:gox56QD77HzSC0w+Ws3MH3iie755GBJU1OER3h5VsYw= +github.com/huandu/skiplist v1.2.0/go.mod h1:7v3iFjLcSAzO4fN5B8dvebvo/qsfumiLiDXMrPiHF9w= github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= -github.com/hudl/fargo v1.4.0/go.mod h1:9Ai6uvFy5fQNq6VPKtg+Ceq1+eTY4nKUlR2JElEOcDo= -github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/improbable-eng/grpc-web v0.14.1 h1:NrN4PY71A6tAz2sKDvC5JCauENWp0ykG8Oq1H3cpFvw= -github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= +github.com/icza/dyno v0.0.0-20220812133438-f0b6f8a18845 h1:H+uM0Bv88eur3ZSsd2NGKg3YIiuXxwxtlN7HjE66UTU= +github.com/icza/dyno v0.0.0-20220812133438-f0b6f8a18845/go.mod h1:c1tRKs5Tx7E2+uHGSyyncziFjvGpgv4H2HrqXeUQ/Uk= +github.com/improbable-eng/grpc-web v0.15.0 h1:BN+7z6uNXZ1tQGcNAuaU1YjsLTApzkjt2tzCixLaUPQ= +github.com/improbable-eng/grpc-web v0.15.0/go.mod h1:1sy9HKV4Jt9aEs9JSnkWlRJPuPtwNr0l57L4f878wP8= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= +github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= -github.com/influxdata/influxdb1-client v0.0.0-20200827194710-b269163b24ab/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= -github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/ipfs/go-cid v0.0.7 h1:ysQJVJA3fNDF1qigJbsSQOdjhVLsOEoPdh0+R97k3jY= +github.com/ipfs/go-cid v0.0.7/go.mod h1:6Ux9z5e+HpkQdckYoX1PG/6xqKspzlEIR5SDmgqgC/I= +github.com/jhump/protoreflect v1.12.1-0.20220721211354-060cc04fc18b h1:izTof8BKh/nE1wrKOrloNA5q4odOarjf+Xpe+4qow98= +github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jmhodges/levigo v1.0.0 h1:q5EC36kV79HWeTBWsod3mG11EgStG3qArTKcvlksN1U= github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= -github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= -github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= -github.com/keybase/go-keychain v0.0.0-20190712205309-48d3d31d256d h1:Z+RDyXzjKE0i2sTjZ/b1uxiGtPhFy34Ou/Tk0qwN0kM= -github.com/keybase/go-keychain v0.0.0-20190712205309-48d3d31d256d/go.mod h1:JJNrCn9otv/2QP4D7SMJBgaleKpOf66PnW6F5WGNRIc= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= -github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= -github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc= -github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.11.2/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= +github.com/klauspost/compress v1.15.12 h1:YClS/PImqYbn+UILDnqxQCZ3RehC9N318SU3kElDUEM= +github.com/klauspost/compress v1.15.12/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= +github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.10.4 h1:SO9z7FRPzA03QhHKJrH5BXA6HU1rS4V2nIVrrNC1iYk= -github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/libp2p/go-buffer-pool v0.0.2 h1:QNK2iAFa8gjAe1SPz6mHSMuCcjs+X1wlHzeOSqcmlfs= -github.com/libp2p/go-buffer-pool v0.0.2/go.mod h1:MvaB6xw5vOrDl8rYZGLFdKAuk/hRoRZd1Vi32+RXyFM= +github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= +github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw= +github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8= +github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg= +github.com/libp2p/go-libp2p-core v0.15.1 h1:0RY+Mi/ARK9DgG1g9xVQLb8dDaaU8tCePMtGALEfBnM= +github.com/libp2p/go-libp2p-core v0.15.1/go.mod h1:agSaboYM4hzB1cWekgVReqV5M4g5M+2eNNejV+1EEhs= +github.com/libp2p/go-openssl v0.0.7 h1:eCAzdLejcNVBzP/iZM9vqHnQm+XyCEbSSIheIPRGNsw= +github.com/libp2p/go-openssl v0.0.7/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc= github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= -github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w= github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= -github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= -github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA= +github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= -github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= +github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-sqlite3 v1.14.12 h1:TJ1bhYJPV44phC+IMu1u2K/i5RriLTPe+yc68XDJ1Z0= github.com/mattn/go-sqlite3 v1.14.12/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= -github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= -github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4= -github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643 h1:hLDRPB66XQT/8+wG9WsDpiCvZf1yKO7sz7scAjSlBa0= github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM= -github.com/minio/highwayhash v1.0.1/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= +github.com/mimoo/StrobeGo v0.0.0-20220103164710-9a04d6ca976b h1:QrHweqAtyJ9EwCaGHBu1fghwxIPiopAHV06JlXrMHjk= +github.com/mimoo/StrobeGo v0.0.0-20220103164710-9a04d6ca976b/go.mod h1:xxLb2ip6sSUts3g1irPVHyk/DGslwQsNOo9I7smJfNU= +github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 h1:lYpkrQH5ajf0OXOcUbGjvZxxijuBwbbmlSxLiuofa+g= +github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ= github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g= github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= +github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= +github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g= +github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= -github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= -github.com/moby/term v0.0.0-20201216013528-df9cb8a40635 h1:rzf0wL0CHVc8CEsgyygG0Mn9CNCCPZqOPaz8RiiHYQk= +github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 h1:dcztxKSvZ4Id8iPpHERQBbIJfabdt4wUm5qy3wOL2Zc= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= -github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= +github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8= +github.com/mr-tron/base58 v1.1.3/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= +github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= +github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/mtibben/percent v0.2.1 h1:5gssi8Nqo8QU/r2pynCm+hBQHpkB/uNK7BJCFogWdzs= github.com/mtibben/percent v0.2.1/go.mod h1:KG9uO+SZkUp+VkRHsCdYQV3XSZrrSpR3O9ibNBTZrns= +github.com/multiformats/go-base32 v0.0.3 h1:tw5+NhuwaOjJCC5Pp82QuXbrmLzWg7uxlMFp8Nq/kkI= +github.com/multiformats/go-base32 v0.0.3/go.mod h1:pLiuGC8y0QR3Ue4Zug5UzK9LjgbkL8NSQj0zQ5Nz/AA= +github.com/multiformats/go-base36 v0.1.0 h1:JR6TyF7JjGd3m6FbLU2cOxhC0Li8z8dLNGQ89tUg4F4= +github.com/multiformats/go-base36 v0.1.0/go.mod h1:kFGE83c6s80PklsHO9sRn2NCoffoRdUUOENyW/Vv6sM= +github.com/multiformats/go-multiaddr v0.4.1 h1:Pq37uLx3hsyNlTDir7FZyU8+cFCTqd5y1KiM2IzOutI= +github.com/multiformats/go-multiaddr v0.4.1/go.mod h1:3afI9HfVW8csiF8UZqtpYRiDyew8pRX7qLIGHu9FLuM= +github.com/multiformats/go-multibase v0.0.3 h1:l/B6bJDQjvQ5G52jw4QGSYeOTZoAwIO77RblWplfIqk= +github.com/multiformats/go-multibase v0.0.3/go.mod h1:5+1R4eQrT3PkYZ24C3W2Ue2tPwIdYQD509ZjSb5y9Oc= +github.com/multiformats/go-multicodec v0.4.1 h1:BSJbf+zpghcZMZrwTYBGwy0CPcVZGWiC72Cp8bBd4R4= +github.com/multiformats/go-multicodec v0.4.1/go.mod h1:1Hj/eHRaVWSXiSNNfcEPcwZleTmdNP81xlxDLnWU9GQ= +github.com/multiformats/go-multihash v0.0.13/go.mod h1:VdAWLKTwram9oKAatUcLxBNUjdtcVwxObEQBtRfuyjc= +github.com/multiformats/go-multihash v0.0.14/go.mod h1:VdAWLKTwram9oKAatUcLxBNUjdtcVwxObEQBtRfuyjc= +github.com/multiformats/go-multihash v0.1.0 h1:CgAgwqk3//SVEw3T+6DqI4mWMyRuDwZtOWcJT0q9+EA= +github.com/multiformats/go-multihash v0.1.0/go.mod h1:RJlXsxt6vHGaia+S8We0ErjhojtKzPP2AH4+kYM7k84= +github.com/multiformats/go-varint v0.0.5/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= +github.com/multiformats/go-varint v0.0.6 h1:gk85QWKxh3TazbLxED/NlDVv8+q+ReFJk7Y2W/KhfNY= +github.com/multiformats/go-varint v0.0.6/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/grpc-proxy v0.0.0-20181017164139-0f1106ef9c76/go.mod h1:x5OoJHDHqxHS801UIuhqGl6QdSAEJvtausosHSdazIo= github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= -github.com/nats-io/jwt v1.2.2/go.mod h1:/xX356yQA6LuXI9xWW7mZNpxgF2mBmGecH+Fj34sP5Q= -github.com/nats-io/jwt/v2 v2.0.3/go.mod h1:VRP+deawSXyhNjXmxPCHskrR6Mq50BqpEI5SEcNiGlY= github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= -github.com/nats-io/nats-server/v2 v2.5.0/go.mod h1:Kj86UtrXAL6LwYRA6H4RqzkHhK0Vcv2ZnKD5WbQ1t3g= github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= -github.com/nats-io/nats.go v1.12.1/go.mod h1:BPko4oXsySz4aSWeFgOHLZs3G4Jq4ZAyE6/zMCxRT6w= github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= -github.com/nats-io/nkeys v0.2.0/go.mod h1:XdZpAbhgyyODYqjTawOnIOI7VlbKSarI9Gfy1tqEu/s= -github.com/nats-io/nkeys v0.3.0/go.mod h1:gvUNGjVcM2IPr5rCsRsC6Wb3Hr2CQAm08dsxtV6A5y4= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= @@ -644,124 +663,104 @@ github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= -github.com/onsi/ginkgo v1.16.2/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E= -github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= -github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= +github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= +github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.13.0 h1:7lLHu94wT9Ij0o6EWWclhu0aOh32VxhkwEJvzuWPeak= -github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY= +github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= +github.com/onsi/gomega v1.20.0 h1:8W0cWlwFkflGPLltQvLRB7ZVD5HuP6ng320w2IS245Q= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= -github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 h1:rc3tiVYb5z54aKaDfakKn0dDjIyPpTtszkjuMzyt7ec= -github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/opencontainers/runc v1.0.2/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0= -github.com/opencontainers/runc v1.0.3/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0= -github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8= +github.com/opencontainers/image-spec v1.1.0-rc2 h1:2zx/Stx4Wc5pIPDvIxHXvXtQFW/7XWJGmnM7r3wg034= +github.com/opencontainers/image-spec v1.1.0-rc2/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ= +github.com/opencontainers/runc v1.1.3 h1:vIXrkId+0/J2Ymu2m7VjGvbSlAId9XNRPhn2p4b+d8w= github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= -github.com/openzipkin/zipkin-go v0.2.5/go.mod h1:KpXfKdgRDnnhsxw4pNIH9Md5lyFqKUa4YDFlwRYAMyE= -github.com/ory/dockertest v3.3.5+incompatible/go.mod h1:1vX4m9wsvi00u5bseYwXaSnhNrne+V0E6LAcBILJdPs= -github.com/otiai10/copy v1.6.0 h1:IinKAryFFuPONZ7cm6T6E2QX/vcJwSnlaA5lfoaXIiQ= +github.com/ory/dockertest v3.3.5+incompatible h1:iLLK6SQwIhcbrG783Dghaaa3WPzGc+4Emza6EbVUUGA= github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= -github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/pelletier/go-toml/v2 v2.0.1 h1:8e3L2cCQzLFi2CR4g7vGFuFxX7Jl1kKX8gW+iV0GUKU= -github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= +github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU= +github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= -github.com/performancecopilot/speed/v4 v4.0.0/go.mod h1:qxrSyuDGrTOWfV+uKRFhfxw6h/4HXRGUiZiufxo49BM= github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 h1:q2e307iGHPdTGp0hoxKjt1H5pDo6utceo3dQVK3I5XQ= github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o= -github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pierrec/xxHash v0.1.5 h1:n/jBpwTHiER4xYvK3/CdPVnLDPchj8eTJFFLUb4QHBo= +github.com/pierrec/xxHash v0.1.5/go.mod h1:w2waW5Zoa/Wc4Yqe0wgrIYAGKqRMf7czn2HNKXmuL+I= github.com/pierrre/gotestcover v0.0.0-20160517101806-924dca7d15f0/go.mod h1:4xpMLz7RBWyB+ElzHu8Llua96TRCB3YwX+l5EP1wmHk= -github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= -github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= -github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= -github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.8.0/go.mod h1:O9VU6huf47PktckDQfMTX0Y8tY0/7TSWwj+ITvv0TnM= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.12.1 h1:ZiaPsmm9uiBeaSMRznKsCDNtPCS0T3JVDGF+06gjBzk= github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= +github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= +github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= +github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.14.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= +github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.30.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4= github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= +github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= +github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.3.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= +github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= github.com/rakyll/statik v0.1.7 h1:OF3QCZUuyPxuGEP7B4ypUa7sB/iHtqOTDYZXGM8KOdQ= github.com/rakyll/statik v0.1.7/go.mod h1:AlZONWzMtEnMs7W4e/1LURLiI49pIMmp6V9Unghqrcc= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 h1:MkV+77GLUNo5oJ0jf870itWm3D0Sjh7+Za9gazKc5LQ= -github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/regen-network/cosmos-proto v0.3.1 h1:rV7iM4SSFAagvy8RiyhiACbWEGotmqzywPxOvwMdxcg= -github.com/regen-network/cosmos-proto v0.3.1/go.mod h1:jO0sVX6a1B36nmE8C9xBFXpNwWejXC7QqCOnH3O0+YM= +github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM= +github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/regen-network/gocuke v0.6.2 h1:pHviZ0kKAq2U2hN2q3smKNxct6hS0mGByFMHGnWA97M= github.com/regen-network/protobuf v1.3.3-alpha.regen.1 h1:OHEc+q5iIAXpqiqFKeLpu5NwTIkVXUs48vFMwzqpqY4= github.com/regen-network/protobuf v1.3.3-alpha.regen.1/go.mod h1:2DjTFR1HhMQhiWC5sZ4OhQ3+NtdbZ6oBDKQwq5Ou+FI= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk= @@ -770,53 +769,48 @@ github.com/robertkrimen/godocdown v0.0.0-20130622164427-0bfa04905481/go.mod h1:C github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= +github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/rs/cors v1.8.2 h1:KCooALfAYGs415Cwu5ABvv9n9509fSiG5SQJn/AQo4U= github.com/rs/cors v1.8.2/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= -github.com/rs/zerolog v1.26.1 h1:/ihwxqH+4z8UxyI70wM1z9yCvkWcfz/a3mj48k/Zngc= +github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/zerolog v1.28.0 h1:MirSo27VyNi7RJYP3078AA1+Cyzd2GB66qy3aUHvsWY= +github.com/rs/zerolog v1.28.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/sagikazarmark/crypt v0.4.0/go.mod h1:ALv2SRj7GxYV4HO9elxH9nS6M9gW+xDNxqmyJ6RfDFM= github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= -github.com/sasha-s/go-deadlock v0.2.1-0.20190427202633-1595213edefa h1:0U2s5loxrTy6/VgfVoLuVLFJcURKLH49ie0zSch7gh4= -github.com/sasha-s/go-deadlock v0.2.1-0.20190427202633-1595213edefa/go.mod h1:F73l+cr82YSh10GxyRI6qZiCgK64VaZjwesgfQ1/iLM= +github.com/sasha-s/go-deadlock v0.3.1 h1:sqv7fDNShgjcaxkO0JNcOAlr8B9+cV5Ey/OB71efZx0= +github.com/sasha-s/go-deadlock v0.3.1/go.mod h1:F73l+cr82YSh10GxyRI6qZiCgK64VaZjwesgfQ1/iLM= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= +github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= -github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= +github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/snikch/goodman v0.0.0-20171125024755-10e37e294daa/go.mod h1:oJyF+mSPHbB5mVY2iO9KV3pTt/QbIkGaO8gQ2WrDbP4= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= +github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 h1:RC6RW7j+1+HkWaX/Yh71Ee5ZHaHYt7ZP4sQgUrm6cDU= +github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572/go.mod h1:w0SWMsp6j9O/dk4/ZpIhL+3CkG8ofA2vuv7k+ltqUMc= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= -github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= -github.com/spf13/afero v1.8.2 h1:xehSyVa0YnHWsJ49JFljMpg1HX19V6NDZ1fkm1Xznbo= -github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo= +github.com/spf13/afero v1.9.3 h1:41FoI0fD7OR7mGcKE/aOiLkGreyf8ifIOQmJANWogMk= +github.com/spf13/afero v1.9.3/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= -github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= -github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= -github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g= -github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU= -github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM= +github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= +github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= @@ -825,24 +819,20 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= -github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= -github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= -github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= -github.com/spf13/viper v1.10.1/go.mod h1:IGlFPqhNAPKRxohIzWpI5QEy4kuI7tcl5WvR+8qy1rU= -github.com/spf13/viper v1.12.0 h1:CZ7eSOd3kZoaYDLbXnmzgQI5RlciuXBMA+18HwHRfZQ= -github.com/spf13/viper v1.12.0/go.mod h1:b6COn30jlNxbm/V2IqWiNWkJ+vZNiMNksliPCiuKtSI= -github.com/strangelove-ventures/ibctest v0.0.0-20220713213153-930886d8db30 h1:pMhrxU1tY220nxDu6t/gb0H30NG+WYiSJFS5OrcxLFs= -github.com/strangelove-ventures/ibctest v0.0.0-20220713213153-930886d8db30/go.mod h1:aWBeEKU4lGfvJ9qO543zJbQGtB2fK2ffrJh6jbtowEM= +github.com/spf13/viper v1.15.0 h1:js3yy885G8xwJa6iOISGFwd+qlUo5AvyXb7CiihdtiU= +github.com/spf13/viper v1.15.0/go.mod h1:fFcTBJxvhhzSJiZy8n+PeW6t8l+KeT/uTARa0jHOQLA= +github.com/strangelove-ventures/go-subkey v1.0.7 h1:cOP/Lajg3uxV/tvspu0m6+0Cu+DJgygkEAbx/s+f35I= +github.com/strangelove-ventures/go-subkey v1.0.7/go.mod h1:E34izOIEm+sZ1YmYawYRquqBQWeZBjVB4pF7bMuhc1c= +github.com/strangelove-ventures/interchaintest/v7 v7.0.0-20230201060741-e0e4e0f9283b h1:TPt4lyXM0XJUrgaO5ufBsAx9jbrNuwvSZvnIBL/5ui0= +github.com/strangelove-ventures/interchaintest/v7 v7.0.0-20230201060741-e0e4e0f9283b/go.mod h1:xABocejOFzpH8VR9Se92KE02IqR3cLhzGnl4SHV36Qw= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= -github.com/streadway/amqp v1.0.0/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= -github.com/streadway/handy v0.0.0-20200128134331-0f66f006fb2e/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= -github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -850,40 +840,36 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= -github.com/subosito/gotenv v1.3.0 h1:mjC+YW8QpAdXibNi+vNWgzmgBH4+5l5dCXv8cNysBLI= -github.com/subosito/gotenv v1.3.0/go.mod h1:YzJjq/33h7nrwdY+iHMhEOEEbW0ovIz0tB6t6PwAXzs= -github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= -github.com/syndtr/goleveldb v1.0.1-0.20200815110645-5c35d600f0ca h1:Ld/zXl5t4+D69SiV4JoN7kkfvJdOWlPpfxrzxpLMoUk= -github.com/syndtr/goleveldb v1.0.1-0.20200815110645-5c35d600f0ca/go.mod h1:u2MKkTVTVJWe5D1rCvame8WqhBd88EuIwODJZ1VHCPM= -github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c h1:g+WoO5jjkqGAzHWCjJB1zZfXPIAaDpzXIEJ0eS6B5Ok= -github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c/go.mod h1:ahpPrc7HpcfEWDQRZEmnXMzHY03mLDYMCxeDzy46i+8= -github.com/tendermint/btcd v0.1.1 h1:0VcxPfflS2zZ3RiOAHkBiFUcPvbtRj5O7zHmcJWHV7s= -github.com/tendermint/btcd v0.1.1/go.mod h1:DC6/m53jtQzr/NFmMNEu0rxf18/ktVoVtMrnDD5pN+U= -github.com/tendermint/crypto v0.0.0-20191022145703-50d29ede1e15 h1:hqAk8riJvK4RMWx1aInLzndwxKalgi5rTqgfXxOxbEI= -github.com/tendermint/crypto v0.0.0-20191022145703-50d29ede1e15/go.mod h1:z4YtwM70uOnk8h0pjJYlj3zdYwi9l03By6iAIF5j/Pk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= +github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= +github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a h1:1ur3QoCqvE5fl+nylMaIr9PVV1w343YRDtsy+Rwu7XI= +github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= github.com/tendermint/go-amino v0.16.0 h1:GyhmgQKvqF82e2oZeuMSp9JTN0N09emoSZlb2lyGa2E= github.com/tendermint/go-amino v0.16.0/go.mod h1:TQU0M1i/ImAo+tYpZi73AU3V/dKeCoMC9Sphe2ZwGME= -github.com/tendermint/tendermint v0.34.14/go.mod h1:FrwVm3TvsVicI9Z7FlucHV6Znfd5KBc/Lpp69cCwtk0= -github.com/tendermint/tendermint v0.34.19 h1:y0P1qI5wSa9IRuhKnTDA6IUcOrLi1hXJuALR+R7HFEk= -github.com/tendermint/tendermint v0.34.19/go.mod h1:R5+wgIwSxMdKQcmOaeudL0Cjkr3HDkhpcdum6VeU3R4= -github.com/tendermint/tm-db v0.6.4/go.mod h1:dptYhIpJ2M5kUuenLr+Yyf3zQOv1SgBZcl8/BmWlMBw= -github.com/tendermint/tm-db v0.6.6 h1:EzhaOfR0bdKyATqcd5PNeyeq8r+V4bRPHBfyFdD9kGM= -github.com/tendermint/tm-db v0.6.6/go.mod h1:wP8d49A85B7/erz/r4YbKssKw6ylsO/hKtFk7E1aWZI= -github.com/tinylib/msgp v1.1.5/go.mod h1:eQsjooMTnV42mHu917E26IogZ2930nFyBQdofk10Udg= +github.com/tendermint/tendermint v0.37.0-rc2 h1:2n1em+jfbhSv6QnBj8F6KHCpbIzZCB8KgcjidJUQNlY= +github.com/tendermint/tendermint v0.37.0-rc2/go.mod h1:uYQO9DRNPeZROa9X3hJOZpYcVREDC2/HST+EiU5g2+A= +github.com/tendermint/tm-db v0.6.7 h1:fE00Cbl0jayAoqlExN6oyQJ7fR/ZtoVOmvPJ//+shu8= +github.com/tendermint/tm-db v0.6.7/go.mod h1:byQDzFkZV1syXr/ReXS808NxA2xvyuuVgXOJ/088L6I= +github.com/tidwall/btree v1.5.2 h1:5eA83Gfki799V3d3bJo9sWk+yL2LRoTEah3O/SA6/8w= +github.com/tidwall/btree v1.5.2/go.mod h1:twD9XRA5jj9VUQGELzDO4HPQTNJsoWWfYEL+EUQ2cKY= +github.com/tklauser/go-sysconf v0.3.5 h1:uu3Xl4nkLzQfXNsWn15rPc/HQCJKObbt1dKJeWp3vU4= +github.com/tklauser/numcpus v0.2.2 h1:oyhllyrScuYI6g+h/zUvNXNp1wy7x8qQy3t/piefldA= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= -github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c/go.mod h1:hzIxponao9Kjc7aWznkXaL4U4TWaDSs8zcsY4Ka08nM= -github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= +github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= +github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= +github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= +github.com/ulikunitz/xz v0.5.8 h1:ERv8V6GKqVi23rgu5cj9pVfVzJbOqAY2Ntl88O6c2nQ= +github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= -github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -892,21 +878,14 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/zondax/hid v0.9.0 h1:eiT3P6vNxAEVxXMw66eZUAAnU2zD33JBkfG/EnfAKl8= -github.com/zondax/hid v0.9.0/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM= -go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +github.com/zondax/hid v0.9.1 h1:gQe66rtmyZ8VeGFcOpbuH3r7erYtNEAezCAYu8LdkJo= +github.com/zondax/hid v0.9.1/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM= +github.com/zondax/ledger-go v0.14.0 h1:dlMC7aO8Wss1CxBq2I96kZ69Nh1ligzbs8UWOtq/AsA= +github.com/zondax/ledger-go v0.14.0/go.mod h1:fZ3Dqg6qcdXWSOJFKMG8GCTnD7slO/RL2feOQv8K320= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU= go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= -go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= -go.etcd.io/etcd/api/v3 v3.5.1/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= -go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= -go.etcd.io/etcd/client/pkg/v3 v3.5.1/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= -go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= -go.etcd.io/etcd/client/v2 v2.305.1/go.mod h1:pMEacxZW7o8pg4CrFE7pquyCJJzZvkvdD2RibOCCCGs= -go.etcd.io/etcd/client/v3 v3.5.0/go.mod h1:AIKXXVX/DQXtfTEqBryiLTUXwON+GuvO6Z7lLS/oTh0= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= @@ -915,60 +894,43 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= -go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= -go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= +go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= -go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/multierr v1.7.0 h1:zaiO/rmgFjbmCXdSYJWQcdvOCsthmdaHfr3Gm2Kx4Ec= -go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= +go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= +go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= -go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= -go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= -go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8= -go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= -golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +go.uber.org/zap v1.23.0 h1:OjGQ5KQDEUawVHxNwQgPpiypGHOxo2mNZsOqTak4fFY= +go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= -golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20210915214749-c084706c2272/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 h1:kUhD7nTDoI3fVd9G4ORWrbV5NY0liEs/Jg2pv5f+bBA= -golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/crypto v0.4.0 h1:UVQgzMY87xqpKNgb+kDsll2Igd33HszWHFLmpaRMq/8= +golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= @@ -978,7 +940,9 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= +golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw= +golang.org/x/exp v0.0.0-20230131160201-f062dba9d201 h1:BEABXpNXLEz0WxtA+6CQIz2xkg80e+1zrhWyMcq8VzE= +golang.org/x/exp v0.0.0-20230131160201-f062dba9d201/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -992,7 +956,6 @@ golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRu golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= @@ -1004,10 +967,8 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= -golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 h1:kQgndtyPBW/JIYERgdxfwMYh3AVStj88WQTlNDi2a+o= -golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= -golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1022,14 +983,12 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -1037,6 +996,7 @@ golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= @@ -1044,30 +1004,23 @@ golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= -golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210903162142-ad29c8ab022f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210917221730-978cfadd31cf/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211208012354-db4efeb81f4b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2 h1:NWy5+hlRbC7HK+PmcXVUmW1IMyFce7to56IUvhUFm7Y= -golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0 h1:L4ZwwTvKW9gr0ZMS1yrHD9GZhIuVjOBBnaKH+SPQK0Q= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1077,14 +1030,10 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.4.0 h1:NF0gk8LVPg1Ml7SSbGyySuoxdsXitj7TvgvuRxIMc/M= +golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1096,8 +1045,8 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220513210516-0976fa681c29 h1:w8s32wxx3sY+OjLlv9qltkLU5yvJzxjjgiHWLjdIcw4= -golang.org/x/sync v0.0.0-20220513210516-0976fa681c29/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1109,102 +1058,82 @@ golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210903071746-97244b99971b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210917161153-d61c044b1678/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210819135213-f52c844e1c1c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211107104306-e0b2ad06fe42/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= +golang.org/x/sys v0.0.0-20220517195934-5e4e11fc645e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1213,20 +1142,17 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs= -golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/time v0.1.0 h1:xYY+Bajn2a7VBmTM5GikTmnK8ZuX8YgnQCqZpbBNtmA= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= @@ -1239,12 +1165,10 @@ golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgw golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -1273,7 +1197,6 @@ golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= -golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= @@ -1284,23 +1207,16 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= -golang.org/x/tools v0.1.10 h1:QjFRCZxdOhBJ/UNgnBZLbNV13DlbnK0quyivTnXJM20= -golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= +golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df h1:5Pf6pFKu98ODmgnpvkJ3kFUOQGGLIzLIkbzUHp47618= golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= -gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= -gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= -gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= @@ -1321,19 +1237,8 @@ google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz513 google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= -google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= -google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= -google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= -google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= -google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= -google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= -google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= -google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= -google.golang.org/api v0.59.0/go.mod h1:sT2boj7M9YJxZzgeZqXogmhfmRWDtPzT31xkieUbuZU= -google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= -google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= +google.golang.org/api v0.107.0 h1:I2SlFjD8ZWabaIFOfeEDg3pf0BHJDh6iYQ1ic3Yu/UU= +google.golang.org/api v0.107.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -1341,6 +1246,7 @@ google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7 google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= @@ -1378,44 +1284,17 @@ google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201119123407-9b1e624d6bc4/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210126160654-44e461bb6506/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= -google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= -google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= -google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210917145530-b395a37504d4/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211008145708-270636b82663/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211028162531-8db9c33dc351/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220519153652-3a47de7e79bd h1:e0TwkXOdbnH/1x5rc5MZ/VYyiZ4v+RdVfrGMqEwT68I= -google.golang.org/genproto v0.0.0-20220519153652-3a47de7e79bd/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220314164441-57ef72a4c106/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= +google.golang.org/genproto v0.0.0-20230202175211-008b39050e57 h1:vArvWooPH749rNHpBGgVl+U9B9dATjiEhJzcWGlovNs= +google.golang.org/genproto v0.0.0-20230202175211-008b39050e57/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -1432,26 +1311,16 @@ google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3Iji google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= -google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.47.0 h1:9n77onPX5F3qfFCqjy9dhn8PbNQsIKeVU04J9G7umt8= -google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= +google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/grpc v1.53.0 h1:LAv2ds7cmFV/XTS3XG1NneeENYrXGmorPxsBbptIjNc= +google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -1465,23 +1334,24 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= -google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.28.2-0.20220831092852-f930b1dc76e8 h1:KR8+MyP7/qOlV+8Af01LtjL04bu7on42eVsxT4EyBQk= +google.golang.org/protobuf v1.28.2-0.20220831092852-f930b1dc76e8/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= +gopkg.in/cheggaaa/pb.v1 v1.0.27/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= -gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.66.4 h1:SsAcf+mM7mRZo2nJNGt8mZCjG8ZRaNGMURJw7BsIST4= -gopkg.in/ini.v1 v1.66.4/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU= +gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= @@ -1500,9 +1370,7 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= -gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= -gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= +gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -1511,6 +1379,8 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +lukechampine.com/blake3 v1.1.6 h1:H3cROdztr7RCfoaTpGZFQsrqvweFLrqS73j7L7cmR5c= +lukechampine.com/blake3 v1.1.6/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA= lukechampine.com/uint128 v1.1.1 h1:pnxCASz787iMf+02ssImqk6OLt+Z5QHMoZyUXR4z6JU= lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= modernc.org/cc/v3 v3.36.0 h1:0kmRkTmqNidmu3c7BNDSdVHCxXCkWLmWmCIVX4LUboo= @@ -1547,10 +1417,13 @@ modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= modernc.org/z v1.5.1 h1:RTNHdsrOpeoSeOF4FbzTo8gBYByaJ5xT7NgZ9ZqRiJM= modernc.org/z v1.5.1/go.mod h1:eWFB510QWW5Th9YGZT81s+LwvaAs3Q2yr4sP0rmLkv8= nhooyr.io/websocket v1.8.6 h1:s+C3xAMLwGmlI31Nyn/eAehUlZPwfYZu2JXM621Q5/k= +nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= +pgregory.net/rapid v0.5.5 h1:jkgx1TjbQPD/feRoK+S/mXw9e1uj6WilpHrXJowi6oA= +pgregory.net/rapid v0.5.5/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= -sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= +sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= +sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= diff --git a/e2e/relayer/relayer.go b/e2e/relayer/relayer.go new file mode 100644 index 00000000000..7467a9f47f3 --- /dev/null +++ b/e2e/relayer/relayer.go @@ -0,0 +1,58 @@ +package relayer + +import ( + "fmt" + "testing" + + dockerclient "github.com/docker/docker/client" + "github.com/strangelove-ventures/interchaintest/v7" + "github.com/strangelove-ventures/interchaintest/v7/ibc" + "github.com/strangelove-ventures/interchaintest/v7/relayer" + "go.uber.org/zap" +) + +const ( + Rly = "rly" + Hermes = "hermes" + + cosmosRelayerRepository = "ghcr.io/cosmos/relayer" + cosmosRelayerUser = "100:1000" // docker run -it --rm --entrypoint echo ghcr.io/cosmos/relayer "$(id -u):$(id -g)" +) + +// Config holds configuration values for the relayer used in the tests. +type Config struct { + // Tag is the tag used for the relayer image. + Tag string + // Type specifies the type of relayer that this is. + Type string +} + +// New returns an implementation of ibc.Relayer depending on the provided RelayerType. +func New(t *testing.T, cfg Config, logger *zap.Logger, dockerClient *dockerclient.Client, network string) ibc.Relayer { + switch cfg.Type { + case Rly: + return newCosmosRelayer(t, cfg.Tag, logger, dockerClient, network) + case Hermes: + return newHermesRelayer() + default: + panic(fmt.Sprintf("unknown relayer specified: %s", cfg.Type)) + } +} + +// newCosmosRelayer returns an instance of the go relayer. +// Options are used to allow for relayer version selection and specifying the default processing option. +func newCosmosRelayer(t *testing.T, tag string, logger *zap.Logger, dockerClient *dockerclient.Client, network string) ibc.Relayer { + customImageOption := relayer.CustomDockerImage(cosmosRelayerRepository, tag, cosmosRelayerUser) + relayerProcessingOption := relayer.StartupFlags("-p", "events") // relayer processes via events + + relayerFactory := interchaintest.NewBuiltinRelayerFactory(ibc.CosmosRly, logger, customImageOption, relayerProcessingOption) + + return relayerFactory.Build( + t, dockerClient, network, + ) +} + +// newHermesRelayer returns an instance of the hermes relayer. +func newHermesRelayer() ibc.Relayer { + panic("hermes relayer not yet implemented for interchaintest") +} diff --git a/e2e/scripts/run-compatibility-tests.sh b/e2e/scripts/run-compatibility-tests.sh new file mode 100755 index 00000000000..76ef12a5e5d --- /dev/null +++ b/e2e/scripts/run-compatibility-tests.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +set -Eeou pipefail + +function run_full_compatibility_suite(){ + local release_branch="${1}" + gh workflow run e2e-compatibility.yaml -f release-branch=${release_branch} + sleep 5 # can take some time for the workflow to appear + run_id="$(gh run list "--workflow=e2e-compatibility.yaml" | grep workflow_dispatch | grep -Eo "[0-9]{9,11}" | head -n 1)" + gh run view ${run_id} --web +} + +RELEASE_BRANCH="${1}" +run_full_compatibility_suite "${RELEASE_BRANCH}" diff --git a/e2e/scripts/run-e2e.sh b/e2e/scripts/run-e2e.sh index 1e0875c099f..e280be0ddee 100755 --- a/e2e/scripts/run-e2e.sh +++ b/e2e/scripts/run-e2e.sh @@ -2,16 +2,11 @@ set -euo pipefail -SUITE="${1}" +ENTRY_POINT="${1}" TEST="${2}" -export SIMD_TAG="${SIMD_TAG:-latest}" -export SIMD_IMAGE="${SIMD_IMAGE:-ibc-go-simd-e2e}" -# In CI, the docker images will be built separately. -# context for building the image is one directory up. -if [ "${CI:-}" != "true" ] -then - (cd ..; docker build . -t "${SIMD_IMAGE}:${SIMD_TAG}") -fi +export CHAIN_A_TAG="${CHAIN_A_TAG:-latest}" +export CHAIN_IMAGE="${CHAIN_IMAGE:-ibc-go-simd}" +export CHAIN_BINARY="${CHAIN_BINARY:-simd}" -go test -v ./ --run ${SUITE} -testify.m ^${TEST}$ +go test -v ./tests/... --run ${ENTRY_POINT} -testify.m ^${TEST}$ diff --git a/e2e/semverutil/semver.go b/e2e/semverutil/semver.go new file mode 100644 index 00000000000..a210289bad4 --- /dev/null +++ b/e2e/semverutil/semver.go @@ -0,0 +1,61 @@ +package semverutil + +import ( + "strings" + + "golang.org/x/mod/semver" +) + +// FeatureReleases contains the combination of versions the feature was released in. +type FeatureReleases struct { + // MajorVersion is the major version in the format including the v. E.g. "v6" + MajorVersion string + // MinorVersions contains a slice of versions including the v and excluding the patch version. E.g. v2.5 + MinorVersions []string +} + +// IsSupported returns whether the version contains the feature. +// This is true if the version is greater than or equal to the major version it was released in +// or is greater than or equal to the list of minor releases it was included in. +func (fr FeatureReleases) IsSupported(versionStr string) bool { + + // in our compatibility tests, our images are in the format of "release-v1.0.x". We want to actually look at + // the "1.0.x" part but we also need this to be a valid version. We can change it to "1.0.0" + // TODO: change the way we provide the ibc-go version. This should be done in a more flexible way such + // as docker labels/metadata instead of the tag, as this will only work for our versioning scheme. + const releasePrefix = "release-" + if strings.HasPrefix(versionStr, releasePrefix) { + versionStr = versionStr[len(releasePrefix):] + versionStr = strings.ReplaceAll(versionStr, "x", "0") + } + + // assume any non-semantic version formatted version supports the feature + // this will be useful during development of the e2e test with the new feature + if !semver.IsValid(versionStr) { + return true + } + + if semverGTE(versionStr, fr.MajorVersion) { + return true + } + + for _, mv := range fr.MinorVersions { + mvMajor, versionStrMajor := semver.Major(mv), semver.Major(versionStr) + + if semverEqual(mvMajor, versionStrMajor) { + return semverGTE(versionStr, mv) + } + } + + return false +} + +// semverGTE returns true if versionA is greater than or equal to versionB. +func semverGTE(versionA, versionB string) bool { + return semver.Compare(versionA, versionB) >= 0 +} + +// semverEqual returns true if versionA is equal to versionB. +func semverEqual(versionA, versionB string) bool { + return semver.Compare(versionA, versionB) == 0 +} diff --git a/e2e/semverutil/semver_test.go b/e2e/semverutil/semver_test.go new file mode 100644 index 00000000000..b2b44f1d318 --- /dev/null +++ b/e2e/semverutil/semver_test.go @@ -0,0 +1,53 @@ +package semverutil_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/ibc-go/e2e/semverutil" +) + +func TestIsSupported(t *testing.T) { + releases := semverutil.FeatureReleases{ + MajorVersion: "v6", + MinorVersions: []string{ + "v2.5", + "v3.4", + "v4.2", + "v5.1", + }, + } + + testCases := []struct { + name string + version string + expSupported bool + }{ + {"non semantic version", "main", true}, + {"non semantic version starts with v", "v", true}, + {"non semantic version", "pr-155", true}, + {"non semantic version", "major.5.1", true}, + {"non semantic version", "1.minor.1", true}, + {"supported semantic version", "v2.5.0", true}, + {"supported semantic version", "v3.4.0", true}, + {"supported semantic version", "v4.2.0", true}, + {"supported semantic version", "v5.1.0", true}, + {"supported semantic version", "v6.0.0", true}, + {"supported semantic version", "v6.1.0", true}, + {"supported semantic version", "v7.1.0", true}, + {"supported semantic version", "v22.5.1", true}, + {"supported semantic version without v", "2.5.0", true}, + {"unsupported semantic version", "v1.5.0", false}, + {"unsupported semantic version", "v2.4.5", false}, + {"unsupported semantic version", "v3.1.0", false}, + {"unsupported semantic version", "v4.1.0", false}, + {"unsupported semantic version", "v5.0.0", false}, + {"unsupported semantic version on partially supported major line", "v2.4.0", false}, + } + + for _, tc := range testCases { + supported := releases.IsSupported(tc.version) + require.Equal(t, tc.expSupported, supported, tc.name) + } +} diff --git a/e2e/testconfig/testconfig.go b/e2e/testconfig/testconfig.go index 46be84e1e58..4cadcee57e2 100644 --- a/e2e/testconfig/testconfig.go +++ b/e2e/testconfig/testconfig.go @@ -1,50 +1,175 @@ package testconfig import ( + "encoding/json" "fmt" "os" + "strings" - "github.com/strangelove-ventures/ibctest/ibc" + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module/testutil" + genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + govv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" + govv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" + "github.com/strangelove-ventures/interchaintest/v7/ibc" + tmjson "github.com/tendermint/tendermint/libs/json" + tmtypes "github.com/tendermint/tendermint/types" + + "github.com/cosmos/ibc-go/e2e/relayer" + "github.com/cosmos/ibc-go/e2e/semverutil" + "github.com/cosmos/ibc-go/e2e/testvalues" ) const ( - DefaultSimdImage = "ghcr.io/cosmos/ibc-go-simd-e2e" - SimdImageEnv = "SIMD_IMAGE" - SimdTagEnv = "SIMD_TAG" - GoRelayerTag = "RLY_TAG" + // ChainImageEnv specifies the image that the chains will use. If left unspecified, it will + // default to being determined based on the specified binary. E.g. ghcr.io/cosmos/ibc-go-simd + ChainImageEnv = "CHAIN_IMAGE" + // ChainATagEnv specifies the tag that Chain A will use. + ChainATagEnv = "CHAIN_A_TAG" + // ChainBTagEnv specifies the tag that Chain B will use. If unspecified + // the value will default to the same value as Chain A. + ChainBTagEnv = "CHAIN_B_TAG" + // RelayerTagEnv specifies the relayer version. Defaults to "main" + RelayerTagEnv = "RELAYER_TAG" + // RelayerTypeEnv specifies the type of relayer that should be used. + RelayerTypeEnv = "RELAYER_TYPE" + // ChainBinaryEnv binary is the binary that will be used for both chains. + ChainBinaryEnv = "CHAIN_BINARY" + // ChainUpgradeTagEnv specifies the upgrade version tag + ChainUpgradeTagEnv = "CHAIN_UPGRADE_TAG" + // ChainUpgradePlanEnv specifies the upgrade plan name + ChainUpgradePlanEnv = "CHAIN_UPGRADE_PLAN" - defaultRlyTag = "main" + // defaultBinary is the default binary that will be used by the chains. + defaultBinary = "simd" + // defaultRlyTag is the tag that will be used if no relayer tag is specified. + // all images are here https://github.com/cosmos/relayer/pkgs/container/relayer/versions + defaultRlyTag = "andrew-tendermint_v0.37" // "v2.2.0" + // defaultChainTag is the tag that will be used for the chains if none is specified. + defaultChainTag = "main" + // defaultRelayerType is the default relayer that will be used if none is specified. + defaultRelayerType = relayer.Rly ) +func getChainImage(binary string) string { + if binary == "" { + binary = defaultBinary + } + return fmt.Sprintf("ghcr.io/cosmos/ibc-go-%s", binary) +} + // TestConfig holds various fields used in the E2E tests. type TestConfig struct { - SimdImage string - SimdTag string - RlyTag string + ChainAConfig ChainConfig + ChainBConfig ChainConfig + RelayerConfig relayer.Config + UpgradeTag string + UpgradePlanName string +} + +// ChainConfig holds information about an individual chain used in the tests. +type ChainConfig struct { + Image string + Tag string + Binary string } // FromEnv returns a TestConfig constructed from environment variables. func FromEnv() TestConfig { - simdImage, ok := os.LookupEnv(SimdImageEnv) + chainBinary, ok := os.LookupEnv(ChainBinaryEnv) + if !ok { + chainBinary = defaultBinary + } + + chainATag, ok := os.LookupEnv(ChainATagEnv) + if !ok { + chainATag = defaultChainTag + } + + chainBTag, ok := os.LookupEnv(ChainBTagEnv) if !ok { - simdImage = DefaultSimdImage + chainBTag = chainATag } - simdTag, ok := os.LookupEnv(SimdTagEnv) + chainAImage := getChainImage(chainBinary) + specifiedChainImage, ok := os.LookupEnv(ChainImageEnv) + if ok { + chainAImage = specifiedChainImage + } + chainBImage := chainAImage + + upgradeTag, ok := os.LookupEnv(ChainUpgradeTagEnv) if !ok { - panic(fmt.Sprintf("must specify simd version for test with environment variable [%s]", SimdTagEnv)) + upgradeTag = "" } - rlyTag, ok := os.LookupEnv(GoRelayerTag) + upgradePlan, ok := os.LookupEnv(ChainUpgradePlanEnv) if !ok { - rlyTag = defaultRlyTag + upgradePlan = "" } return TestConfig{ - SimdImage: simdImage, - SimdTag: simdTag, - RlyTag: rlyTag, + ChainAConfig: ChainConfig{ + Image: chainAImage, + Tag: chainATag, + Binary: chainBinary, + }, + ChainBConfig: ChainConfig{ + Image: chainBImage, + Tag: chainBTag, + Binary: chainBinary, + }, + UpgradeTag: upgradeTag, + UpgradePlanName: upgradePlan, + RelayerConfig: GetRelayerConfigFromEnv(), + } +} + +// GetRelayerConfigFromEnv returns the RelayerConfig from present environment variables. +func GetRelayerConfigFromEnv() relayer.Config { + relayerType := strings.TrimSpace(os.Getenv(RelayerTypeEnv)) + if relayerType == "" { + relayerType = defaultRelayerType + } + + rlyTag := strings.TrimSpace(os.Getenv(RelayerTagEnv)) + if rlyTag == "" { + if relayerType == relayer.Rly { + rlyTag = defaultRlyTag + } + if relayerType == relayer.Hermes { + // TODO: set default hermes version + } + } + return relayer.Config{ + Tag: rlyTag, + Type: relayerType, + } +} + +func GetChainATag() string { + chainATag, ok := os.LookupEnv(ChainATagEnv) + if !ok { + panic(fmt.Sprintf("no environment variable specified for %s", ChainATagEnv)) + } + return chainATag +} + +func GetChainBTag() string { + chainBTag, ok := os.LookupEnv(ChainBTagEnv) + if !ok { + return GetChainATag() } + return chainBTag +} + +// IsCI returns true if the tests are running in CI, false is returned +// if the tests are running locally. +// Note: github actions passes a CI env value of true by default to all runners. +func IsCI() bool { + return strings.ToLower(os.Getenv("CI")) == "true" } // ChainOptions stores chain configurations for the chains that will be @@ -62,8 +187,8 @@ type ChainOptionConfiguration func(options *ChainOptions) // These options can be configured by passing configuration functions to E2ETestSuite.GetChains. func DefaultChainOptions() ChainOptions { tc := FromEnv() - chainACfg := newDefaultSimappConfig(tc, "simapp-a", "chain-a", "atoma") - chainBCfg := newDefaultSimappConfig(tc, "simapp-b", "chain-b", "atomb") + chainACfg := newDefaultSimappConfig(tc.ChainAConfig, "simapp-a", "chain-a", "atoma") + chainBCfg := newDefaultSimappConfig(tc.ChainBConfig, "simapp-b", "chain-b", "atomb") return ChainOptions{ ChainAConfig: &chainACfg, ChainBConfig: &chainBCfg, @@ -71,23 +196,175 @@ func DefaultChainOptions() ChainOptions { } // newDefaultSimappConfig creates an ibc configuration for simd. -func newDefaultSimappConfig(tc TestConfig, name, chainID, denom string) ibc.ChainConfig { +func newDefaultSimappConfig(cc ChainConfig, name, chainID, denom string) ibc.ChainConfig { + return ibc.ChainConfig{ Type: "cosmos", Name: name, ChainID: chainID, Images: []ibc.DockerImage{ { - Repository: tc.SimdImage, - Version: tc.SimdTag, + Repository: cc.Image, + Version: cc.Tag, }, }, - Bin: "simd", + Bin: cc.Binary, Bech32Prefix: "cosmos", + CoinType: fmt.Sprint(sdk.GetConfig().GetCoinType()), Denom: denom, GasPrices: fmt.Sprintf("0.00%s", denom), GasAdjustment: 1.3, TrustingPeriod: "508h", NoHostMount: false, + ModifyGenesis: getGenesisModificationFunction(cc), + } +} + +// getGenesisModificationFunction returns a genesis modification function that handles the GenesisState type +// correctly depending on if the govv1beta1 gov module is used or if govv1 is being used. +func getGenesisModificationFunction(cc ChainConfig) func(ibc.ChainConfig, []byte) ([]byte, error) { + version := cc.Tag + + if govGenesisFeatureReleases.IsSupported(version) { + return defaultGovv1ModifyGenesis() + } + + return defaultGovv1Beta1ModifyGenesis() +} + +// govGenesisFeatureReleases represents the releases the governance module genesis +// was upgraded from v1beta1 to v1. +var govGenesisFeatureReleases = semverutil.FeatureReleases{ + MajorVersion: "v7", +} + +// defaultGovv1ModifyGenesis will only modify governance params to ensure the voting period and minimum deposit +// are functional for e2e testing purposes. +func defaultGovv1ModifyGenesis() func(ibc.ChainConfig, []byte) ([]byte, error) { + return func(chainConfig ibc.ChainConfig, genbz []byte) ([]byte, error) { + genDoc, err := tmtypes.GenesisDocFromJSON(genbz) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal genesis bytes into genesis doc: %w", err) + } + + var appState genutiltypes.AppMap + if err := json.Unmarshal(genDoc.AppState, &appState); err != nil { + return nil, fmt.Errorf("failed to unmarshal genesis bytes into app state: %w", err) + } + + govGenBz, err := modifyGovAppState(chainConfig, appState[govtypes.ModuleName]) + if err != nil { + return nil, err + } + + appState[govtypes.ModuleName] = govGenBz + + genDoc.AppState, err = json.Marshal(appState) + if err != nil { + return nil, err + } + + bz, err := tmjson.MarshalIndent(genDoc, "", " ") + if err != nil { + return nil, err + } + + return bz, nil + } +} + +// defaultGovv1Beta1ModifyGenesis will only modify governance params to ensure the voting period and minimum deposit +// // are functional for e2e testing purposes. +func defaultGovv1Beta1ModifyGenesis() func(ibc.ChainConfig, []byte) ([]byte, error) { + const appStateKey = "app_state" + return func(chainConfig ibc.ChainConfig, genbz []byte) ([]byte, error) { + genesisDocMap := map[string]interface{}{} + err := json.Unmarshal(genbz, &genesisDocMap) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal genesis bytes into genesis doc: %w", err) + } + + appStateMap, ok := genesisDocMap[appStateKey].(map[string]interface{}) + if !ok { + return nil, fmt.Errorf("failed to extract to app_state") + } + + govModuleBytes, err := json.Marshal(appStateMap[govtypes.ModuleName]) + if err != nil { + return nil, fmt.Errorf("failed to extract gov genesis bytes: %s", err) + } + + govModuleGenesisBytes, err := modifyGovv1Beta1AppState(chainConfig, govModuleBytes) + if err != nil { + return nil, err + } + + govModuleGenesisMap := map[string]interface{}{} + err = json.Unmarshal(govModuleGenesisBytes, &govModuleGenesisMap) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal gov genesis bytes into map: %w", err) + } + + appStateMap[govtypes.ModuleName] = govModuleGenesisMap + genesisDocMap[appStateKey] = appStateMap + + finalGenesisDocBytes, err := json.MarshalIndent(genesisDocMap, "", " ") + if err != nil { + return nil, err + } + + return finalGenesisDocBytes, nil + } +} + +// modifyGovAppState takes the existing gov app state and marshals it to a govv1 GenesisState. +func modifyGovAppState(chainConfig ibc.ChainConfig, govAppState []byte) ([]byte, error) { + cfg := testutil.MakeTestEncodingConfig() + + cdc := codec.NewProtoCodec(cfg.InterfaceRegistry) + govv1.RegisterInterfaces(cfg.InterfaceRegistry) + + govGenesisState := &govv1.GenesisState{} + + if err := cdc.UnmarshalJSON(govAppState, govGenesisState); err != nil { + return nil, fmt.Errorf("failed to unmarshal genesis bytes into gov genesis state: %w", err) + } + + if govGenesisState.Params == nil { + govGenesisState.Params = &govv1.Params{} + } + + govGenesisState.Params.MinDeposit = sdk.NewCoins(sdk.NewCoin(chainConfig.Denom, govv1beta1.DefaultMinDepositTokens)) + vp := testvalues.VotingPeriod + govGenesisState.Params.VotingPeriod = &vp + + govGenBz, err := cdc.MarshalJSON(govGenesisState) + if err != nil { + return nil, fmt.Errorf("failed to marshal gov genesis state: %w", err) } + + return govGenBz, nil +} + +// modifyGovv1Beta1AppState takes the existing gov app state and marshals it to a govv1beta1 GenesisState. +func modifyGovv1Beta1AppState(chainConfig ibc.ChainConfig, govAppState []byte) ([]byte, error) { + cfg := testutil.MakeTestEncodingConfig() + + cdc := codec.NewProtoCodec(cfg.InterfaceRegistry) + govv1beta1.RegisterInterfaces(cfg.InterfaceRegistry) + + govGenesisState := &govv1beta1.GenesisState{} + if err := cdc.UnmarshalJSON(govAppState, govGenesisState); err != nil { + return nil, fmt.Errorf("failed to unmarshal genesis bytes into govv1beta1 genesis state: %w", err) + } + + govGenesisState.DepositParams.MinDeposit = sdk.NewCoins(sdk.NewCoin(chainConfig.Denom, govv1beta1.DefaultMinDepositTokens)) + govGenesisState.VotingParams.VotingPeriod = testvalues.VotingPeriod + + govGenBz, err := cdc.MarshalJSON(govGenesisState) + if err != nil { + return nil, fmt.Errorf("failed to marshal gov genesis state: %w", err) + } + + return govGenBz, nil } diff --git a/e2e/tests/core/03-connection/connection_test.go b/e2e/tests/core/03-connection/connection_test.go new file mode 100644 index 00000000000..1694bc2e0ce --- /dev/null +++ b/e2e/tests/core/03-connection/connection_test.go @@ -0,0 +1,132 @@ +package connection + +import ( + "context" + "fmt" + "strconv" + "strings" + "testing" + "time" + + paramsproposaltypes "github.com/cosmos/cosmos-sdk/x/params/types/proposal" + "github.com/strangelove-ventures/interchaintest/v7/ibc" + test "github.com/strangelove-ventures/interchaintest/v7/testutil" + "github.com/stretchr/testify/suite" + + "github.com/cosmos/ibc-go/e2e/testsuite" + "github.com/cosmos/ibc-go/e2e/testvalues" + transfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" + connectiontypes "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" + ibcexported "github.com/cosmos/ibc-go/v7/modules/core/exported" + ibctesting "github.com/cosmos/ibc-go/v7/testing" +) + +func TestConnectionTestSuite(t *testing.T) { + suite.Run(t, new(ConnectionTestSuite)) +} + +type ConnectionTestSuite struct { + testsuite.E2ETestSuite +} + +// QueryMaxExpectedTimePerBlockParam queries the on-chain max expected time per block param for 03-connection +func (s *ConnectionTestSuite) QueryMaxExpectedTimePerBlockParam(ctx context.Context, chain ibc.Chain) uint64 { + queryClient := s.GetChainGRCPClients(chain).ParamsQueryClient + res, err := queryClient.Params(ctx, ¶msproposaltypes.QueryParamsRequest{ + Subspace: ibcexported.ModuleName, + Key: string(connectiontypes.KeyMaxExpectedTimePerBlock), + }) + s.Require().NoError(err) + + // removing additional strings that are used for amino + delay := strings.ReplaceAll(res.Param.Value, "\"", "") + time, err := strconv.ParseUint(delay, 10, 64) + s.Require().NoError(err) + + return time +} + +// TestMaxExpectedTimePerBlockParam tests changing the MaxExpectedTimePerBlock param using a governance proposal +func (s *ConnectionTestSuite) TestMaxExpectedTimePerBlockParam() { + t := s.T() + ctx := context.TODO() + + relayer, channelA := s.SetupChainsRelayerAndChannel(ctx, transferChannelOptions()) + chainA, chainB := s.GetChains() + + chainBDenom := chainB.Config().Denom + chainAIBCToken := testsuite.GetIBCToken(chainBDenom, channelA.PortID, channelA.ChannelID) + + chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + chainAAddress := chainAWallet.FormattedAddress() + + chainBWallet := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount) + chainBAddress := chainBWallet.FormattedAddress() + + s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB), "failed to wait for blocks") + + t.Run("ensure delay is set to the default of 30 seconds", func(t *testing.T) { + expectedDelay := uint64(30 * time.Second) + delay := s.QueryMaxExpectedTimePerBlockParam(ctx, chainA) + s.Require().Equal(expectedDelay, delay) + }) + + t.Run("change the delay to 60 seconds", func(t *testing.T) { + delay := fmt.Sprintf(`"%d"`, 1*time.Minute) + changes := []paramsproposaltypes.ParamChange{ + paramsproposaltypes.NewParamChange(ibcexported.ModuleName, string(connectiontypes.KeyMaxExpectedTimePerBlock), delay), + } + + proposal := paramsproposaltypes.NewParameterChangeProposal(ibctesting.Title, ibctesting.Description, changes) + s.ExecuteGovProposal(ctx, chainA, chainAWallet, proposal) + }) + + t.Run("validate the param was successfully changed", func(t *testing.T) { + expectedDelay := uint64(1 * time.Minute) + delay := s.QueryMaxExpectedTimePerBlockParam(ctx, chainA) + s.Require().Equal(expectedDelay, delay) + }) + + t.Run("ensure packets can be received, send from chainB to chainA", func(t *testing.T) { + t.Run("send tokens from chainB to chainA", func(t *testing.T) { + transferTxResp, err := s.Transfer(ctx, chainB, chainBWallet, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, testvalues.DefaultTransferAmount(chainBDenom), chainBAddress, chainAAddress, s.GetTimeoutHeight(ctx, chainA), 0, "") + s.Require().NoError(err) + s.AssertValidTxResponse(transferTxResp) + }) + + t.Run("tokens are escrowed", func(t *testing.T) { + actualBalance, err := s.GetChainBNativeBalance(ctx, chainBWallet) + s.Require().NoError(err) + + expected := testvalues.StartingTokenAmount - testvalues.IBCTransferAmount + s.Require().Equal(expected, actualBalance) + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer) + }) + + t.Run("packets are relayed", func(t *testing.T) { + s.AssertPacketRelayed(ctx, chainA, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, 1) + + actualBalance, err := chainA.GetBalance(ctx, chainAAddress, chainAIBCToken.IBCDenom()) + s.Require().NoError(err) + + expected := testvalues.IBCTransferAmount + s.Require().Equal(expected, actualBalance) + }) + + t.Run("stop relayer", func(t *testing.T) { + s.StopRelayer(ctx, relayer) + }) + }) +} + +// transferChannelOptions configures both of the chains to have non-incentivized transfer channels. +func transferChannelOptions() func(options *ibc.CreateChannelOptions) { + return func(opts *ibc.CreateChannelOptions) { + opts.Version = transfertypes.Version + opts.SourcePortName = transfertypes.PortID + opts.DestPortName = transfertypes.PortID + } +} diff --git a/e2e/tests/core/client_test.go b/e2e/tests/core/client_test.go new file mode 100644 index 00000000000..03b1b9566cd --- /dev/null +++ b/e2e/tests/core/client_test.go @@ -0,0 +1,121 @@ +package e2e + +import ( + "context" + "fmt" + "strings" + "testing" + "time" + + "github.com/strangelove-ventures/interchaintest/v7/ibc" + test "github.com/strangelove-ventures/interchaintest/v7/testutil" + "github.com/stretchr/testify/suite" + + "github.com/cosmos/ibc-go/e2e/testsuite" + "github.com/cosmos/ibc-go/e2e/testvalues" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + ibcexported "github.com/cosmos/ibc-go/v7/modules/core/exported" + ibctesting "github.com/cosmos/ibc-go/v7/testing" +) + +func TestClientTestSuite(t *testing.T) { + suite.Run(t, new(ClientTestSuite)) +} + +type ClientTestSuite struct { + testsuite.E2ETestSuite +} + +// Status queries the current status of the client +func (s *ClientTestSuite) Status(ctx context.Context, chain ibc.Chain, clientID string) (string, error) { + queryClient := s.GetChainGRCPClients(chain).ClientQueryClient + res, err := queryClient.ClientStatus(ctx, &clienttypes.QueryClientStatusRequest{ + ClientId: clientID, + }) + if err != nil { + return "", err + } + + return res.Status, nil +} + +func (s *ClientTestSuite) TestClientUpdateProposal_Succeeds() { + t := s.T() + ctx := context.TODO() + + var ( + pathName string + relayer ibc.Relayer + subjectClientID string + substituteClientID string + badTrustingPeriod = time.Duration(time.Second) + ) + + t.Run("create substitute client with correct trusting period", func(t *testing.T) { + relayer, _ = s.SetupChainsRelayerAndChannel(ctx) + + // TODO: update when client identifier created is accessible + // currently assumes first client is 07-tendermint-0 + substituteClientID = clienttypes.FormatClientIdentifier(ibcexported.Tendermint, 0) + + // TODO: replace with better handling of path names + pathName = fmt.Sprintf("%s-path-%d", s.T().Name(), 0) + pathName = strings.ReplaceAll(pathName, "/", "-") + }) + + chainA, chainB := s.GetChains() + chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + + t.Run("create subject client with bad trusting period", func(t *testing.T) { + createClientOptions := ibc.CreateClientOptions{ + TrustingPeriod: badTrustingPeriod.String(), + } + + s.SetupClients(ctx, relayer, createClientOptions) + + // TODO: update when client identifier created is accessible + // currently assumes second client is 07-tendermint-1 + subjectClientID = clienttypes.FormatClientIdentifier(ibcexported.Tendermint, 1) + }) + + time.Sleep(badTrustingPeriod) + + t.Run("update substitute client", func(t *testing.T) { + s.UpdateClients(ctx, relayer, pathName) + }) + + s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB), "failed to wait for blocks") + + t.Run("check status of each client", func(t *testing.T) { + t.Run("substitute should be active", func(t *testing.T) { + status, err := s.Status(ctx, chainA, substituteClientID) + s.Require().NoError(err) + s.Require().Equal(ibcexported.Active.String(), status) + }) + + t.Run("subject should be expired", func(t *testing.T) { + status, err := s.Status(ctx, chainA, subjectClientID) + s.Require().NoError(err) + s.Require().Equal(ibcexported.Expired.String(), status) + }) + }) + + t.Run("pass client update proposal", func(t *testing.T) { + proposal := clienttypes.NewClientUpdateProposal(ibctesting.Title, ibctesting.Description, subjectClientID, substituteClientID) + s.ExecuteGovProposal(ctx, chainA, chainAWallet, proposal) + }) + + t.Run("check status of each client", func(t *testing.T) { + t.Run("substitute should be active", func(t *testing.T) { + status, err := s.Status(ctx, chainA, substituteClientID) + s.Require().NoError(err) + s.Require().Equal(ibcexported.Active.String(), status) + }) + + t.Run("subject should be active", func(t *testing.T) { + status, err := s.Status(ctx, chainA, subjectClientID) + s.Require().NoError(err) + s.Require().Equal(ibcexported.Active.String(), status) + }) + }) +} diff --git a/e2e/tests/interchain_accounts/base_test.go b/e2e/tests/interchain_accounts/base_test.go new file mode 100644 index 00000000000..53c5384be31 --- /dev/null +++ b/e2e/tests/interchain_accounts/base_test.go @@ -0,0 +1,421 @@ +package interchain_accounts + +import ( + "context" + "testing" + "time" + + "github.com/strangelove-ventures/interchaintest/v7" + "github.com/strangelove-ventures/interchaintest/v7/chain/cosmos" + "github.com/strangelove-ventures/interchaintest/v7/ibc" + test "github.com/strangelove-ventures/interchaintest/v7/testutil" + "github.com/stretchr/testify/suite" + "golang.org/x/mod/semver" + + sdk "github.com/cosmos/cosmos-sdk/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/cosmos/gogoproto/proto" + + "github.com/cosmos/ibc-go/e2e/testconfig" + "github.com/cosmos/ibc-go/e2e/testsuite" + "github.com/cosmos/ibc-go/e2e/testvalues" + controllertypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/controller/types" + icatypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + ibctesting "github.com/cosmos/ibc-go/v7/testing" +) + +func TestInterchainAccountsTestSuite(t *testing.T) { + suite.Run(t, new(InterchainAccountsTestSuite)) +} + +type InterchainAccountsTestSuite struct { + testsuite.E2ETestSuite +} + +// getICAVersion returns the version which should be used in the MsgRegisterAccount broadcast from the +// controller chain. +func getICAVersion(chainAVersion, chainBVersion string) string { + chainBIsGreaterThanOrEqualToChainA := semver.Compare(chainAVersion, chainBVersion) <= 0 + if chainBIsGreaterThanOrEqualToChainA { + // allow version to be specified by the controller chain + return "" + } + // explicitly set the version string because the host chain might not yet support incentivized channels. + return icatypes.NewDefaultMetadataString(ibctesting.FirstConnectionID, ibctesting.FirstConnectionID) +} + +// RegisterInterchainAccount will attempt to register an interchain account on the counterparty chain. +func (s *InterchainAccountsTestSuite) RegisterInterchainAccount(ctx context.Context, chain *cosmos.CosmosChain, user ibc.Wallet, msgRegisterAccount *controllertypes.MsgRegisterInterchainAccount) { + txResp, err := s.BroadcastMessages(ctx, chain, user, msgRegisterAccount) + s.Require().NoError(err) + s.AssertValidTxResponse(txResp) +} + +func (s *InterchainAccountsTestSuite) TestMsgSendTx_SuccessfulTransfer() { + t := s.T() + ctx := context.TODO() + + // setup relayers and connection-0 between two chains + // channel-0 is a transfer channel but it will not be used in this test case + relayer, _ := s.SetupChainsRelayerAndChannel(ctx) + chainA, chainB := s.GetChains() + + // setup 2 accounts: controller account on chain A, a second chain B account. + // host account will be created when the ICA is registered + controllerAccount := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + controllerAddress := controllerAccount.FormattedAddress() + chainBAccount := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount) + var hostAccount string + + t.Run("broadcast MsgRegisterInterchainAccount", func(t *testing.T) { + version := getICAVersion(testconfig.GetChainATag(), testconfig.GetChainBTag()) + msgRegisterAccount := controllertypes.NewMsgRegisterInterchainAccount(ibctesting.FirstConnectionID, controllerAddress, version) + + txResp, err := s.BroadcastMessages(ctx, chainA, controllerAccount, msgRegisterAccount) + s.Require().NoError(err) + s.AssertValidTxResponse(txResp) + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer) + }) + + t.Run("verify interchain account", func(t *testing.T) { + var err error + hostAccount, err = s.QueryInterchainAccount(ctx, chainA, controllerAddress, ibctesting.FirstConnectionID) + s.Require().NoError(err) + s.Require().NotZero(len(hostAccount)) + + channels, err := relayer.GetChannels(ctx, s.GetRelayerExecReporter(), chainA.Config().ChainID) + s.Require().NoError(err) + s.Require().Equal(len(channels), 2) + }) + + t.Run("interchain account executes a bank transfer on behalf of the corresponding owner account", func(t *testing.T) { + t.Run("fund interchain account wallet", func(t *testing.T) { + // fund the host account so it has some $$ to send + err := chainB.SendFunds(ctx, interchaintest.FaucetAccountKeyName, ibc.WalletAmount{ + Address: hostAccount, + Amount: testvalues.StartingTokenAmount, + Denom: chainB.Config().Denom, + }) + s.Require().NoError(err) + }) + + t.Run("broadcast MsgSendTx", func(t *testing.T) { + // assemble bank transfer message from host account to user account on host chain + msgSend := &banktypes.MsgSend{ + FromAddress: hostAccount, + ToAddress: chainBAccount.FormattedAddress(), + Amount: sdk.NewCoins(testvalues.DefaultTransferAmount(chainB.Config().Denom)), + } + + cdc := testsuite.Codec() + bz, err := icatypes.SerializeCosmosTx(cdc, []proto.Message{msgSend}) + s.Require().NoError(err) + + packetData := icatypes.InterchainAccountPacketData{ + Type: icatypes.EXECUTE_TX, + Data: bz, + Memo: "e2e", + } + + msgSendTx := controllertypes.NewMsgSendTx(controllerAccount.FormattedAddress(), ibctesting.FirstConnectionID, uint64(time.Hour.Nanoseconds()), packetData) + + resp, err := s.BroadcastMessages( + ctx, + chainA, + controllerAccount, + msgSendTx, + ) + + s.AssertValidTxResponse(resp) + s.Require().NoError(err) + + s.Require().NoError(test.WaitForBlocks(ctx, 10, chainA, chainB)) + }) + + t.Run("verify tokens transferred", func(t *testing.T) { + balance, err := chainB.GetBalance(ctx, chainBAccount.FormattedAddress(), chainB.Config().Denom) + s.Require().NoError(err) + + _, err = chainB.GetBalance(ctx, hostAccount, chainB.Config().Denom) + s.Require().NoError(err) + + expected := testvalues.IBCTransferAmount + testvalues.StartingTokenAmount + s.Require().Equal(expected, balance) + }) + }) +} + +func (s *InterchainAccountsTestSuite) TestMsgSendTx_FailedTransfer_InsufficientFunds() { + t := s.T() + ctx := context.TODO() + + // setup relayers and connection-0 between two chains + // channel-0 is a transfer channel but it will not be used in this test case + relayer, _ := s.SetupChainsRelayerAndChannel(ctx) + chainA, chainB := s.GetChains() + + // setup 2 accounts: controller account on chain A, a second chain B account. + // host account will be created when the ICA is registered + controllerAccount := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + controllerAddress := controllerAccount.FormattedAddress() + chainBAccount := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount) + var hostAccount string + + t.Run("broadcast MsgRegisterInterchainAccount", func(t *testing.T) { + version := getICAVersion(testconfig.GetChainATag(), testconfig.GetChainBTag()) + msgRegisterAccount := controllertypes.NewMsgRegisterInterchainAccount(ibctesting.FirstConnectionID, controllerAddress, version) + + txResp, err := s.BroadcastMessages(ctx, chainA, controllerAccount, msgRegisterAccount) + s.Require().NoError(err) + s.AssertValidTxResponse(txResp) + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer) + }) + + t.Run("verify interchain account", func(t *testing.T) { + var err error + hostAccount, err = s.QueryInterchainAccount(ctx, chainA, controllerAddress, ibctesting.FirstConnectionID) + s.Require().NoError(err) + s.Require().NotZero(len(hostAccount)) + + channels, err := relayer.GetChannels(ctx, s.GetRelayerExecReporter(), chainA.Config().ChainID) + s.Require().NoError(err) + s.Require().Equal(len(channels), 2) + }) + + t.Run("fail to execute bank transfer over ICA", func(t *testing.T) { + t.Run("verify empty host wallet", func(t *testing.T) { + hostAccountBalance, err := chainB.GetBalance(ctx, hostAccount, chainB.Config().Denom) + s.Require().NoError(err) + s.Require().Zero(hostAccountBalance) + }) + + t.Run("broadcast MsgSendTx", func(t *testing.T) { + // assemble bank transfer message from host account to user account on host chain + msgSend := &banktypes.MsgSend{ + FromAddress: hostAccount, + ToAddress: chainBAccount.FormattedAddress(), + Amount: sdk.NewCoins(testvalues.DefaultTransferAmount(chainB.Config().Denom)), + } + + cdc := testsuite.Codec() + bz, err := icatypes.SerializeCosmosTx(cdc, []proto.Message{msgSend}) + s.Require().NoError(err) + + packetData := icatypes.InterchainAccountPacketData{ + Type: icatypes.EXECUTE_TX, + Data: bz, + Memo: "e2e", + } + + msgSendTx := controllertypes.NewMsgSendTx(controllerAddress, ibctesting.FirstConnectionID, uint64(time.Hour.Nanoseconds()), packetData) + + txResp, err := s.BroadcastMessages( + ctx, + chainA, + controllerAccount, + msgSendTx, + ) + + s.AssertValidTxResponse(txResp) + s.Require().NoError(err) + + s.Require().NoError(test.WaitForBlocks(ctx, 10, chainA, chainB)) + }) + + t.Run("verify balance is the same", func(t *testing.T) { + balance, err := chainB.GetBalance(ctx, chainBAccount.FormattedAddress(), chainB.Config().Denom) + s.Require().NoError(err) + + expected := testvalues.StartingTokenAmount + s.Require().Equal(expected, balance) + }) + }) +} + +func (s *InterchainAccountsTestSuite) TestMsgSubmitTx_SuccessfulTransfer_AfterReopeningICA() { + t := s.T() + ctx := context.TODO() + + // setup relayers and connection-0 between two chains + // channel-0 is a transfer channel but it will not be used in this test case + relayer, _ := s.SetupChainsRelayerAndChannel(ctx) + chainA, chainB := s.GetChains() + + // setup 2 accounts: controller account on chain A, a second chain B account. + // host account will be created when the ICA is registered + controllerAccount := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + controllerAddress := controllerAccount.FormattedAddress() + chainBAccount := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount) + + var ( + portID string + hostAccount string + + initialChannelID = "channel-1" + channelIDAfterReopening = "channel-2" + ) + + t.Run("register interchain account", func(t *testing.T) { + var err error + version := getICAVersion(testconfig.GetChainATag(), testconfig.GetChainBTag()) + msgRegisterInterchainAccount := controllertypes.NewMsgRegisterInterchainAccount(ibctesting.FirstConnectionID, controllerAddress, version) + s.RegisterInterchainAccount(ctx, chainA, controllerAccount, msgRegisterInterchainAccount) + portID, err = icatypes.NewControllerPortID(controllerAddress) + s.Require().NoError(err) + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer) + }) + + t.Run("verify interchain account", func(t *testing.T) { + var err error + hostAccount, err = s.QueryInterchainAccount(ctx, chainA, controllerAddress, ibctesting.FirstConnectionID) + s.Require().NoError(err) + s.Require().NotZero(len(hostAccount)) + + _, err = s.QueryChannel(ctx, chainA, portID, initialChannelID) + s.Require().NoError(err) + }) + + // stop the relayer to let the submit tx message time out + t.Run("stop relayer", func(t *testing.T) { + s.StopRelayer(ctx, relayer) + }) + + t.Run("submit tx message with bank transfer message times out", func(t *testing.T) { + t.Run("fund interchain account wallet", func(t *testing.T) { + // fund the host account account so it has some $$ to send + err := chainB.SendFunds(ctx, interchaintest.FaucetAccountKeyName, ibc.WalletAmount{ + Address: hostAccount, + Amount: testvalues.StartingTokenAmount, + Denom: chainB.Config().Denom, + }) + s.Require().NoError(err) + }) + + t.Run("broadcast MsgSendTx", func(t *testing.T) { + // assemble bank transfer message from host account to user account on host chain + msgSend := &banktypes.MsgSend{ + FromAddress: hostAccount, + ToAddress: chainBAccount.FormattedAddress(), + Amount: sdk.NewCoins(testvalues.DefaultTransferAmount(chainB.Config().Denom)), + } + + cdc := testsuite.Codec() + + bz, err := icatypes.SerializeCosmosTx(cdc, []proto.Message{msgSend}) + s.Require().NoError(err) + + packetData := icatypes.InterchainAccountPacketData{ + Type: icatypes.EXECUTE_TX, + Data: bz, + Memo: "e2e", + } + + msgSendTx := controllertypes.NewMsgSendTx(controllerAddress, ibctesting.FirstConnectionID, uint64(1), packetData) + + resp, err := s.BroadcastMessages( + ctx, + chainA, + controllerAccount, + msgSendTx, + ) + + s.AssertValidTxResponse(resp) + s.Require().NoError(err) + + // this sleep is to allow the packet to timeout + time.Sleep(1 * time.Second) + }) + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer) + }) + + t.Run("verify channel is closed due to timeout on ordered channel", func(t *testing.T) { + channel, err := s.QueryChannel(ctx, chainA, portID, initialChannelID) + s.Require().NoError(err) + + s.Require().Equal(channeltypes.CLOSED, channel.State, "the channel was not in an expected state") + }) + + t.Run("verify tokens not transferred", func(t *testing.T) { + balance, err := chainB.GetBalance(ctx, chainBAccount.FormattedAddress(), chainB.Config().Denom) + s.Require().NoError(err) + + _, err = chainB.GetBalance(ctx, hostAccount, chainB.Config().Denom) + s.Require().NoError(err) + + expected := testvalues.StartingTokenAmount + s.Require().Equal(expected, balance) + }) + + // re-register interchain account to reopen the channel now that it has been closed due to timeout + // on an ordered channel + t.Run("register interchain account", func(t *testing.T) { + version := getICAVersion(testconfig.GetChainATag(), testconfig.GetChainBTag()) + msgRegisterInterchainAccount := controllertypes.NewMsgRegisterInterchainAccount(ibctesting.FirstConnectionID, controllerAddress, version) + s.RegisterInterchainAccount(ctx, chainA, controllerAccount, msgRegisterInterchainAccount) + + s.Require().NoError(test.WaitForBlocks(ctx, 10, chainA, chainB)) + }) + + t.Run("verify new channel is now open and interchain account has been reregistered with the same portID", func(t *testing.T) { + channel, err := s.QueryChannel(ctx, chainA, portID, channelIDAfterReopening) + s.Require().NoError(err) + + s.Require().Equal(channeltypes.OPEN, channel.State, "the channel was not in an expected state") + }) + + t.Run("broadcast MsgSendTx", func(t *testing.T) { + // assemble bank transfer message from host account to user account on host chain + msgSend := &banktypes.MsgSend{ + FromAddress: hostAccount, + ToAddress: chainBAccount.FormattedAddress(), + Amount: sdk.NewCoins(testvalues.DefaultTransferAmount(chainB.Config().Denom)), + } + + cdc := testsuite.Codec() + + bz, err := icatypes.SerializeCosmosTx(cdc, []proto.Message{msgSend}) + s.Require().NoError(err) + + packetData := icatypes.InterchainAccountPacketData{ + Type: icatypes.EXECUTE_TX, + Data: bz, + Memo: "e2e", + } + + msgSendTx := controllertypes.NewMsgSendTx(controllerAddress, ibctesting.FirstConnectionID, uint64(5*time.Minute), packetData) + + resp, err := s.BroadcastMessages( + ctx, + chainA, + controllerAccount, + msgSendTx, + ) + + s.AssertValidTxResponse(resp) + s.Require().NoError(err) + + // time for the packet to be relayed + s.Require().NoError(test.WaitForBlocks(ctx, 5, chainA, chainB)) + }) + + t.Run("verify tokens transferred", func(t *testing.T) { + balance, err := chainB.GetBalance(ctx, chainBAccount.FormattedAddress(), chainB.Config().Denom) + s.Require().NoError(err) + + expected := testvalues.IBCTransferAmount + testvalues.StartingTokenAmount + s.Require().Equal(expected, balance) + }) +} diff --git a/e2e/tests/interchain_accounts/gov_test.go b/e2e/tests/interchain_accounts/gov_test.go new file mode 100644 index 00000000000..2f572c35596 --- /dev/null +++ b/e2e/tests/interchain_accounts/gov_test.go @@ -0,0 +1,117 @@ +package interchain_accounts + +import ( + "context" + "testing" + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + "github.com/cosmos/gogoproto/proto" + + "github.com/strangelove-ventures/interchaintest/v7" + "github.com/strangelove-ventures/interchaintest/v7/ibc" + test "github.com/strangelove-ventures/interchaintest/v7/testutil" + "github.com/stretchr/testify/suite" + + "github.com/cosmos/ibc-go/e2e/testsuite" + "github.com/cosmos/ibc-go/e2e/testvalues" + controllertypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/controller/types" + icatypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types" + ibctesting "github.com/cosmos/ibc-go/v7/testing" +) + +func TestInterchainAccountsGovTestSuite(t *testing.T) { + suite.Run(t, new(InterchainAccountsGovTestSuite)) +} + +type InterchainAccountsGovTestSuite struct { + testsuite.E2ETestSuite +} + +func (s *InterchainAccountsGovTestSuite) TestInterchainAccountsGovIntegration() { + t := s.T() + ctx := context.TODO() + + // setup relayers and connection-0 between two chains + // channel-0 is a transfer channel but it will not be used in this test case + relayer, _ := s.SetupChainsRelayerAndChannel(ctx) + chainA, chainB := s.GetChains() + controllerAccount := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + + chainBAccount := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount) + chainBAddress := chainBAccount.FormattedAddress() + + govModuleAddress, err := s.QueryModuleAccountAddress(ctx, govtypes.ModuleName, chainA) + s.Require().NoError(err) + s.Require().NotNil(govModuleAddress) + + t.Run("execute proposal for MsgRegisterInterchainAccount", func(t *testing.T) { + version := icatypes.NewDefaultMetadataString(ibctesting.FirstConnectionID, ibctesting.FirstConnectionID) + msgRegisterAccount := controllertypes.NewMsgRegisterInterchainAccount(ibctesting.FirstConnectionID, govModuleAddress.String(), version) + s.ExecuteGovProposalV1(ctx, msgRegisterAccount, chainA, controllerAccount, 1) + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer) + }) + + s.Require().NoError(test.WaitForBlocks(ctx, 10, chainA, chainB)) + + var interchainAccAddr string + t.Run("verify interchain account registration success", func(t *testing.T) { + var err error + interchainAccAddr, err = s.QueryInterchainAccount(ctx, chainA, govModuleAddress.String(), ibctesting.FirstConnectionID) + s.Require().NoError(err) + s.Require().NotZero(len(interchainAccAddr)) + + channels, err := relayer.GetChannels(ctx, s.GetRelayerExecReporter(), chainA.Config().ChainID) + s.Require().NoError(err) + s.Require().Equal(len(channels), 2) + }) + + t.Run("interchain account executes a bank transfer on behalf of the corresponding owner account", func(t *testing.T) { + t.Run("fund interchain account wallet", func(t *testing.T) { + // fund the host account, so it has some $$ to send + err := chainB.SendFunds(ctx, interchaintest.FaucetAccountKeyName, ibc.WalletAmount{ + Address: interchainAccAddr, + Amount: testvalues.StartingTokenAmount, + Denom: chainB.Config().Denom, + }) + s.Require().NoError(err) + }) + + t.Run("execute proposal for MsgSendTx", func(t *testing.T) { + msgBankSend := &banktypes.MsgSend{ + FromAddress: interchainAccAddr, + ToAddress: chainBAddress, + Amount: sdk.NewCoins(testvalues.DefaultTransferAmount(chainB.Config().Denom)), + } + + cdc := testsuite.Codec() + bz, err := icatypes.SerializeCosmosTx(cdc, []proto.Message{msgBankSend}) + s.Require().NoError(err) + + packetData := icatypes.InterchainAccountPacketData{ + Type: icatypes.EXECUTE_TX, + Data: bz, + Memo: "e2e", + } + + msgSendTx := controllertypes.NewMsgSendTx(govModuleAddress.String(), ibctesting.FirstConnectionID, uint64(time.Hour.Nanoseconds()), packetData) + s.ExecuteGovProposalV1(ctx, msgSendTx, chainA, controllerAccount, 2) + }) + + t.Run("verify tokens transferred", func(t *testing.T) { + balance, err := chainB.GetBalance(ctx, chainBAccount.FormattedAddress(), chainB.Config().Denom) + s.Require().NoError(err) + + _, err = chainB.GetBalance(ctx, interchainAccAddr, chainB.Config().Denom) + s.Require().NoError(err) + + expected := testvalues.IBCTransferAmount + testvalues.StartingTokenAmount + s.Require().Equal(expected, balance) + }) + }) +} diff --git a/e2e/tests/interchain_accounts/groups_test.go b/e2e/tests/interchain_accounts/groups_test.go new file mode 100644 index 00000000000..7e7ad72324d --- /dev/null +++ b/e2e/tests/interchain_accounts/groups_test.go @@ -0,0 +1,214 @@ +package interchain_accounts + +import ( + "context" + "testing" + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + grouptypes "github.com/cosmos/cosmos-sdk/x/group" + "github.com/cosmos/gogoproto/proto" + interchaintest "github.com/strangelove-ventures/interchaintest/v7" + "github.com/strangelove-ventures/interchaintest/v7/ibc" + test "github.com/strangelove-ventures/interchaintest/v7/testutil" + "github.com/stretchr/testify/suite" + + "github.com/cosmos/ibc-go/e2e/testsuite" + "github.com/cosmos/ibc-go/e2e/testvalues" + controllertypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/controller/types" + icatypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types" + ibctesting "github.com/cosmos/ibc-go/v7/testing" +) + +const ( + // DefaultGroupMemberWeight is the members voting weight. + // A group members weight is used in the sum of `YES` votes required to meet a decision policy threshold. + DefaultGroupMemberWeight = "1" + + // DefaultGroupThreshold is the minimum weighted sum of `YES` votes that must be met or + // exceeded for a proposal to succeed. + DefaultGroupThreshold = "1" + + // DefaultMetadata defines a resusable metadata string for testing purposes + DefaultMetadata = "custom metadata" + + // DefaultMinExecutionPeriod is the minimum duration after the proposal submission + // where members can start sending MsgExec. This means that the window for + // sending a MsgExec transaction is: + // `[ submission + min_execution_period ; submission + voting_period + max_execution_period]` + // where max_execution_period is a app-specific config, defined in the keeper. + // If not set, min_execution_period will default to 0. + DefaultMinExecutionPeriod = time.Duration(0) + + // DefaultVotingPeriod is the duration from submission of a proposal to the end of voting period + // Within this times votes can be submitted with MsgVote. + DefaultVotingPeriod = time.Minute + + // InitialGroupID is the first group ID generated by x/group + InitialGroupID = 1 + + // InitialProposalID is the first group proposal ID generated by x/group + InitialProposalID = 1 +) + +func TestInterchainAccountsGroupsTestSuite(t *testing.T) { + suite.Run(t, new(InterchainAccountsGroupsTestSuite)) +} + +type InterchainAccountsGroupsTestSuite struct { + testsuite.E2ETestSuite +} + +func (s *InterchainAccountsGroupsTestSuite) QueryGroupPolicyAddress(ctx context.Context, chain ibc.Chain) string { + queryClient := s.GetChainGRCPClients(chain).GroupsQueryClient + res, err := queryClient.GroupPoliciesByGroup(ctx, &grouptypes.QueryGroupPoliciesByGroupRequest{ + GroupId: InitialGroupID, // always use the initial group id + }) + s.Require().NoError(err) + + return res.GroupPolicies[0].Address +} + +func (s *InterchainAccountsGroupsTestSuite) TestInterchainAccountsGroupsIntegration() { + t := s.T() + ctx := context.TODO() + + var ( + groupPolicyAddr string + interchainAccAddr string + err error + ) + + // setup relayers and connection-0 between two chains + // channel-0 is a transfer channel but it will not be used in this test case + relayer, _ := s.SetupChainsRelayerAndChannel(ctx) + chainA, chainB := s.GetChains() + + chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + chainAAddress := chainAWallet.FormattedAddress() + + chainBWallet := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount) + chainBAddress := chainBWallet.FormattedAddress() + + t.Run("create group with new threshold decision policy", func(t *testing.T) { + members := []grouptypes.MemberRequest{ + { + Address: chainAAddress, + Weight: DefaultGroupMemberWeight, + }, + } + + decisionPolicy := grouptypes.NewThresholdDecisionPolicy(DefaultGroupThreshold, DefaultVotingPeriod, DefaultMinExecutionPeriod) + msgCreateGroupWithPolicy, err := grouptypes.NewMsgCreateGroupWithPolicy(chainAAddress, members, DefaultMetadata, DefaultMetadata, true, decisionPolicy) + s.Require().NoError(err) + + txResp, err := s.BroadcastMessages(ctx, chainA, chainAWallet, msgCreateGroupWithPolicy) + s.Require().NoError(err) + s.AssertValidTxResponse(txResp) + }) + + t.Run("submit proposal for MsgRegisterInterchainAccount", func(t *testing.T) { + groupPolicyAddr = s.QueryGroupPolicyAddress(ctx, chainA) + msgRegisterAccount := controllertypes.NewMsgRegisterInterchainAccount(ibctesting.FirstConnectionID, groupPolicyAddr, icatypes.NewDefaultMetadataString(ibctesting.FirstConnectionID, ibctesting.FirstConnectionID)) + + msgSubmitProposal, err := grouptypes.NewMsgSubmitProposal(groupPolicyAddr, []string{chainAAddress}, []sdk.Msg{msgRegisterAccount}, DefaultMetadata, grouptypes.Exec_EXEC_UNSPECIFIED, "e2e groups proposal: for MsgRegisterInterchainAccount", "e2e groups proposal: for MsgRegisterInterchainAccount") + s.Require().NoError(err) + + txResp, err := s.BroadcastMessages(ctx, chainA, chainAWallet, msgSubmitProposal) + s.Require().NoError(err) + s.AssertValidTxResponse(txResp) + }) + + t.Run("vote and exec proposal", func(t *testing.T) { + msgVote := &grouptypes.MsgVote{ + ProposalId: InitialProposalID, + Voter: chainAAddress, + Option: grouptypes.VOTE_OPTION_YES, + Exec: grouptypes.Exec_EXEC_TRY, + } + + txResp, err := s.BroadcastMessages(ctx, chainA, chainAWallet, msgVote) + s.Require().NoError(err) + s.AssertValidTxResponse(txResp) + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer) + }) + + t.Run("verify interchain account registration success", func(t *testing.T) { + interchainAccAddr, err = s.QueryInterchainAccount(ctx, chainA, groupPolicyAddr, ibctesting.FirstConnectionID) + s.Require().NotEmpty(interchainAccAddr) + s.Require().NoError(err) + + channels, err := relayer.GetChannels(ctx, s.GetRelayerExecReporter(), chainA.Config().ChainID) + s.Require().NoError(err) + s.Require().Equal(len(channels), 2) // 1 transfer (created by default), 1 interchain-accounts + }) + + t.Run("fund interchain account wallet", func(t *testing.T) { + err := chainB.SendFunds(ctx, interchaintest.FaucetAccountKeyName, ibc.WalletAmount{ + Address: interchainAccAddr, + Amount: testvalues.StartingTokenAmount, + Denom: chainB.Config().Denom, + }) + s.Require().NoError(err) + }) + + t.Run("submit proposal for MsgSendTx", func(t *testing.T) { + msgBankSend := &banktypes.MsgSend{ + FromAddress: interchainAccAddr, + ToAddress: chainBAddress, + Amount: sdk.NewCoins(testvalues.DefaultTransferAmount(chainB.Config().Denom)), + } + + cdc := testsuite.Codec() + + bz, err := icatypes.SerializeCosmosTx(cdc, []proto.Message{msgBankSend}) + s.Require().NoError(err) + + packetData := icatypes.InterchainAccountPacketData{ + Type: icatypes.EXECUTE_TX, + Data: bz, + Memo: "e2e", + } + + msgSubmitTx := controllertypes.NewMsgSendTx(groupPolicyAddr, ibctesting.FirstConnectionID, uint64(time.Hour.Nanoseconds()), packetData) + msgSubmitProposal, err := grouptypes.NewMsgSubmitProposal(groupPolicyAddr, []string{chainAAddress}, []sdk.Msg{msgSubmitTx}, DefaultMetadata, grouptypes.Exec_EXEC_UNSPECIFIED, "e2e groups proposal: for MsgRegisterInterchainAccount", "e2e groups proposal: for MsgRegisterInterchainAccount") + s.Require().NoError(err) + + txResp, err := s.BroadcastMessages(ctx, chainA, chainAWallet, msgSubmitProposal) + s.Require().NoError(err) + s.AssertValidTxResponse(txResp) + }) + + t.Run("vote and exec proposal", func(t *testing.T) { + msgVote := &grouptypes.MsgVote{ + ProposalId: InitialProposalID + 1, + Voter: chainAAddress, + Option: grouptypes.VOTE_OPTION_YES, + Exec: grouptypes.Exec_EXEC_TRY, + } + + txResp, err := s.BroadcastMessages(ctx, chainA, chainAWallet, msgVote) + s.Require().NoError(err) + s.AssertValidTxResponse(txResp) + }) + + t.Run("verify tokens transferred", func(t *testing.T) { + s.Require().NoError(test.WaitForBlocks(ctx, 10, chainA, chainB), "failed to wait for blocks") + + balance, err := chainB.GetBalance(ctx, chainBAddress, chainB.Config().Denom) + s.Require().NoError(err) + + expected := testvalues.IBCTransferAmount + testvalues.StartingTokenAmount + s.Require().Equal(expected, balance) + + balance, err = chainB.GetBalance(ctx, interchainAccAddr, chainB.Config().Denom) + s.Require().NoError(err) + + expected = testvalues.StartingTokenAmount - testvalues.IBCTransferAmount + s.Require().Equal(expected, balance) + }) +} diff --git a/e2e/tests/interchain_accounts/incentivized_test.go b/e2e/tests/interchain_accounts/incentivized_test.go new file mode 100644 index 00000000000..e91f52eb439 --- /dev/null +++ b/e2e/tests/interchain_accounts/incentivized_test.go @@ -0,0 +1,393 @@ +package interchain_accounts + +import ( + "context" + "testing" + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/cosmos/gogoproto/proto" + interchaintest "github.com/strangelove-ventures/interchaintest/v7" + "github.com/strangelove-ventures/interchaintest/v7/chain/cosmos" + "github.com/strangelove-ventures/interchaintest/v7/ibc" + test "github.com/strangelove-ventures/interchaintest/v7/testutil" + "github.com/stretchr/testify/suite" + + "github.com/cosmos/ibc-go/e2e/testconfig" + "github.com/cosmos/ibc-go/e2e/testsuite" + "github.com/cosmos/ibc-go/e2e/testvalues" + controllertypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/controller/types" + icatypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types" + feetypes "github.com/cosmos/ibc-go/v7/modules/apps/29-fee/types" + ibctesting "github.com/cosmos/ibc-go/v7/testing" +) + +func TestIncentivizedInterchainAccountsTestSuite(t *testing.T) { + suite.Run(t, new(IncentivizedInterchainAccountsTestSuite)) +} + +type IncentivizedInterchainAccountsTestSuite struct { + InterchainAccountsTestSuite +} + +// RegisterCounterPartyPayee broadcasts a MsgRegisterCounterpartyPayee message. +func (s *IncentivizedInterchainAccountsTestSuite) RegisterCounterPartyPayee(ctx context.Context, chain *cosmos.CosmosChain, + user ibc.Wallet, portID, channelID, relayerAddr, counterpartyPayeeAddr string, +) (sdk.TxResponse, error) { + msg := feetypes.NewMsgRegisterCounterpartyPayee(portID, channelID, relayerAddr, counterpartyPayeeAddr) + return s.BroadcastMessages(ctx, chain, user, msg) +} + +func (s *IncentivizedInterchainAccountsTestSuite) TestMsgSendTx_SuccessfulBankSend_Incentivized() { + t := s.T() + ctx := context.TODO() + + // setup relayers and connection-0 between two chains + // channel-0 is a transfer channel but it will not be used in this test case + relayer, _ := s.SetupChainsRelayerAndChannel(ctx) + chainA, chainB := s.GetChains() + + var ( + chainADenom = chainA.Config().Denom + interchainAcc = "" + testFee = testvalues.DefaultFee(chainADenom) + ) + + t.Run("relayer wallets recovered", func(t *testing.T) { + err := s.RecoverRelayerWallets(ctx, relayer) + s.Require().NoError(err) + }) + + chainARelayerWallet, chainBRelayerWallet, err := s.GetRelayerWallets(relayer) + t.Run("relayer wallets fetched", func(t *testing.T) { + s.Require().NoError(err) + }) + + s.Require().NoError(test.WaitForBlocks(ctx, 5, chainA, chainB), "failed to wait for blocks") + + chainARelayerUser, chainBRelayerUser := s.GetRelayerUsers(ctx) + relayerAStartingBalance, err := s.GetChainANativeBalance(ctx, chainARelayerUser) + s.Require().NoError(err) + t.Logf("relayer A user starting with balance: %d", relayerAStartingBalance) + + controllerAccount := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + chainBAccount := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount) + + t.Run("broadcast MsgRegisterInterchainAccount", func(t *testing.T) { + version := getICAVersion(testconfig.GetChainATag(), testconfig.GetChainBTag()) + msgRegisterAccount := controllertypes.NewMsgRegisterInterchainAccount(ibctesting.FirstConnectionID, controllerAccount.FormattedAddress(), version) + + txResp, err := s.BroadcastMessages(ctx, chainA, controllerAccount, msgRegisterAccount) + s.Require().NoError(err) + s.AssertValidTxResponse(txResp) + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer) + }) + + var channelOutput ibc.ChannelOutput + t.Run("verify interchain account", func(t *testing.T) { + var err error + interchainAcc, err = s.QueryInterchainAccount(ctx, chainA, controllerAccount.FormattedAddress(), ibctesting.FirstConnectionID) + s.Require().NoError(err) + s.Require().NotZero(len(interchainAcc)) + + channels, err := relayer.GetChannels(ctx, s.GetRelayerExecReporter(), chainA.Config().ChainID) + s.Require().NoError(err) + s.Require().Equal(len(channels), 2) + + // interchain accounts channel at index: 0 + channelOutput = channels[0] + + s.Require().NoError(test.WaitForBlocks(ctx, 2, chainA, chainB)) + }) + + t.Run("execute interchain account bank send through controller", func(t *testing.T) { + t.Run("fund interchain account wallet on host chainB", func(t *testing.T) { + // fund the interchain account so it has some $$ to send + err := chainB.SendFunds(ctx, interchaintest.FaucetAccountKeyName, ibc.WalletAmount{ + Address: interchainAcc, + Amount: testvalues.StartingTokenAmount, + Denom: chainB.Config().Denom, + }) + s.Require().NoError(err) + }) + + t.Run("register counterparty payee", func(t *testing.T) { + resp, err := s.RegisterCounterPartyPayee(ctx, chainB, chainBRelayerUser, channelOutput.Counterparty.PortID, channelOutput.Counterparty.ChannelID, chainBRelayerWallet.FormattedAddress(), chainARelayerWallet.FormattedAddress()) + s.Require().NoError(err) + s.AssertValidTxResponse(resp) + }) + + t.Run("verify counterparty payee", func(t *testing.T) { + address, err := s.QueryCounterPartyPayee(ctx, chainB, chainBRelayerWallet.FormattedAddress(), channelOutput.Counterparty.ChannelID) + s.Require().NoError(err) + s.Require().Equal(chainARelayerWallet.FormattedAddress(), address) + }) + + t.Run("no incentivized packets", func(t *testing.T) { + packets, err := s.QueryIncentivizedPacketsForChannel(ctx, chainA, channelOutput.PortID, channelOutput.ChannelID) + s.Require().NoError(err) + s.Require().Empty(packets) + }) + + t.Run("stop relayer", func(t *testing.T) { + s.StopRelayer(ctx, relayer) + }) + + t.Run("broadcast incentivized MsgSendTx", func(t *testing.T) { + msgPayPacketFee := &feetypes.MsgPayPacketFee{ + Fee: testvalues.DefaultFee(chainADenom), + SourcePortId: channelOutput.PortID, + SourceChannelId: channelOutput.ChannelID, + Signer: controllerAccount.FormattedAddress(), + } + + msgSend := &banktypes.MsgSend{ + FromAddress: interchainAcc, + ToAddress: chainBAccount.FormattedAddress(), + Amount: sdk.NewCoins(testvalues.DefaultTransferAmount(chainB.Config().Denom)), + } + + cdc := testsuite.Codec() + bz, err := icatypes.SerializeCosmosTx(cdc, []proto.Message{msgSend}) + s.Require().NoError(err) + + packetData := icatypes.InterchainAccountPacketData{ + Type: icatypes.EXECUTE_TX, + Data: bz, + Memo: "e2e", + } + + msgSendTx := controllertypes.NewMsgSendTx(controllerAccount.FormattedAddress(), ibctesting.FirstConnectionID, uint64(time.Hour.Nanoseconds()), packetData) + + resp, err := s.BroadcastMessages(ctx, chainA, controllerAccount, msgPayPacketFee, msgSendTx) + s.AssertValidTxResponse(resp) + s.Require().NoError(err) + + s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB)) + }) + + t.Run("there should be incentivized packets", func(t *testing.T) { + packets, err := s.QueryIncentivizedPacketsForChannel(ctx, chainA, channelOutput.PortID, channelOutput.ChannelID) + s.Require().NoError(err) + s.Require().Len(packets, 1) + actualFee := packets[0].PacketFees[0].Fee + + s.Require().True(actualFee.RecvFee.IsEqual(testFee.RecvFee)) + s.Require().True(actualFee.AckFee.IsEqual(testFee.AckFee)) + s.Require().True(actualFee.TimeoutFee.IsEqual(testFee.TimeoutFee)) + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer) + }) + + t.Run("packets are relayed", func(t *testing.T) { + packets, err := s.QueryIncentivizedPacketsForChannel(ctx, chainA, channelOutput.PortID, channelOutput.ChannelID) + s.Require().NoError(err) + s.Require().Empty(packets) + }) + + t.Run("verify interchain account sent tokens", func(t *testing.T) { + balance, err := chainB.GetBalance(ctx, chainBAccount.FormattedAddress(), chainB.Config().Denom) + s.Require().NoError(err) + + _, err = chainB.GetBalance(ctx, interchainAcc, chainB.Config().Denom) + s.Require().NoError(err) + + expected := testvalues.IBCTransferAmount + testvalues.StartingTokenAmount + s.Require().Equal(expected, balance) + }) + + t.Run("timeout fee is refunded", func(t *testing.T) { + actualBalance, err := s.GetChainANativeBalance(ctx, controllerAccount) + s.Require().NoError(err) + + expected := testvalues.StartingTokenAmount - testFee.AckFee.AmountOf(chainADenom).Int64() - testFee.RecvFee.AmountOf(chainADenom).Int64() + s.Require().Equal(expected, actualBalance) + }) + + t.Run("relayerA is paid ack and recv fee", func(t *testing.T) { + actualBalance, err := s.GetChainANativeBalance(ctx, chainARelayerUser) + s.Require().NoError(err) + + expected := relayerAStartingBalance + testFee.AckFee.AmountOf(chainADenom).Int64() + testFee.RecvFee.AmountOf(chainADenom).Int64() + s.Require().Equal(expected, actualBalance) + }) + }) +} + +func (s *IncentivizedInterchainAccountsTestSuite) TestMsgSendTx_FailedBankSend_Incentivized() { + t := s.T() + ctx := context.TODO() + + // setup relayers and connection-0 between two chains + // channel-0 is a transfer channel but it will not be used in this test case + relayer, _ := s.SetupChainsRelayerAndChannel(ctx) + chainA, chainB := s.GetChains() + + var ( + chainADenom = chainA.Config().Denom + interchainAcc = "" + testFee = testvalues.DefaultFee(chainADenom) + ) + + t.Run("relayer wallets recovered", func(t *testing.T) { + err := s.RecoverRelayerWallets(ctx, relayer) + s.Require().NoError(err) + }) + + chainARelayerWallet, chainBRelayerWallet, err := s.GetRelayerWallets(relayer) + t.Run("relayer wallets fetched", func(t *testing.T) { + s.Require().NoError(err) + }) + + s.Require().NoError(test.WaitForBlocks(ctx, 5, chainA, chainB), "failed to wait for blocks") + + chainARelayerUser, chainBRelayerUser := s.GetRelayerUsers(ctx) + relayerAStartingBalance, err := s.GetChainANativeBalance(ctx, chainARelayerUser) + s.Require().NoError(err) + t.Logf("relayer A user starting with balance: %d", relayerAStartingBalance) + + controllerAccount := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + chainBAccount := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount) + + t.Run("broadcast MsgRegisterInterchainAccount", func(t *testing.T) { + version := getICAVersion(testconfig.GetChainATag(), testconfig.GetChainBTag()) + msgRegisterAccount := controllertypes.NewMsgRegisterInterchainAccount(ibctesting.FirstConnectionID, controllerAccount.FormattedAddress(), version) + + txResp, err := s.BroadcastMessages(ctx, chainA, controllerAccount, msgRegisterAccount) + s.Require().NoError(err) + s.AssertValidTxResponse(txResp) + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer) + }) + + var channelOutput ibc.ChannelOutput + t.Run("verify interchain account", func(t *testing.T) { + var err error + interchainAcc, err = s.QueryInterchainAccount(ctx, chainA, controllerAccount.FormattedAddress(), ibctesting.FirstConnectionID) + s.Require().NoError(err) + s.Require().NotZero(len(interchainAcc)) + + channels, err := relayer.GetChannels(ctx, s.GetRelayerExecReporter(), chainA.Config().ChainID) + s.Require().NoError(err) + s.Require().Equal(len(channels), 2) + + // interchain accounts channel at index: 0 + channelOutput = channels[0] + + s.Require().NoError(test.WaitForBlocks(ctx, 2, chainA, chainB)) + }) + + t.Run("execute interchain account bank send through controller", func(t *testing.T) { + t.Run("register counterparty payee", func(t *testing.T) { + resp, err := s.RegisterCounterPartyPayee(ctx, chainB, chainBRelayerUser, channelOutput.Counterparty.PortID, channelOutput.Counterparty.ChannelID, chainBRelayerWallet.FormattedAddress(), chainARelayerWallet.FormattedAddress()) + s.Require().NoError(err) + s.AssertValidTxResponse(resp) + }) + + t.Run("verify counterparty payee", func(t *testing.T) { + address, err := s.QueryCounterPartyPayee(ctx, chainB, chainBRelayerWallet.FormattedAddress(), channelOutput.Counterparty.ChannelID) + s.Require().NoError(err) + s.Require().Equal(chainARelayerWallet.FormattedAddress(), address) + }) + + t.Run("no incentivized packets", func(t *testing.T) { + packets, err := s.QueryIncentivizedPacketsForChannel(ctx, chainA, channelOutput.PortID, channelOutput.ChannelID) + s.Require().NoError(err) + s.Require().Empty(packets) + }) + + t.Run("stop relayer", func(t *testing.T) { + err := relayer.StopRelayer(ctx, s.GetRelayerExecReporter()) + s.Require().NoError(err) + }) + + t.Run("broadcast incentivized MsgSendTx", func(t *testing.T) { + msgPayPacketFee := &feetypes.MsgPayPacketFee{ + Fee: testvalues.DefaultFee(chainADenom), + SourcePortId: channelOutput.PortID, + SourceChannelId: channelOutput.ChannelID, + Signer: controllerAccount.FormattedAddress(), + } + + msgSend := &banktypes.MsgSend{ + FromAddress: interchainAcc, + ToAddress: chainBAccount.FormattedAddress(), + Amount: sdk.NewCoins(testvalues.DefaultTransferAmount(chainB.Config().Denom)), + } + + cdc := testsuite.Codec() + bz, err := icatypes.SerializeCosmosTx(cdc, []proto.Message{msgSend}) + s.Require().NoError(err) + + packetData := icatypes.InterchainAccountPacketData{ + Type: icatypes.EXECUTE_TX, + Data: bz, + Memo: "e2e", + } + + msgSendTx := controllertypes.NewMsgSendTx(controllerAccount.FormattedAddress(), ibctesting.FirstConnectionID, uint64(time.Hour.Nanoseconds()), packetData) + + resp, err := s.BroadcastMessages(ctx, chainA, controllerAccount, msgPayPacketFee, msgSendTx) + s.AssertValidTxResponse(resp) + s.Require().NoError(err) + + s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB)) + }) + + t.Run("there should be incentivized packets", func(t *testing.T) { + packets, err := s.QueryIncentivizedPacketsForChannel(ctx, chainA, channelOutput.PortID, channelOutput.ChannelID) + s.Require().NoError(err) + s.Require().Len(packets, 1) + actualFee := packets[0].PacketFees[0].Fee + + s.Require().True(actualFee.RecvFee.IsEqual(testFee.RecvFee)) + s.Require().True(actualFee.AckFee.IsEqual(testFee.AckFee)) + s.Require().True(actualFee.TimeoutFee.IsEqual(testFee.TimeoutFee)) + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer) + }) + + t.Run("packets are relayed", func(t *testing.T) { + packets, err := s.QueryIncentivizedPacketsForChannel(ctx, chainA, channelOutput.PortID, channelOutput.ChannelID) + s.Require().NoError(err) + s.Require().Empty(packets) + }) + + t.Run("verify interchain account did not send tokens", func(t *testing.T) { + balance, err := chainB.GetBalance(ctx, chainBAccount.FormattedAddress(), chainB.Config().Denom) + s.Require().NoError(err) + + _, err = chainB.GetBalance(ctx, interchainAcc, chainB.Config().Denom) + s.Require().NoError(err) + + expected := testvalues.StartingTokenAmount + s.Require().Equal(expected, balance, "tokens should not have been sent as interchain account was not funded") + }) + + t.Run("timeout fee is refunded", func(t *testing.T) { + actualBalance, err := s.GetChainANativeBalance(ctx, controllerAccount) + s.Require().NoError(err) + + expected := testvalues.StartingTokenAmount - testFee.AckFee.AmountOf(chainADenom).Int64() - testFee.RecvFee.AmountOf(chainADenom).Int64() + s.Require().Equal(expected, actualBalance) + }) + + t.Run("relayerA is paid ack and recv fee", func(t *testing.T) { + actualBalance, err := s.GetChainANativeBalance(ctx, chainARelayerUser) + s.Require().NoError(err) + + expected := relayerAStartingBalance + testFee.AckFee.AmountOf(chainADenom).Int64() + testFee.RecvFee.AmountOf(chainADenom).Int64() + s.Require().Equal(expected, actualBalance) + }) + }) +} diff --git a/e2e/tests/interchain_accounts/intertx_incentivized_test.go b/e2e/tests/interchain_accounts/intertx_incentivized_test.go new file mode 100644 index 00000000000..a76ae1a990c --- /dev/null +++ b/e2e/tests/interchain_accounts/intertx_incentivized_test.go @@ -0,0 +1,355 @@ +package interchain_accounts + +import ( + "context" + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + intertxtypes "github.com/cosmos/interchain-accounts/x/inter-tx/types" + "github.com/strangelove-ventures/interchaintest/v7" + "github.com/strangelove-ventures/interchaintest/v7/ibc" + test "github.com/strangelove-ventures/interchaintest/v7/testutil" + "github.com/stretchr/testify/suite" + + "github.com/cosmos/ibc-go/e2e/testvalues" + feetypes "github.com/cosmos/ibc-go/v7/modules/apps/29-fee/types" + ibctesting "github.com/cosmos/ibc-go/v7/testing" +) + +func TestIncentivizedInterTxTestSuite(t *testing.T) { + suite.Run(t, new(IncentivizedInterTxTestSuite)) +} + +type IncentivizedInterTxTestSuite struct { + InterTxTestSuite +} + +func (s *IncentivizedInterTxTestSuite) TestMsgSubmitTx_SuccessfulBankSend_Incentivized() { + t := s.T() + ctx := context.TODO() + + // setup relayers and connection-0 between two chains + // channel-0 is a transfer channel but it will not be used in this test case + relayer, _ := s.SetupChainsRelayerAndChannel(ctx) + chainA, chainB := s.GetChains() + + var ( + chainADenom = chainA.Config().Denom + interchainAcc = "" + testFee = testvalues.DefaultFee(chainADenom) + ) + + t.Run("relayer wallets recovered", func(t *testing.T) { + err := s.RecoverRelayerWallets(ctx, relayer) + s.Require().NoError(err) + }) + + chainARelayerWallet, chainBRelayerWallet, err := s.GetRelayerWallets(relayer) + t.Run("relayer wallets fetched", func(t *testing.T) { + s.Require().NoError(err) + }) + + s.Require().NoError(test.WaitForBlocks(ctx, 5, chainA, chainB), "failed to wait for blocks") + + chainARelayerUser, chainBRelayerUser := s.GetRelayerUsers(ctx) + relayerAStartingBalance, err := s.GetChainANativeBalance(ctx, chainARelayerUser) + s.Require().NoError(err) + t.Logf("relayer A user starting with balance: %d", relayerAStartingBalance) + + controllerAccount := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + chainBAccount := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount) + + t.Run("register interchain account", func(t *testing.T) { + version := "" // allow app to handle the version as appropriate. + msgRegisterAccount := intertxtypes.NewMsgRegisterAccount(controllerAccount.FormattedAddress(), ibctesting.FirstConnectionID, version) + err := s.RegisterInterchainAccount(ctx, chainA, controllerAccount, msgRegisterAccount) + s.Require().NoError(err) + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer) + }) + + var channelOutput ibc.ChannelOutput + t.Run("verify interchain account", func(t *testing.T) { + var err error + interchainAcc, err = s.QueryInterchainAccountLegacy(ctx, chainA, controllerAccount.FormattedAddress(), ibctesting.FirstConnectionID) + s.Require().NoError(err) + s.Require().NotZero(len(interchainAcc)) + + channels, err := relayer.GetChannels(ctx, s.GetRelayerExecReporter(), chainA.Config().ChainID) + s.Require().NoError(err) + s.Require().Equal(len(channels), 2) + + // interchain accounts channel at index: 0 + channelOutput = channels[0] + }) + + t.Run("execute interchain account bank send through controller", func(t *testing.T) { + t.Run("fund interchain account wallet on host chainB", func(t *testing.T) { + // fund the interchain account so it has some $$ to send + err := chainB.SendFunds(ctx, interchaintest.FaucetAccountKeyName, ibc.WalletAmount{ + Address: interchainAcc, + Amount: testvalues.StartingTokenAmount, + Denom: chainB.Config().Denom, + }) + s.Require().NoError(err) + }) + + t.Run("register counterparty payee", func(t *testing.T) { + resp, err := s.RegisterCounterPartyPayee(ctx, chainB, chainBRelayerUser, channelOutput.Counterparty.PortID, channelOutput.Counterparty.ChannelID, chainBRelayerWallet.FormattedAddress(), chainARelayerWallet.FormattedAddress()) + s.Require().NoError(err) + s.AssertValidTxResponse(resp) + }) + + t.Run("verify counterparty payee", func(t *testing.T) { + address, err := s.QueryCounterPartyPayee(ctx, chainB, chainBRelayerWallet.FormattedAddress(), channelOutput.Counterparty.ChannelID) + s.Require().NoError(err) + s.Require().Equal(chainARelayerWallet.FormattedAddress(), address) + }) + + t.Run("no incentivized packets", func(t *testing.T) { + packets, err := s.QueryIncentivizedPacketsForChannel(ctx, chainA, channelOutput.PortID, channelOutput.ChannelID) + s.Require().NoError(err) + s.Require().Empty(packets) + }) + + t.Run("stop relayer", func(t *testing.T) { + s.StopRelayer(ctx, relayer) + }) + + t.Run("broadcast incentivized MsgSubmitTx", func(t *testing.T) { + msgPayPacketFee := &feetypes.MsgPayPacketFee{ + Fee: testvalues.DefaultFee(chainADenom), + SourcePortId: channelOutput.PortID, + SourceChannelId: channelOutput.ChannelID, + Signer: controllerAccount.FormattedAddress(), + } + + msgSend := &banktypes.MsgSend{ + FromAddress: interchainAcc, + ToAddress: chainBAccount.FormattedAddress(), + Amount: sdk.NewCoins(testvalues.DefaultTransferAmount(chainB.Config().Denom)), + } + + msgSubmitTx, err := intertxtypes.NewMsgSubmitTx(msgSend, ibctesting.FirstConnectionID, controllerAccount.FormattedAddress()) + s.Require().NoError(err) + + resp, err := s.BroadcastMessages(ctx, chainA, controllerAccount, msgPayPacketFee, msgSubmitTx) + s.AssertValidTxResponse(resp) + s.Require().NoError(err) + + s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB)) + }) + + t.Run("there should be incentivized packets", func(t *testing.T) { + packets, err := s.QueryIncentivizedPacketsForChannel(ctx, chainA, channelOutput.PortID, channelOutput.ChannelID) + s.Require().NoError(err) + s.Require().Len(packets, 1) + actualFee := packets[0].PacketFees[0].Fee + + s.Require().True(actualFee.RecvFee.IsEqual(testFee.RecvFee)) + s.Require().True(actualFee.AckFee.IsEqual(testFee.AckFee)) + s.Require().True(actualFee.TimeoutFee.IsEqual(testFee.TimeoutFee)) + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer) + }) + + t.Run("packets are relayed", func(t *testing.T) { + packets, err := s.QueryIncentivizedPacketsForChannel(ctx, chainA, channelOutput.PortID, channelOutput.ChannelID) + s.Require().NoError(err) + s.Require().Empty(packets) + }) + + t.Run("verify interchain account sent tokens", func(t *testing.T) { + balance, err := chainB.GetBalance(ctx, chainBAccount.FormattedAddress(), chainB.Config().Denom) + s.Require().NoError(err) + + _, err = chainB.GetBalance(ctx, interchainAcc, chainB.Config().Denom) + s.Require().NoError(err) + + expected := testvalues.IBCTransferAmount + testvalues.StartingTokenAmount + s.Require().Equal(expected, balance) + }) + + t.Run("timeout fee is refunded", func(t *testing.T) { + actualBalance, err := s.GetChainANativeBalance(ctx, controllerAccount) + s.Require().NoError(err) + + expected := testvalues.StartingTokenAmount - testFee.AckFee.AmountOf(chainADenom).Int64() - testFee.RecvFee.AmountOf(chainADenom).Int64() + s.Require().Equal(expected, actualBalance) + }) + + t.Run("relayerA is paid ack and recv fee", func(t *testing.T) { + actualBalance, err := s.GetChainANativeBalance(ctx, chainARelayerUser) + s.Require().NoError(err) + + expected := relayerAStartingBalance + testFee.AckFee.AmountOf(chainADenom).Int64() + testFee.RecvFee.AmountOf(chainADenom).Int64() + s.Require().Equal(expected, actualBalance) + }) + }) +} + +func (s *IncentivizedInterTxTestSuite) TestMsgSubmitTx_FailedBankSend_Incentivized() { + t := s.T() + ctx := context.TODO() + + // setup relayers and connection-0 between two chains + // channel-0 is a transfer channel but it will not be used in this test case + relayer, _ := s.SetupChainsRelayerAndChannel(ctx) + chainA, chainB := s.GetChains() + + var ( + chainADenom = chainA.Config().Denom + interchainAcc = "" + testFee = testvalues.DefaultFee(chainADenom) + ) + + t.Run("relayer wallets recovered", func(t *testing.T) { + err := s.RecoverRelayerWallets(ctx, relayer) + s.Require().NoError(err) + }) + + chainARelayerWallet, chainBRelayerWallet, err := s.GetRelayerWallets(relayer) + t.Run("relayer wallets fetched", func(t *testing.T) { + s.Require().NoError(err) + }) + + s.Require().NoError(test.WaitForBlocks(ctx, 5, chainA, chainB), "failed to wait for blocks") + + chainARelayerUser, chainBRelayerUser := s.GetRelayerUsers(ctx) + relayerAStartingBalance, err := s.GetChainANativeBalance(ctx, chainARelayerUser) + s.Require().NoError(err) + t.Logf("relayer A user starting with balance: %d", relayerAStartingBalance) + + controllerAccount := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + chainBAccount := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount) + + t.Run("register interchain account", func(t *testing.T) { + version := "" // allow app to handle the version as appropriate. + msgRegisterAccount := intertxtypes.NewMsgRegisterAccount(controllerAccount.FormattedAddress(), ibctesting.FirstConnectionID, version) + err := s.RegisterInterchainAccount(ctx, chainA, controllerAccount, msgRegisterAccount) + s.Require().NoError(err) + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer) + }) + + var channelOutput ibc.ChannelOutput + t.Run("verify interchain account", func(t *testing.T) { + var err error + interchainAcc, err = s.QueryInterchainAccountLegacy(ctx, chainA, controllerAccount.FormattedAddress(), ibctesting.FirstConnectionID) + s.Require().NoError(err) + s.Require().NotZero(len(interchainAcc)) + + channels, err := relayer.GetChannels(ctx, s.GetRelayerExecReporter(), chainA.Config().ChainID) + s.Require().NoError(err) + s.Require().Equal(len(channels), 2) + + // interchain accounts channel at index: 0 + channelOutput = channels[0] + + s.Require().NoError(test.WaitForBlocks(ctx, 2, chainA, chainB)) + }) + + t.Run("execute interchain account bank send through controller", func(t *testing.T) { + t.Run("register counterparty payee", func(t *testing.T) { + resp, err := s.RegisterCounterPartyPayee(ctx, chainB, chainBRelayerUser, channelOutput.Counterparty.PortID, channelOutput.Counterparty.ChannelID, chainBRelayerWallet.FormattedAddress(), chainARelayerWallet.FormattedAddress()) + s.Require().NoError(err) + s.AssertValidTxResponse(resp) + }) + + t.Run("verify counterparty payee", func(t *testing.T) { + address, err := s.QueryCounterPartyPayee(ctx, chainB, chainBRelayerWallet.FormattedAddress(), channelOutput.Counterparty.ChannelID) + s.Require().NoError(err) + s.Require().Equal(chainARelayerWallet.FormattedAddress(), address) + }) + + t.Run("no incentivized packets", func(t *testing.T) { + packets, err := s.QueryIncentivizedPacketsForChannel(ctx, chainA, channelOutput.PortID, channelOutput.ChannelID) + s.Require().NoError(err) + s.Require().Empty(packets) + }) + + t.Run("stop relayer", func(t *testing.T) { + err := relayer.StopRelayer(ctx, s.GetRelayerExecReporter()) + s.Require().NoError(err) + }) + + t.Run("broadcast incentivized MsgSubmitTx", func(t *testing.T) { + msgPayPacketFee := &feetypes.MsgPayPacketFee{ + Fee: testvalues.DefaultFee(chainADenom), + SourcePortId: channelOutput.PortID, + SourceChannelId: channelOutput.ChannelID, + Signer: controllerAccount.FormattedAddress(), + } + + msgSend := &banktypes.MsgSend{ + FromAddress: interchainAcc, + ToAddress: chainBAccount.FormattedAddress(), + Amount: sdk.NewCoins(testvalues.DefaultTransferAmount(chainB.Config().Denom)), + } + + msgSubmitTx, err := intertxtypes.NewMsgSubmitTx(msgSend, ibctesting.FirstConnectionID, controllerAccount.FormattedAddress()) + s.Require().NoError(err) + + resp, err := s.BroadcastMessages(ctx, chainA, controllerAccount, msgPayPacketFee, msgSubmitTx) + s.AssertValidTxResponse(resp) + s.Require().NoError(err) + + s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB)) + }) + + t.Run("there should be incentivized packets", func(t *testing.T) { + packets, err := s.QueryIncentivizedPacketsForChannel(ctx, chainA, channelOutput.PortID, channelOutput.ChannelID) + s.Require().NoError(err) + s.Require().Len(packets, 1) + actualFee := packets[0].PacketFees[0].Fee + + s.Require().True(actualFee.RecvFee.IsEqual(testFee.RecvFee)) + s.Require().True(actualFee.AckFee.IsEqual(testFee.AckFee)) + s.Require().True(actualFee.TimeoutFee.IsEqual(testFee.TimeoutFee)) + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer) + }) + + t.Run("packets are relayed", func(t *testing.T) { + packets, err := s.QueryIncentivizedPacketsForChannel(ctx, chainA, channelOutput.PortID, channelOutput.ChannelID) + s.Require().NoError(err) + s.Require().Empty(packets) + }) + + t.Run("verify interchain account did not send tokens", func(t *testing.T) { + balance, err := chainB.GetBalance(ctx, chainBAccount.FormattedAddress(), chainB.Config().Denom) + s.Require().NoError(err) + + _, err = chainB.GetBalance(ctx, interchainAcc, chainB.Config().Denom) + s.Require().NoError(err) + + expected := testvalues.StartingTokenAmount + s.Require().Equal(expected, balance, "tokens should not have been sent as interchain account was not funded") + }) + + t.Run("timeout fee is refunded", func(t *testing.T) { + actualBalance, err := s.GetChainANativeBalance(ctx, controllerAccount) + s.Require().NoError(err) + + expected := testvalues.StartingTokenAmount - testFee.AckFee.AmountOf(chainADenom).Int64() - testFee.RecvFee.AmountOf(chainADenom).Int64() + s.Require().Equal(expected, actualBalance) + }) + + t.Run("relayerA is paid ack and recv fee", func(t *testing.T) { + actualBalance, err := s.GetChainANativeBalance(ctx, chainARelayerUser) + s.Require().NoError(err) + + expected := relayerAStartingBalance + testFee.AckFee.AmountOf(chainADenom).Int64() + testFee.RecvFee.AmountOf(chainADenom).Int64() + s.Require().Equal(expected, actualBalance) + }) + }) +} diff --git a/e2e/tests/interchain_accounts/intertx_test.go b/e2e/tests/interchain_accounts/intertx_test.go new file mode 100644 index 00000000000..b70d539a55b --- /dev/null +++ b/e2e/tests/interchain_accounts/intertx_test.go @@ -0,0 +1,215 @@ +package interchain_accounts + +import ( + "context" + "testing" + + "github.com/strangelove-ventures/interchaintest/v7" + "github.com/strangelove-ventures/interchaintest/v7/chain/cosmos" + "github.com/strangelove-ventures/interchaintest/v7/ibc" + test "github.com/strangelove-ventures/interchaintest/v7/testutil" + "github.com/stretchr/testify/suite" + + sdk "github.com/cosmos/cosmos-sdk/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + intertxtypes "github.com/cosmos/interchain-accounts/x/inter-tx/types" + + "github.com/cosmos/ibc-go/e2e/testconfig" + "github.com/cosmos/ibc-go/e2e/testsuite" + "github.com/cosmos/ibc-go/e2e/testvalues" + + ibctesting "github.com/cosmos/ibc-go/v7/testing" +) + +func TestInterTxTestSuite(t *testing.T) { + suite.Run(t, new(InterTxTestSuite)) +} + +type InterTxTestSuite struct { + testsuite.E2ETestSuite +} + +// RegisterInterchainAccount will attempt to register an interchain account on the counterparty chain. +func (s *InterTxTestSuite) RegisterInterchainAccount(ctx context.Context, chain *cosmos.CosmosChain, user ibc.Wallet, msgRegisterAccount *intertxtypes.MsgRegisterAccount) error { + txResp, err := s.BroadcastMessages(ctx, chain, user, msgRegisterAccount) + s.Require().NoError(err) + s.AssertValidTxResponse(txResp) + return err +} + +func (s *InterTxTestSuite) TestMsgSubmitTx_SuccessfulTransfer() { + t := s.T() + ctx := context.TODO() + + // setup relayers and connection-0 between two chains + // channel-0 is a transfer channel but it will not be used in this test case + relayer, _ := s.SetupChainsRelayerAndChannel(ctx) + chainA, chainB := s.GetChains() + + // setup 2 accounts: controller account on chain A, a second chain B account. + // host account will be created when the ICA is registered + controllerAccount := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + chainBAccount := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount) + var hostAccount string + + t.Run("register interchain account", func(t *testing.T) { + version := getICAVersion(testconfig.GetChainATag(), testconfig.GetChainBTag()) + msgRegisterAccount := intertxtypes.NewMsgRegisterAccount(controllerAccount.FormattedAddress(), ibctesting.FirstConnectionID, version) + err := s.RegisterInterchainAccount(ctx, chainA, controllerAccount, msgRegisterAccount) + s.Require().NoError(err) + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer) + }) + + t.Run("verify interchain account", func(t *testing.T) { + var err error + hostAccount, err = s.QueryInterchainAccountLegacy(ctx, chainA, controllerAccount.FormattedAddress(), ibctesting.FirstConnectionID) + s.Require().NoError(err) + s.Require().NotZero(len(hostAccount)) + + channels, err := relayer.GetChannels(ctx, s.GetRelayerExecReporter(), chainA.Config().ChainID) + s.Require().NoError(err) + s.Require().Equal(len(channels), 2) + }) + + t.Run("interchain account executes a bank transfer on behalf of the corresponding owner account", func(t *testing.T) { + t.Run("fund interchain account wallet", func(t *testing.T) { + // fund the host account account so it has some $$ to send + err := chainB.SendFunds(ctx, interchaintest.FaucetAccountKeyName, ibc.WalletAmount{ + Address: hostAccount, + Amount: testvalues.StartingTokenAmount, + Denom: chainB.Config().Denom, + }) + s.Require().NoError(err) + }) + + t.Run("broadcast MsgSubmitTx", func(t *testing.T) { + // assemble bank transfer message from host account to user account on host chain + msgSend := &banktypes.MsgSend{ + FromAddress: hostAccount, + ToAddress: chainBAccount.FormattedAddress(), + Amount: sdk.NewCoins(testvalues.DefaultTransferAmount(chainB.Config().Denom)), + } + + // assemble submitMessage tx for intertx + msgSubmitTx, err := intertxtypes.NewMsgSubmitTx( + msgSend, + ibctesting.FirstConnectionID, + controllerAccount.FormattedAddress(), + ) + s.Require().NoError(err) + + // broadcast submitMessage tx from controller account on chain A + // this message should trigger the sending of an ICA packet over channel-1 (channel created between controller and host) + // this ICA packet contains the assembled bank transfer message from above, which will be executed by the host account on the host chain. + resp, err := s.BroadcastMessages( + ctx, + chainA, + controllerAccount, + msgSubmitTx, + ) + + s.AssertValidTxResponse(resp) + s.Require().NoError(err) + + s.Require().NoError(test.WaitForBlocks(ctx, 10, chainA, chainB)) + }) + + t.Run("verify tokens transferred", func(t *testing.T) { + balance, err := chainB.GetBalance(ctx, chainBAccount.FormattedAddress(), chainB.Config().Denom) + s.Require().NoError(err) + + _, err = chainB.GetBalance(ctx, hostAccount, chainB.Config().Denom) + s.Require().NoError(err) + + expected := testvalues.IBCTransferAmount + testvalues.StartingTokenAmount + s.Require().Equal(expected, balance) + }) + }) +} + +func (s *InterTxTestSuite) TestMsgSubmitTx_FailedTransfer_InsufficientFunds() { + t := s.T() + ctx := context.TODO() + + // setup relayers and connection-0 between two chains + // channel-0 is a transfer channel but it will not be used in this test case + relayer, _ := s.SetupChainsRelayerAndChannel(ctx) + chainA, chainB := s.GetChains() + + // setup 2 accounts: controller account on chain A, a second chain B account. + // host account will be created when the ICA is registered + controllerAccount := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + chainBAccount := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount) + var hostAccount string + + t.Run("register interchain account", func(t *testing.T) { + version := getICAVersion(testconfig.GetChainATag(), testconfig.GetChainBTag()) + msgRegisterAccount := intertxtypes.NewMsgRegisterAccount(controllerAccount.FormattedAddress(), ibctesting.FirstConnectionID, version) + err := s.RegisterInterchainAccount(ctx, chainA, controllerAccount, msgRegisterAccount) + s.Require().NoError(err) + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer) + }) + + t.Run("verify interchain account", func(t *testing.T) { + var err error + hostAccount, err = s.QueryInterchainAccountLegacy(ctx, chainA, controllerAccount.FormattedAddress(), ibctesting.FirstConnectionID) + s.Require().NoError(err) + s.Require().NotZero(len(hostAccount)) + + channels, err := relayer.GetChannels(ctx, s.GetRelayerExecReporter(), chainA.Config().ChainID) + s.Require().NoError(err) + s.Require().Equal(len(channels), 2) + }) + + t.Run("fail to execute bank transfer over ICA", func(t *testing.T) { + t.Run("verify empty host wallet", func(t *testing.T) { + hostAccountBalance, err := chainB.GetBalance(ctx, hostAccount, chainB.Config().Denom) + s.Require().NoError(err) + s.Require().Zero(hostAccountBalance) + }) + + t.Run("broadcast MsgSubmitTx", func(t *testing.T) { + // assemble bank transfer message from host account to user account on host chain + transferMsg := &banktypes.MsgSend{ + FromAddress: hostAccount, + ToAddress: chainBAccount.FormattedAddress(), + Amount: sdk.NewCoins(testvalues.DefaultTransferAmount(chainB.Config().Denom)), + } + + // assemble submitMessage tx for intertx + submitMsg, err := intertxtypes.NewMsgSubmitTx( + transferMsg, + ibctesting.FirstConnectionID, + controllerAccount.FormattedAddress(), + ) + s.Require().NoError(err) + + // broadcast submitMessage tx from controller account on chain A + // this message should trigger the sending of an ICA packet over channel-1 (channel created between controller and host) + // this ICA packet contains the assembled bank transfer message from above, which will be executed by the host account on the host chain. + resp, err := s.BroadcastMessages( + ctx, + chainA, + controllerAccount, + submitMsg, + ) + + s.AssertValidTxResponse(resp) + s.Require().NoError(err) + }) + + t.Run("verify balance is the same", func(t *testing.T) { + balance, err := chainB.GetBalance(ctx, chainBAccount.FormattedAddress(), chainB.Config().Denom) + s.Require().NoError(err) + + expected := testvalues.StartingTokenAmount + s.Require().Equal(expected, balance) + }) + }) +} diff --git a/e2e/tests/transfer/authz_test.go b/e2e/tests/transfer/authz_test.go new file mode 100644 index 00000000000..2906122d0d0 --- /dev/null +++ b/e2e/tests/transfer/authz_test.go @@ -0,0 +1,329 @@ +package transfer + +import ( + "context" + "testing" + + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/x/authz" + test "github.com/strangelove-ventures/interchaintest/v7/testutil" + "github.com/stretchr/testify/suite" + + "github.com/cosmos/ibc-go/e2e/testsuite" + "github.com/cosmos/ibc-go/e2e/testvalues" + transfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" +) + +func TestAuthzTransferTestSuite(t *testing.T) { + suite.Run(t, new(AuthzTransferTestSuite)) +} + +type AuthzTransferTestSuite struct { + testsuite.E2ETestSuite +} + +func (suite *AuthzTransferTestSuite) TestAuthz_MsgTransfer_Succeeds() { + t := suite.T() + ctx := context.TODO() + + relayer, channelA := suite.SetupChainsRelayerAndChannel(ctx, transferChannelOptions()) + chainA, chainB := suite.GetChains() + + chainADenom := chainA.Config().Denom + + granterWallet := suite.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + granterAddress := granterWallet.FormattedAddress() + + granteeWallet := suite.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + granteeAddress := granteeWallet.FormattedAddress() + + receiverWallet := suite.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount) + receiverWalletAddress := receiverWallet.FormattedAddress() + + t.Run("start relayer", func(t *testing.T) { + suite.StartRelayer(relayer) + }) + + // createMsgGrantFn initializes a TransferAuthorization and broadcasts a MsgGrant message. + createMsgGrantFn := func(t *testing.T) { + transferAuth := transfertypes.TransferAuthorization{ + Allocations: []transfertypes.Allocation{ + { + SourcePort: channelA.PortID, + SourceChannel: channelA.ChannelID, + SpendLimit: sdk.NewCoins(sdk.NewCoin(chainADenom, sdk.NewInt(testvalues.StartingTokenAmount))), + AllowList: []string{receiverWalletAddress}, + }, + }, + } + + protoAny, err := codectypes.NewAnyWithValue(&transferAuth) + suite.Require().NoError(err) + + msgGrant := &authz.MsgGrant{ + Granter: granterAddress, + Grantee: granteeAddress, + Grant: authz.Grant{ + Authorization: protoAny, + // no expiration + Expiration: nil, + }, + } + + resp, err := suite.BroadcastMessages(context.TODO(), chainA, granterWallet, msgGrant) + suite.AssertValidTxResponse(resp) + suite.Require().NoError(err) + } + + // verifyGrantFn returns a test function which asserts chainA has a grant authorization + // with the given spend limit. + verifyGrantFn := func(expectedLimit int64) func(t *testing.T) { + return func(t *testing.T) { + grantAuths, err := suite.QueryGranterGrants(ctx, chainA, granterAddress) + + suite.Require().NoError(err) + suite.Require().Len(grantAuths, 1) + grantAuthorization := grantAuths[0] + + transferAuth := suite.extractTransferAuthorizationFromGrantAuthorization(grantAuthorization) + expectedSpendLimit := sdk.NewCoins(sdk.NewCoin(chainADenom, sdk.NewInt(expectedLimit))) + suite.Require().Equal(expectedSpendLimit, transferAuth.Allocations[0].SpendLimit) + } + } + + t.Run("broadcast MsgGrant", createMsgGrantFn) + + t.Run("broadcast MsgExec for ibc MsgTransfer", func(t *testing.T) { + transferMsg := transfertypes.MsgTransfer{ + SourcePort: channelA.PortID, + SourceChannel: channelA.ChannelID, + Token: testvalues.DefaultTransferAmount(chainADenom), + Sender: granterAddress, + Receiver: receiverWalletAddress, + TimeoutHeight: suite.GetTimeoutHeight(ctx, chainB), + } + + protoAny, err := codectypes.NewAnyWithValue(&transferMsg) + suite.Require().NoError(err) + + msgExec := &authz.MsgExec{ + Grantee: granteeAddress, + Msgs: []*codectypes.Any{protoAny}, + } + + resp, err := suite.BroadcastMessages(context.TODO(), chainA, granteeWallet, msgExec) + suite.AssertValidTxResponse(resp) + suite.Require().NoError(err) + }) + + t.Run("verify granter wallet amount", func(t *testing.T) { + actualBalance, err := suite.GetChainANativeBalance(ctx, granterWallet) + suite.Require().NoError(err) + + expected := testvalues.StartingTokenAmount - testvalues.IBCTransferAmount + suite.Require().Equal(expected, actualBalance) + }) + + suite.Require().NoError(test.WaitForBlocks(context.TODO(), 10, chainB)) + + t.Run("verify receiver wallet amount", func(t *testing.T) { + chainBIBCToken := testsuite.GetIBCToken(chainADenom, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID) + actualBalance, err := chainB.GetBalance(ctx, receiverWalletAddress, chainBIBCToken.IBCDenom()) + suite.Require().NoError(err) + suite.Require().Equal(testvalues.IBCTransferAmount, actualBalance) + }) + + t.Run("granter grant spend limit reduced", verifyGrantFn(testvalues.StartingTokenAmount-testvalues.IBCTransferAmount)) + + t.Run("re-initialize MsgGrant", createMsgGrantFn) + + t.Run("granter grant was reinitialized", verifyGrantFn(testvalues.StartingTokenAmount)) + + t.Run("revoke access", func(t *testing.T) { + msgRevoke := authz.MsgRevoke{ + Granter: granterAddress, + Grantee: granteeAddress, + MsgTypeUrl: transfertypes.TransferAuthorization{}.MsgTypeURL(), + } + + resp, err := suite.BroadcastMessages(context.TODO(), chainA, granterWallet, &msgRevoke) + suite.AssertValidTxResponse(resp) + suite.Require().NoError(err) + }) + + t.Run("exec unauthorized MsgTransfer", func(t *testing.T) { + transferMsg := transfertypes.MsgTransfer{ + SourcePort: channelA.PortID, + SourceChannel: channelA.ChannelID, + Token: testvalues.DefaultTransferAmount(chainADenom), + Sender: granterAddress, + Receiver: receiverWalletAddress, + TimeoutHeight: suite.GetTimeoutHeight(ctx, chainB), + } + + protoAny, err := codectypes.NewAnyWithValue(&transferMsg) + suite.Require().NoError(err) + + msgExec := &authz.MsgExec{ + Grantee: granteeAddress, + Msgs: []*codectypes.Any{protoAny}, + } + + resp, err := suite.BroadcastMessages(context.TODO(), chainA, granteeWallet, msgExec) + suite.Require().NotEqual(0, resp.Code) + suite.Require().Contains(resp.RawLog, authz.ErrNoAuthorizationFound.Error()) + suite.Require().NoError(err) + }) +} + +func (suite *AuthzTransferTestSuite) TestAuthz_InvalidTransferAuthorizations() { + t := suite.T() + ctx := context.TODO() + + relayer, channelA := suite.SetupChainsRelayerAndChannel(ctx, transferChannelOptions()) + chainA, chainB := suite.GetChains() + + chainADenom := chainA.Config().Denom + + granterWallet := suite.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + granterAddress := granterWallet.FormattedAddress() + + granteeWallet := suite.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + granteeAddress := granteeWallet.FormattedAddress() + + receiverWallet := suite.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount) + receiverWalletAddress := receiverWallet.FormattedAddress() + + t.Run("start relayer", func(t *testing.T) { + suite.StartRelayer(relayer) + }) + + const spendLimit = 1000 + + t.Run("broadcast MsgGrant", func(t *testing.T) { + transferAuth := transfertypes.TransferAuthorization{ + Allocations: []transfertypes.Allocation{ + { + SourcePort: channelA.PortID, + SourceChannel: channelA.ChannelID, + SpendLimit: sdk.NewCoins(sdk.NewCoin(chainADenom, sdk.NewInt(spendLimit))), + AllowList: []string{receiverWalletAddress}, + }, + }, + } + + protoAny, err := codectypes.NewAnyWithValue(&transferAuth) + suite.Require().NoError(err) + + msgGrant := &authz.MsgGrant{ + Granter: granterAddress, + Grantee: granteeAddress, + Grant: authz.Grant{ + Authorization: protoAny, + // no expiration + Expiration: nil, + }, + } + + resp, err := suite.BroadcastMessages(context.TODO(), chainA, granterWallet, msgGrant) + suite.AssertValidTxResponse(resp) + suite.Require().NoError(err) + }) + + t.Run("exceed spend limit", func(t *testing.T) { + const invalidSpendAmount = spendLimit + 1 + + t.Run("broadcast MsgExec for ibc MsgTransfer", func(t *testing.T) { + transferMsg := transfertypes.MsgTransfer{ + SourcePort: channelA.PortID, + SourceChannel: channelA.ChannelID, + Token: sdk.Coin{Denom: chainADenom, Amount: sdk.NewInt(invalidSpendAmount)}, + Sender: granterAddress, + Receiver: receiverWalletAddress, + TimeoutHeight: suite.GetTimeoutHeight(ctx, chainB), + } + + protoAny, err := codectypes.NewAnyWithValue(&transferMsg) + suite.Require().NoError(err) + + msgExec := &authz.MsgExec{ + Grantee: granteeAddress, + Msgs: []*codectypes.Any{protoAny}, + } + + resp, err := suite.BroadcastMessages(context.TODO(), chainA, granteeWallet, msgExec) + suite.Require().NotEqual(0, resp.Code) + suite.Require().Contains(resp.RawLog, sdkerrors.ErrInsufficientFunds.Error()) + suite.Require().NoError(err) + }) + + t.Run("verify granter wallet amount", func(t *testing.T) { + actualBalance, err := suite.GetChainANativeBalance(ctx, granterWallet) + suite.Require().NoError(err) + suite.Require().Equal(testvalues.StartingTokenAmount, actualBalance) + }) + + t.Run("verify receiver wallet amount", func(t *testing.T) { + chainBIBCToken := testsuite.GetIBCToken(chainADenom, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID) + actualBalance, err := chainB.GetBalance(ctx, receiverWalletAddress, chainBIBCToken.IBCDenom()) + suite.Require().NoError(err) + suite.Require().Equal(int64(0), actualBalance) + }) + + t.Run("granter grant spend limit unchanged", func(t *testing.T) { + grantAuths, err := suite.QueryGranterGrants(ctx, chainA, granterAddress) + + suite.Require().NoError(err) + suite.Require().Len(grantAuths, 1) + grantAuthorization := grantAuths[0] + + transferAuth := suite.extractTransferAuthorizationFromGrantAuthorization(grantAuthorization) + expectedSpendLimit := sdk.NewCoins(sdk.NewCoin(chainADenom, sdk.NewInt(spendLimit))) + suite.Require().Equal(expectedSpendLimit, transferAuth.Allocations[0].SpendLimit) + }) + }) + + t.Run("send funds to invalid address", func(t *testing.T) { + invalidWallet := suite.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount) + invalidWalletAddress := invalidWallet.FormattedAddress() + + t.Run("broadcast MsgExec for ibc MsgTransfer", func(t *testing.T) { + transferMsg := transfertypes.MsgTransfer{ + SourcePort: channelA.PortID, + SourceChannel: channelA.ChannelID, + Token: sdk.Coin{Denom: chainADenom, Amount: sdk.NewInt(spendLimit)}, + Sender: granterAddress, + Receiver: invalidWalletAddress, + TimeoutHeight: suite.GetTimeoutHeight(ctx, chainB), + } + + protoAny, err := codectypes.NewAnyWithValue(&transferMsg) + suite.Require().NoError(err) + + msgExec := &authz.MsgExec{ + Grantee: granteeAddress, + Msgs: []*codectypes.Any{protoAny}, + } + + resp, err := suite.BroadcastMessages(context.TODO(), chainA, granteeWallet, msgExec) + suite.Require().NotEqual(0, resp.Code) + suite.Require().Contains(resp.RawLog, sdkerrors.ErrInvalidAddress.Error()) + suite.Require().NoError(err) + }) + }) +} + +// extractTransferAuthorizationFromGrantAuthorization extracts a TransferAuthorization from the given +// GrantAuthorization. +func (suite *AuthzTransferTestSuite) extractTransferAuthorizationFromGrantAuthorization(grantAuth *authz.GrantAuthorization) *transfertypes.TransferAuthorization { + cfg := testsuite.EncodingConfig() + var authorization authz.Authorization + err := cfg.InterfaceRegistry.UnpackAny(grantAuth.Authorization, &authorization) + suite.Require().NoError(err) + + transferAuth, ok := authorization.(*transfertypes.TransferAuthorization) + suite.Require().True(ok) + return transferAuth +} diff --git a/e2e/tests/transfer/base_test.go b/e2e/tests/transfer/base_test.go new file mode 100644 index 00000000000..d858bc04058 --- /dev/null +++ b/e2e/tests/transfer/base_test.go @@ -0,0 +1,472 @@ +package transfer + +import ( + "context" + "strconv" + "testing" + "time" + + paramsproposaltypes "github.com/cosmos/cosmos-sdk/x/params/types/proposal" + "github.com/strangelove-ventures/interchaintest/v7/ibc" + test "github.com/strangelove-ventures/interchaintest/v7/testutil" + "github.com/stretchr/testify/suite" + + "github.com/cosmos/ibc-go/e2e/semverutil" + "github.com/cosmos/ibc-go/e2e/testsuite" + "github.com/cosmos/ibc-go/e2e/testvalues" + transfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" + ibctesting "github.com/cosmos/ibc-go/v7/testing" +) + +func TestTransferTestSuite(t *testing.T) { + suite.Run(t, new(TransferTestSuite)) +} + +type TransferTestSuite struct { + testsuite.E2ETestSuite +} + +// QueryTransferSendEnabledParam queries the on-chain send enabled param for the transfer module +func (s *TransferTestSuite) QueryTransferSendEnabledParam(ctx context.Context, chain ibc.Chain) bool { + queryClient := s.GetChainGRCPClients(chain).ParamsQueryClient + res, err := queryClient.Params(ctx, ¶msproposaltypes.QueryParamsRequest{ + Subspace: transfertypes.StoreKey, + Key: string(transfertypes.KeySendEnabled), + }) + s.Require().NoError(err) + + enabled, err := strconv.ParseBool(res.Param.Value) + s.Require().NoError(err) + + return enabled +} + +// QueryTransferReceiveEnabledParam queries the on-chain receive enabled param for the transfer module +func (s *TransferTestSuite) QueryTransferReceiveEnabledParam(ctx context.Context, chain ibc.Chain) bool { + queryClient := s.GetChainGRCPClients(chain).ParamsQueryClient + res, err := queryClient.Params(ctx, ¶msproposaltypes.QueryParamsRequest{ + Subspace: transfertypes.StoreKey, + Key: string(transfertypes.KeyReceiveEnabled), + }) + s.Require().NoError(err) + + enabled, err := strconv.ParseBool(res.Param.Value) + s.Require().NoError(err) + + return enabled +} + +// TestMsgTransfer_Succeeds_Nonincentivized will test sending successful IBC transfers from chainA to chainB. +// The transfer will occur over a basic transfer channel (non incentivized) and both native and non-native tokens +// will be sent forwards and backwards in the IBC transfer timeline (both chains will act as source and receiver chains). +func (s *TransferTestSuite) TestMsgTransfer_Succeeds_Nonincentivized() { + t := s.T() + ctx := context.TODO() + + relayer, channelA := s.SetupChainsRelayerAndChannel(ctx, transferChannelOptions()) + chainA, chainB := s.GetChains() + + chainADenom := chainA.Config().Denom + + chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + chainAAddress := chainAWallet.FormattedAddress() + + chainBWallet := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount) + chainBAddress := chainBWallet.FormattedAddress() + + s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB), "failed to wait for blocks") + + t.Run("native IBC token transfer from chainA to chainB, sender is source of tokens", func(t *testing.T) { + transferTxResp, err := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, testvalues.DefaultTransferAmount(chainADenom), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "") + s.Require().NoError(err) + s.AssertValidTxResponse(transferTxResp) + }) + + t.Run("tokens are escrowed", func(t *testing.T) { + actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet) + s.Require().NoError(err) + + expected := testvalues.StartingTokenAmount - testvalues.IBCTransferAmount + s.Require().Equal(expected, actualBalance) + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer) + }) + + chainBIBCToken := testsuite.GetIBCToken(chainADenom, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID) + + t.Run("packets are relayed", func(t *testing.T) { + s.AssertPacketRelayed(ctx, chainA, channelA.PortID, channelA.ChannelID, 1) + + actualBalance, err := chainB.GetBalance(ctx, chainBAddress, chainBIBCToken.IBCDenom()) + s.Require().NoError(err) + + expected := testvalues.IBCTransferAmount + s.Require().Equal(expected, actualBalance) + }) + + t.Run("non-native IBC token transfer from chainB to chainA, receiver is source of tokens", func(t *testing.T) { + transferTxResp, err := s.Transfer(ctx, chainB, chainBWallet, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, testvalues.DefaultTransferAmount(chainBIBCToken.IBCDenom()), chainBAddress, chainAAddress, s.GetTimeoutHeight(ctx, chainA), 0, "") + s.Require().NoError(err) + s.AssertValidTxResponse(transferTxResp) + }) + + t.Run("tokens are escrowed", func(t *testing.T) { + actualBalance, err := chainB.GetBalance(ctx, chainBAddress, chainBIBCToken.IBCDenom()) + s.Require().NoError(err) + + s.Require().Equal(int64(0), actualBalance) + }) + + s.Require().NoError(test.WaitForBlocks(ctx, 5, chainA, chainB), "failed to wait for blocks") + + t.Run("packets are relayed", func(t *testing.T) { + s.AssertPacketRelayed(ctx, chainB, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, 1) + + actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet) + s.Require().NoError(err) + + expected := testvalues.StartingTokenAmount + s.Require().Equal(expected, actualBalance) + }) +} + +// TestMsgTransfer_Fails_InvalidAddress attempts to send an IBC transfer to an invalid address and ensures +// that the tokens on the sending chain are unescrowed. +func (s *TransferTestSuite) TestMsgTransfer_Fails_InvalidAddress() { + t := s.T() + ctx := context.TODO() + + relayer, channelA := s.SetupChainsRelayerAndChannel(ctx, transferChannelOptions()) + chainA, chainB := s.GetChains() + + chainADenom := chainA.Config().Denom + + chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + chainAAddress := chainAWallet.FormattedAddress() + + s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB), "failed to wait for blocks") + + t.Run("native IBC token transfer from chainA to invalid address", func(t *testing.T) { + transferTxResp, err := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, testvalues.DefaultTransferAmount(chainADenom), chainAAddress, testvalues.InvalidAddress, s.GetTimeoutHeight(ctx, chainB), 0, "") + s.Require().NoError(err) + s.AssertValidTxResponse(transferTxResp) + }) + + t.Run("tokens are escrowed", func(t *testing.T) { + actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet) + s.Require().NoError(err) + + expected := testvalues.StartingTokenAmount - testvalues.IBCTransferAmount + s.Require().Equal(expected, actualBalance) + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer) + }) + + t.Run("packets are relayed", func(t *testing.T) { + s.AssertPacketRelayed(ctx, chainA, channelA.PortID, channelA.ChannelID, 1) + }) + + t.Run("token transfer amount unescrowed", func(t *testing.T) { + actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet) + s.Require().NoError(err) + + expected := testvalues.StartingTokenAmount + s.Require().Equal(expected, actualBalance) + }) +} + +func (s *TransferTestSuite) TestMsgTransfer_Timeout_Nonincentivized() { + t := s.T() + ctx := context.TODO() + + relayer, channelA := s.SetupChainsRelayerAndChannel(ctx, transferChannelOptions()) + chainA, _ := s.GetChains() + chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + chainBWallet := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount) + + chainBWalletAmount := ibc.WalletAmount{ + Address: chainBWallet.FormattedAddress(), // destination address + Denom: chainA.Config().Denom, + Amount: testvalues.IBCTransferAmount, + } + + t.Run("IBC transfer packet timesout", func(t *testing.T) { + tx, err := chainA.SendIBCTransfer(ctx, channelA.ChannelID, chainAWallet.KeyName(), chainBWalletAmount, ibc.TransferOptions{Timeout: testvalues.ImmediatelyTimeout()}) + s.Require().NoError(err) + s.Require().NoError(tx.Validate(), "source ibc transfer tx is invalid") + time.Sleep(time.Nanosecond * 1) // want it to timeout immediately + }) + + t.Run("tokens are escrowed", func(t *testing.T) { + actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet) + s.Require().NoError(err) + + expected := testvalues.StartingTokenAmount - testvalues.IBCTransferAmount + s.Require().Equal(expected, actualBalance) + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer) + }) + + t.Run("ensure escrowed tokens have been refunded to sender due to timeout", func(t *testing.T) { + // ensure destination address did not receive any tokens + bal, err := s.GetChainBNativeBalance(ctx, chainBWallet) + s.Require().NoError(err) + s.Require().Equal(testvalues.StartingTokenAmount, bal) + + // ensure that the sender address has been successfully refunded the full amount + bal, err = s.GetChainANativeBalance(ctx, chainAWallet) + s.Require().NoError(err) + s.Require().Equal(testvalues.StartingTokenAmount, bal) + }) +} + +// TestSendEnabledParam tests changing ics20 SendEnabled parameter +func (s *TransferTestSuite) TestSendEnabledParam() { + t := s.T() + ctx := context.TODO() + + _, channelA := s.SetupChainsRelayerAndChannel(ctx, transferChannelOptions()) + chainA, chainB := s.GetChains() + + chainADenom := chainA.Config().Denom + + chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + chainAAddress := chainAWallet.FormattedAddress() + + chainBWallet := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount) + chainBAddress := chainBWallet.FormattedAddress() + + s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB), "failed to wait for blocks") + + t.Run("ensure transfer sending is enabled", func(t *testing.T) { + enabled := s.QueryTransferSendEnabledParam(ctx, chainA) + s.Require().True(enabled) + }) + + t.Run("ensure packets can be sent", func(t *testing.T) { + transferTxResp, err := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, testvalues.DefaultTransferAmount(chainADenom), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "") + s.Require().NoError(err) + s.AssertValidTxResponse(transferTxResp) + }) + + t.Run("change send enabled parameter to disabled", func(t *testing.T) { + changes := []paramsproposaltypes.ParamChange{ + paramsproposaltypes.NewParamChange(transfertypes.StoreKey, string(transfertypes.KeySendEnabled), "false"), + } + + proposal := paramsproposaltypes.NewParameterChangeProposal(ibctesting.Title, ibctesting.Description, changes) + s.ExecuteGovProposal(ctx, chainA, chainAWallet, proposal) + }) + + t.Run("ensure transfer params are disabled", func(t *testing.T) { + enabled := s.QueryTransferSendEnabledParam(ctx, chainA) + s.Require().False(enabled) + }) + + t.Run("ensure ics20 transfer fails", func(t *testing.T) { + transferTxResp, err := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, testvalues.DefaultTransferAmount(chainADenom), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "") + s.Require().NoError(err) + s.Require().Equal(transfertypes.ErrSendDisabled.ABCICode(), transferTxResp.Code) + }) +} + +// TestReceiveEnabledParam tests changing ics20 ReceiveEnabled parameter +func (s *TransferTestSuite) TestReceiveEnabledParam() { + t := s.T() + ctx := context.TODO() + + relayer, channelA := s.SetupChainsRelayerAndChannel(ctx, transferChannelOptions()) + chainA, chainB := s.GetChains() + + chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + chainBWallet := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount) + + var ( + chainBDenom = chainB.Config().Denom + chainAIBCToken = testsuite.GetIBCToken(chainBDenom, channelA.PortID, channelA.ChannelID) // IBC token sent to chainA + + chainAAddress = chainAWallet.FormattedAddress() + chainBAddress = chainBWallet.FormattedAddress() + ) + + s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB), "failed to wait for blocks") + + t.Run("ensure transfer receive is enabled", func(t *testing.T) { + enabled := s.QueryTransferReceiveEnabledParam(ctx, chainA) + s.Require().True(enabled) + }) + + t.Run("ensure packets can be received, send from chainB to chainA", func(t *testing.T) { + t.Run("send from chainB to chainA", func(t *testing.T) { + transferTxResp, err := s.Transfer(ctx, chainB, chainBWallet, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, testvalues.DefaultTransferAmount(chainBDenom), chainBAddress, chainAAddress, s.GetTimeoutHeight(ctx, chainA), 0, "") + s.Require().NoError(err) + s.AssertValidTxResponse(transferTxResp) + }) + + t.Run("tokens are escrowed", func(t *testing.T) { + actualBalance, err := s.GetChainBNativeBalance(ctx, chainBWallet) + s.Require().NoError(err) + + expected := testvalues.StartingTokenAmount - testvalues.IBCTransferAmount + s.Require().Equal(expected, actualBalance) + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer) + }) + + t.Run("packets are relayed", func(t *testing.T) { + s.AssertPacketRelayed(ctx, chainA, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, 1) + + actualBalance, err := chainA.GetBalance(ctx, chainAAddress, chainAIBCToken.IBCDenom()) + s.Require().NoError(err) + + expected := testvalues.IBCTransferAmount + s.Require().Equal(expected, actualBalance) + }) + + t.Run("stop relayer", func(t *testing.T) { + s.StopRelayer(ctx, relayer) + }) + }) + + t.Run("change receive enabled parameter to disabled ", func(t *testing.T) { + changes := []paramsproposaltypes.ParamChange{ + paramsproposaltypes.NewParamChange(transfertypes.StoreKey, string(transfertypes.KeyReceiveEnabled), "false"), + } + + proposal := paramsproposaltypes.NewParameterChangeProposal(ibctesting.Title, ibctesting.Description, changes) + s.ExecuteGovProposal(ctx, chainA, chainAWallet, proposal) + }) + + t.Run("ensure transfer params are disabled", func(t *testing.T) { + enabled := s.QueryTransferReceiveEnabledParam(ctx, chainA) + s.Require().False(enabled) + }) + + t.Run("ensure ics20 transfer fails", func(t *testing.T) { + t.Run("send from chainB to chainA", func(t *testing.T) { + transferTxResp, err := s.Transfer(ctx, chainB, chainBWallet, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, testvalues.DefaultTransferAmount(chainBDenom), chainBAddress, chainAAddress, s.GetTimeoutHeight(ctx, chainA), 0, "") + s.Require().NoError(err) + s.AssertValidTxResponse(transferTxResp) + }) + + t.Run("tokens are escrowed", func(t *testing.T) { + actualBalance, err := s.GetChainBNativeBalance(ctx, chainBWallet) + s.Require().NoError(err) + + expected := testvalues.StartingTokenAmount - (testvalues.IBCTransferAmount * 2) // second send + s.Require().Equal(expected, actualBalance) + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer) + }) + + t.Run("tokens are unescrowed in failed acknowledgement", func(t *testing.T) { + actualBalance, err := s.GetChainBNativeBalance(ctx, chainBWallet) + s.Require().NoError(err) + + expected := testvalues.StartingTokenAmount - testvalues.IBCTransferAmount // only first send marked + s.Require().Equal(expected, actualBalance) + }) + }) +} + +// memoFeatureReleases represents the releases the memo field was released in. +var memoFeatureReleases = semverutil.FeatureReleases{ + MajorVersion: "v6", + MinorVersions: []string{ + "v2.5", + "v3.4", + "v4.2", + "v5.1", + }, +} + +// This can be used to test sending with a transfer packet with a memo given different combinations of +// ibc-go versions. +// +// TestMsgTransfer_WithMemo will test sending IBC transfers from chainA to chainB +// If the chains contain a version of FungibleTokenPacketData with memo, both send and receive should succeed. +// If one of the chains contains a version of FungibleTokenPacketData without memo, then receiving a packet with +// memo should fail in that chain +func (s *TransferTestSuite) TestMsgTransfer_WithMemo() { + t := s.T() + ctx := context.TODO() + + relayer, channelA := s.SetupChainsRelayerAndChannel(ctx, transferChannelOptions()) + chainA, chainB := s.GetChains() + + chainADenom := chainA.Config().Denom + + chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + chainAAddress := chainAWallet.FormattedAddress() + + chainBWallet := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount) + chainBAddress := chainBWallet.FormattedAddress() + + s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB), "failed to wait for blocks") + + chainAVersion := chainA.Config().Images[0].Version + chainBVersion := chainB.Config().Images[0].Version + + t.Run("IBC token transfer with memo from chainA to chainB", func(t *testing.T) { + transferTxResp, err := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, testvalues.DefaultTransferAmount(chainADenom), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "memo") + s.Require().NoError(err) + + if memoFeatureReleases.IsSupported(chainAVersion) { + s.AssertValidTxResponse(transferTxResp) + } else { + s.Require().Equal(uint32(2), transferTxResp.Code) + s.Require().Contains(transferTxResp.RawLog, "errUnknownField") + } + }) + + if !memoFeatureReleases.IsSupported(chainAVersion) { + // transfer not sent, end test + return + } + + t.Run("tokens are escrowed", func(t *testing.T) { + actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet) + s.Require().NoError(err) + + expected := testvalues.StartingTokenAmount - testvalues.IBCTransferAmount + s.Require().Equal(expected, actualBalance) + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer) + }) + + chainBIBCToken := testsuite.GetIBCToken(chainADenom, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID) + + t.Run("packets relayed", func(t *testing.T) { + s.AssertPacketRelayed(ctx, chainA, channelA.PortID, channelA.ChannelID, 1) + + actualBalance, err := chainB.GetBalance(ctx, chainBAddress, chainBIBCToken.IBCDenom()) + s.Require().NoError(err) + + if memoFeatureReleases.IsSupported(chainBVersion) { + s.Require().Equal(testvalues.IBCTransferAmount, actualBalance) + } else { + s.Require().Equal(int64(0), actualBalance) + } + }) +} + +// transferChannelOptions configures both of the chains to have non-incentivized transfer channels. +func transferChannelOptions() func(options *ibc.CreateChannelOptions) { + return func(opts *ibc.CreateChannelOptions) { + opts.Version = transfertypes.Version + opts.SourcePortName = transfertypes.PortID + opts.DestPortName = transfertypes.PortID + } +} diff --git a/e2e/tests/transfer/incentivized_test.go b/e2e/tests/transfer/incentivized_test.go new file mode 100644 index 00000000000..7da6e7b5c5d --- /dev/null +++ b/e2e/tests/transfer/incentivized_test.go @@ -0,0 +1,742 @@ +package transfer + +import ( + "context" + "testing" + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/strangelove-ventures/interchaintest/v7/ibc" + test "github.com/strangelove-ventures/interchaintest/v7/testutil" + "github.com/stretchr/testify/suite" + + "github.com/cosmos/ibc-go/e2e/testvalues" + feetypes "github.com/cosmos/ibc-go/v7/modules/apps/29-fee/types" + transfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" +) + +type IncentivizedTransferTestSuite struct { + TransferTestSuite +} + +func TestIncentivizedTransferTestSuite(t *testing.T) { + suite.Run(t, new(IncentivizedTransferTestSuite)) +} + +func (s *IncentivizedTransferTestSuite) TestMsgPayPacketFee_AsyncSingleSender_Succeeds() { + t := s.T() + ctx := context.TODO() + + relayer, channelA := s.SetupChainsRelayerAndChannel(ctx, feeMiddlewareChannelOptions()) + chainA, chainB := s.GetChains() + + var ( + chainADenom = chainA.Config().Denom + testFee = testvalues.DefaultFee(chainADenom) + chainATx ibc.Tx + payPacketFeeTxResp sdk.TxResponse + ) + + chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + + t.Run("relayer wallets recovered", func(t *testing.T) { + err := s.RecoverRelayerWallets(ctx, relayer) + s.Require().NoError(err) + }) + + chainARelayerWallet, chainBRelayerWallet, err := s.GetRelayerWallets(relayer) + t.Run("relayer wallets fetched", func(t *testing.T) { + s.Require().NoError(err) + }) + + s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB), "failed to wait for blocks") + + _, chainBRelayerUser := s.GetRelayerUsers(ctx) + + t.Run("register counterparty payee", func(t *testing.T) { + resp, err := s.RegisterCounterPartyPayee(ctx, chainB, chainBRelayerUser, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, chainBRelayerWallet.FormattedAddress(), chainARelayerWallet.FormattedAddress()) + s.Require().NoError(err) + s.AssertValidTxResponse(resp) + }) + + t.Run("verify counterparty payee", func(t *testing.T) { + address, err := s.QueryCounterPartyPayee(ctx, chainB, chainBRelayerWallet.FormattedAddress(), channelA.Counterparty.ChannelID) + s.Require().NoError(err) + s.Require().Equal(chainARelayerWallet.FormattedAddress(), address) + }) + + walletAmount := ibc.WalletAmount{ + Address: chainAWallet.FormattedAddress(), // destination address + Denom: chainADenom, + Amount: testvalues.IBCTransferAmount, + } + + t.Run("send IBC transfer", func(t *testing.T) { + chainATx, err = chainA.SendIBCTransfer(ctx, channelA.ChannelID, chainAWallet.KeyName(), walletAmount, ibc.TransferOptions{}) + s.Require().NoError(err) + s.Require().NoError(chainATx.Validate(), "chain-a ibc transfer tx is invalid") + }) + + t.Run("tokens are escrowed", func(t *testing.T) { + actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet) + s.Require().NoError(err) + + expected := testvalues.StartingTokenAmount - walletAmount.Amount + s.Require().Equal(expected, actualBalance) + }) + + t.Run("pay packet fee", func(t *testing.T) { + t.Run("no incentivized packets", func(t *testing.T) { + packets, err := s.QueryIncentivizedPacketsForChannel(ctx, chainA, channelA.PortID, channelA.ChannelID) + s.Require().NoError(err) + s.Require().Empty(packets) + }) + + packetId := channeltypes.NewPacketID(channelA.PortID, channelA.ChannelID, chainATx.Packet.Sequence) + packetFee := feetypes.NewPacketFee(testFee, chainAWallet.FormattedAddress(), nil) + + t.Run("should succeed", func(t *testing.T) { + payPacketFeeTxResp, err = s.PayPacketFeeAsync(ctx, chainA, chainAWallet, packetId, packetFee) + s.Require().NoError(err) + s.AssertValidTxResponse(payPacketFeeTxResp) + }) + + t.Run("there should be incentivized packets", func(t *testing.T) { + packets, err := s.QueryIncentivizedPacketsForChannel(ctx, chainA, channelA.PortID, channelA.ChannelID) + s.Require().NoError(err) + s.Require().Len(packets, 1) + actualFee := packets[0].PacketFees[0].Fee + + s.Require().True(actualFee.RecvFee.IsEqual(testFee.RecvFee)) + s.Require().True(actualFee.AckFee.IsEqual(testFee.AckFee)) + s.Require().True(actualFee.TimeoutFee.IsEqual(testFee.TimeoutFee)) + }) + + t.Run("balance should be lowered by sum of recv ack and timeout", func(t *testing.T) { + // The balance should be lowered by the sum of the recv, ack and timeout fees. + actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet) + s.Require().NoError(err) + + expected := testvalues.StartingTokenAmount - walletAmount.Amount - testFee.Total().AmountOf(chainADenom).Int64() + s.Require().Equal(expected, actualBalance) + }) + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer) + }) + + t.Run("packets are relayed", func(t *testing.T) { + packets, err := s.QueryIncentivizedPacketsForChannel(ctx, chainA, channelA.PortID, channelA.ChannelID) + s.Require().NoError(err) + s.Require().Empty(packets) + }) + + t.Run("timeout fee is refunded", func(t *testing.T) { + actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet) + s.Require().NoError(err) + + // once the relayer has relayed the packets, the timeout fee should be refunded. + expected := testvalues.StartingTokenAmount - walletAmount.Amount - testFee.AckFee.AmountOf(chainADenom).Int64() - testFee.RecvFee.AmountOf(chainADenom).Int64() + s.Require().Equal(expected, actualBalance) + }) +} + +func (s *IncentivizedTransferTestSuite) TestMsgPayPacketFee_InvalidReceiverAccount() { + t := s.T() + ctx := context.TODO() + + relayer, channelA := s.SetupChainsRelayerAndChannel(ctx, feeMiddlewareChannelOptions()) + chainA, chainB := s.GetChains() + + var ( + chainADenom = chainA.Config().Denom + testFee = testvalues.DefaultFee(chainADenom) + payPacketFeeTxResp sdk.TxResponse + ) + + chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + + t.Run("relayer wallets recovered", func(t *testing.T) { + err := s.RecoverRelayerWallets(ctx, relayer) + s.Require().NoError(err) + }) + + chainARelayerWallet, chainBRelayerWallet, err := s.GetRelayerWallets(relayer) + t.Run("relayer wallets fetched", func(t *testing.T) { + s.Require().NoError(err) + }) + + s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB), "failed to wait for blocks") + + _, chainBRelayerUser := s.GetRelayerUsers(ctx) + + t.Run("register counterparty payee", func(t *testing.T) { + resp, err := s.RegisterCounterPartyPayee(ctx, chainB, chainBRelayerUser, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, chainBRelayerWallet.FormattedAddress(), chainARelayerWallet.FormattedAddress()) + s.Require().NoError(err) + s.AssertValidTxResponse(resp) + }) + + t.Run("verify counterparty payee", func(t *testing.T) { + address, err := s.QueryCounterPartyPayee(ctx, chainB, chainBRelayerWallet.FormattedAddress(), channelA.Counterparty.ChannelID) + s.Require().NoError(err) + s.Require().Equal(chainARelayerWallet.FormattedAddress(), address) + }) + + transferAmount := testvalues.DefaultTransferAmount(chainADenom) + + t.Run("send IBC transfer", func(t *testing.T) { + transferMsg := transfertypes.NewMsgTransfer(channelA.PortID, channelA.ChannelID, transferAmount, chainAWallet.FormattedAddress(), testvalues.InvalidAddress, s.GetTimeoutHeight(ctx, chainB), 0, "") + txResp, err := s.BroadcastMessages(ctx, chainA, chainAWallet, transferMsg) + // this message should be successful, as receiver account is not validated on the sending chain. + s.Require().NoError(err) + s.AssertValidTxResponse(txResp) + }) + + t.Run("tokens are escrowed", func(t *testing.T) { + actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet) + s.Require().NoError(err) + + expected := testvalues.StartingTokenAmount - transferAmount.Amount.Int64() + s.Require().Equal(expected, actualBalance) + }) + + t.Run("pay packet fee", func(t *testing.T) { + t.Run("no incentivized packets", func(t *testing.T) { + packets, err := s.QueryIncentivizedPacketsForChannel(ctx, chainA, channelA.PortID, channelA.ChannelID) + s.Require().NoError(err) + s.Require().Empty(packets) + }) + + packetId := channeltypes.NewPacketID(channelA.PortID, channelA.ChannelID, 1) + packetFee := feetypes.NewPacketFee(testFee, chainAWallet.FormattedAddress(), nil) + + t.Run("should succeed", func(t *testing.T) { + payPacketFeeTxResp, err = s.PayPacketFeeAsync(ctx, chainA, chainAWallet, packetId, packetFee) + s.Require().NoError(err) + s.AssertValidTxResponse(payPacketFeeTxResp) + }) + + t.Run("there should be incentivized packets", func(t *testing.T) { + packets, err := s.QueryIncentivizedPacketsForChannel(ctx, chainA, channelA.PortID, channelA.ChannelID) + s.Require().NoError(err) + s.Require().Len(packets, 1) + actualFee := packets[0].PacketFees[0].Fee + + s.Require().True(actualFee.RecvFee.IsEqual(testFee.RecvFee)) + s.Require().True(actualFee.AckFee.IsEqual(testFee.AckFee)) + s.Require().True(actualFee.TimeoutFee.IsEqual(testFee.TimeoutFee)) + }) + + t.Run("balance should be lowered by sum of recv, ack and timeout", func(t *testing.T) { + // The balance should be lowered by the sum of the recv, ack and timeout fees. + actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet) + s.Require().NoError(err) + + expected := testvalues.StartingTokenAmount - transferAmount.Amount.Int64() - testFee.Total().AmountOf(chainADenom).Int64() + s.Require().Equal(expected, actualBalance) + }) + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer) + }) + + t.Run("packets are relayed", func(t *testing.T) { + packets, err := s.QueryIncentivizedPacketsForChannel(ctx, chainA, channelA.PortID, channelA.ChannelID) + s.Require().NoError(err) + s.Require().Empty(packets) + }) + t.Run("timeout fee and transfer amount refunded", func(t *testing.T) { + actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet) + s.Require().NoError(err) + + // once the relayer has relayed the packets, the timeout fee should be refunded. + // the address was invalid so the amount sent should be unescrowed. + expected := testvalues.StartingTokenAmount - testFee.AckFee.AmountOf(chainADenom).Int64() - testFee.RecvFee.AmountOf(chainADenom).Int64() + s.Require().Equal(expected, actualBalance, "the amount sent and timeout fee should have been refunded as there was an invalid receiver address provided") + }) +} + +func (s *IncentivizedTransferTestSuite) TestMultiMsg_MsgPayPacketFeeSingleSender() { + t := s.T() + ctx := context.TODO() + + relayer, channelA := s.SetupChainsRelayerAndChannel(ctx, feeMiddlewareChannelOptions()) + + chainA, chainB := s.GetChains() + + var ( + chainADenom = chainA.Config().Denom + testFee = testvalues.DefaultFee(chainADenom) + multiMsgTxResponse sdk.TxResponse + ) + + transferAmount := testvalues.DefaultTransferAmount(chainA.Config().Denom) + + chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + chainBWallet := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount) + + t.Run("relayer wallets recovered", func(t *testing.T) { + err := s.RecoverRelayerWallets(ctx, relayer) + s.Require().NoError(err) + }) + + chainARelayerWallet, chainBRelayerWallet, err := s.GetRelayerWallets(relayer) + t.Run("relayer wallets fetched", func(t *testing.T) { + s.Require().NoError(err) + }) + + s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB), "failed to wait for blocks") + + chainARelayerUser, chainBRelayerUser := s.GetRelayerUsers(ctx) + + relayerAStartingBalance, err := s.GetChainANativeBalance(ctx, chainARelayerUser) + s.Require().NoError(err) + t.Logf("relayer A user starting with balance: %d", relayerAStartingBalance) + + t.Run("register counterparty payee", func(t *testing.T) { + multiMsgTxResponse, err = s.RegisterCounterPartyPayee(ctx, chainB, chainBRelayerUser, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, chainBRelayerWallet.FormattedAddress(), chainARelayerWallet.FormattedAddress()) + s.Require().NoError(err) + s.AssertValidTxResponse(multiMsgTxResponse) + }) + + t.Run("verify counterparty payee", func(t *testing.T) { + address, err := s.QueryCounterPartyPayee(ctx, chainB, chainBRelayerWallet.FormattedAddress(), channelA.Counterparty.ChannelID) + s.Require().NoError(err) + s.Require().Equal(chainARelayerWallet.FormattedAddress(), address) + }) + + t.Run("no incentivized packets", func(t *testing.T) { + packets, err := s.QueryIncentivizedPacketsForChannel(ctx, chainA, channelA.PortID, channelA.ChannelID) + s.Require().NoError(err) + s.Require().Empty(packets) + }) + + payPacketFeeMsg := feetypes.NewMsgPayPacketFee(testFee, channelA.PortID, channelA.ChannelID, chainAWallet.FormattedAddress(), nil) + transferMsg := transfertypes.NewMsgTransfer(channelA.PortID, channelA.ChannelID, transferAmount, chainAWallet.FormattedAddress(), chainBWallet.FormattedAddress(), s.GetTimeoutHeight(ctx, chainB), 0, "") + resp, err := s.BroadcastMessages(ctx, chainA, chainAWallet, payPacketFeeMsg, transferMsg) + + t.Run("transfer successful", func(t *testing.T) { + s.AssertValidTxResponse(resp) + s.Require().NoError(err) + }) + + t.Run("there should be incentivized packets", func(t *testing.T) { + packets, err := s.QueryIncentivizedPacketsForChannel(ctx, chainA, channelA.PortID, channelA.ChannelID) + s.Require().NoError(err) + s.Require().Len(packets, 1) + actualFee := packets[0].PacketFees[0].Fee + + s.Require().True(actualFee.RecvFee.IsEqual(testFee.RecvFee)) + s.Require().True(actualFee.AckFee.IsEqual(testFee.AckFee)) + s.Require().True(actualFee.TimeoutFee.IsEqual(testFee.TimeoutFee)) + }) + + t.Run("balance should be lowered by sum of recv ack and timeout", func(t *testing.T) { + // The balance should be lowered by the sum of the recv, ack and timeout fees. + actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet) + s.Require().NoError(err) + + expected := testvalues.StartingTokenAmount - testvalues.IBCTransferAmount - testFee.Total().AmountOf(chainADenom).Int64() + s.Require().Equal(expected, actualBalance) + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer) + }) + + t.Run("packets are relayed", func(t *testing.T) { + packets, err := s.QueryIncentivizedPacketsForChannel(ctx, chainA, channelA.PortID, channelA.ChannelID) + s.Require().NoError(err) + s.Require().Empty(packets) + }) + + t.Run("timeout fee is refunded", func(t *testing.T) { + actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet) + s.Require().NoError(err) + + // once the relayer has relayed the packets, the timeout fee should be refunded. + expected := testvalues.StartingTokenAmount - testvalues.IBCTransferAmount - testFee.AckFee.AmountOf(chainADenom).Int64() - testFee.RecvFee.AmountOf(chainADenom).Int64() + s.Require().Equal(expected, actualBalance) + }) + + t.Run("relayerA is paid ack and recv fee", func(t *testing.T) { + actualBalance, err := s.GetChainANativeBalance(ctx, chainARelayerUser) + s.Require().NoError(err) + expected := relayerAStartingBalance + testFee.AckFee.AmountOf(chainADenom).Int64() + testFee.RecvFee.AmountOf(chainADenom).Int64() + s.Require().Equal(expected, actualBalance) + }) +} + +func (s *IncentivizedTransferTestSuite) TestMsgPayPacketFee_SingleSender_TimesOut() { + t := s.T() + ctx := context.TODO() + + relayer, channelA := s.SetupChainsRelayerAndChannel(ctx, feeMiddlewareChannelOptions()) + chainA, chainB := s.GetChains() + + var ( + chainADenom = chainA.Config().Denom + testFee = testvalues.DefaultFee(chainADenom) + chainATx ibc.Tx + payPacketFeeTxResp sdk.TxResponse + ) + + chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + chainBWallet := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount) + + t.Run("relayer wallets recovered", func(t *testing.T) { + s.Require().NoError(s.RecoverRelayerWallets(ctx, relayer)) + }) + + chainARelayerWallet, chainBRelayerWallet, err := s.GetRelayerWallets(relayer) + t.Run("relayer wallets fetched", func(t *testing.T) { + s.Require().NoError(err) + }) + + _, chainBRelayerUser := s.GetRelayerUsers(ctx) + + t.Run("register counterparty payee", func(t *testing.T) { + resp, err := s.RegisterCounterPartyPayee(ctx, chainB, chainBRelayerUser, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, chainBRelayerWallet.FormattedAddress(), chainARelayerWallet.FormattedAddress()) + s.Require().NoError(err) + s.AssertValidTxResponse(resp) + }) + + t.Run("verify counterparty payee", func(t *testing.T) { + address, err := s.QueryCounterPartyPayee(ctx, chainB, chainBRelayerWallet.FormattedAddress(), channelA.Counterparty.ChannelID) + s.Require().NoError(err) + s.Require().Equal(chainARelayerWallet.FormattedAddress(), address) + }) + + chainBWalletAmount := ibc.WalletAmount{ + Address: chainBWallet.FormattedAddress(), // destination address + Denom: chainA.Config().Denom, + Amount: testvalues.IBCTransferAmount, + } + + t.Run("Send IBC transfer", func(t *testing.T) { + chainATx, err = chainA.SendIBCTransfer(ctx, channelA.ChannelID, chainAWallet.KeyName(), chainBWalletAmount, ibc.TransferOptions{Timeout: testvalues.ImmediatelyTimeout()}) + s.Require().NoError(err) + s.Require().NoError(chainATx.Validate(), "source ibc transfer tx is invalid") + time.Sleep(time.Nanosecond * 1) // want it to timeout immediately + }) + + t.Run("tokens are escrowed", func(t *testing.T) { + actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet) + s.Require().NoError(err) + + expected := testvalues.StartingTokenAmount - chainBWalletAmount.Amount + s.Require().Equal(expected, actualBalance) + }) + + t.Run("pay packet fee", func(t *testing.T) { + packetId := channeltypes.NewPacketID(channelA.PortID, channelA.ChannelID, chainATx.Packet.Sequence) + packetFee := feetypes.NewPacketFee(testFee, chainAWallet.FormattedAddress(), nil) + + t.Run("no incentivized packets", func(t *testing.T) { + packets, err := s.QueryIncentivizedPacketsForChannel(ctx, chainA, channelA.PortID, channelA.ChannelID) + s.Require().NoError(err) + s.Require().Empty(packets) + }) + + t.Run("should succeed", func(t *testing.T) { + payPacketFeeTxResp, err = s.PayPacketFeeAsync(ctx, chainA, chainAWallet, packetId, packetFee) + s.Require().NoError(err) + s.AssertValidTxResponse(payPacketFeeTxResp) + }) + + t.Run("there should be incentivized packets", func(t *testing.T) { + packets, err := s.QueryIncentivizedPacketsForChannel(ctx, chainA, channelA.PortID, channelA.ChannelID) + s.Require().NoError(err) + s.Require().Len(packets, 1) + actualFee := packets[0].PacketFees[0].Fee + + s.Require().True(actualFee.RecvFee.IsEqual(testFee.RecvFee)) + s.Require().True(actualFee.AckFee.IsEqual(testFee.AckFee)) + s.Require().True(actualFee.TimeoutFee.IsEqual(testFee.TimeoutFee)) + }) + + t.Run("balance should be lowered by sum of recv ack and timeout", func(t *testing.T) { + // The balance should be lowered by the sum of the recv, ack and timeout fees. + actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet) + s.Require().NoError(err) + + expected := testvalues.StartingTokenAmount - chainBWalletAmount.Amount - testFee.Total().AmountOf(chainADenom).Int64() + s.Require().Equal(expected, actualBalance) + }) + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer) + }) + + t.Run("packets are relayed", func(t *testing.T) { + packets, err := s.QueryIncentivizedPacketsForChannel(ctx, chainA, channelA.PortID, channelA.ChannelID) + s.Require().NoError(err) + s.Require().Empty(packets) + }) + + t.Run("recv and ack should be refunded", func(t *testing.T) { + actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet) + s.Require().NoError(err) + + expected := testvalues.StartingTokenAmount - testFee.TimeoutFee.AmountOf(chainADenom).Int64() + s.Require().Equal(expected, actualBalance) + }) +} + +func (s *IncentivizedTransferTestSuite) TestPayPacketFeeAsync_SingleSender_NoCounterPartyAddress() { + t := s.T() + ctx := context.TODO() + + relayer, channelA := s.SetupChainsRelayerAndChannel(ctx, feeMiddlewareChannelOptions()) + chainA, _ := s.GetChains() + + var ( + chainADenom = chainA.Config().Denom + testFee = testvalues.DefaultFee(chainADenom) + chainATx ibc.Tx + payPacketFeeTxResp sdk.TxResponse + ) + + chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + + t.Run("relayer wallets recovered", func(t *testing.T) { + err := s.RecoverRelayerWallets(ctx, relayer) + s.Require().NoError(err) + }) + + chainBWalletAmount := ibc.WalletAmount{ + Address: chainAWallet.FormattedAddress(), // destination address + Denom: chainADenom, + Amount: testvalues.IBCTransferAmount, + } + + t.Run("send IBC transfer", func(t *testing.T) { + var err error + chainATx, err = chainA.SendIBCTransfer(ctx, channelA.ChannelID, chainAWallet.KeyName(), chainBWalletAmount, ibc.TransferOptions{}) + s.Require().NoError(err) + s.Require().NoError(chainATx.Validate(), "source ibc transfer tx is invalid") + }) + + t.Run("tokens are escrowed", func(t *testing.T) { + actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet) + s.Require().NoError(err) + + expected := testvalues.StartingTokenAmount - chainBWalletAmount.Amount + s.Require().Equal(expected, actualBalance) + }) + + t.Run("pay packet fee", func(t *testing.T) { + t.Run("no incentivized packets", func(t *testing.T) { + packets, err := s.QueryIncentivizedPacketsForChannel(ctx, chainA, channelA.PortID, channelA.ChannelID) + s.Require().NoError(err) + s.Require().Empty(packets) + }) + + packetId := channeltypes.NewPacketID(channelA.PortID, channelA.ChannelID, chainATx.Packet.Sequence) + packetFee := feetypes.NewPacketFee(testFee, chainAWallet.FormattedAddress(), nil) + + t.Run("should succeed", func(t *testing.T) { + var err error + payPacketFeeTxResp, err = s.PayPacketFeeAsync(ctx, chainA, chainAWallet, packetId, packetFee) + s.Require().NoError(err) + s.AssertValidTxResponse(payPacketFeeTxResp) + }) + + t.Run("should be incentivized packets", func(t *testing.T) { + packets, err := s.QueryIncentivizedPacketsForChannel(ctx, chainA, channelA.PortID, channelA.ChannelID) + s.Require().NoError(err) + s.Require().Len(packets, 1) + actualFee := packets[0].PacketFees[0].Fee + + s.Require().True(actualFee.RecvFee.IsEqual(testFee.RecvFee)) + s.Require().True(actualFee.AckFee.IsEqual(testFee.AckFee)) + s.Require().True(actualFee.TimeoutFee.IsEqual(testFee.TimeoutFee)) + }) + }) + + t.Run("balance should be lowered by sum of recv, ack and timeout", func(t *testing.T) { + // The balance should be lowered by the sum of the recv, ack and timeout fees. + actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet) + s.Require().NoError(err) + + expected := testvalues.StartingTokenAmount - chainBWalletAmount.Amount - testFee.Total().AmountOf(chainADenom).Int64() + s.Require().Equal(expected, actualBalance) + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer) + }) + + t.Run("with no counterparty address", func(t *testing.T) { + t.Run("packets are relayed", func(t *testing.T) { + packets, err := s.QueryIncentivizedPacketsForChannel(ctx, chainA, channelA.PortID, channelA.ChannelID) + s.Require().NoError(err) + s.Require().Empty(packets) + }) + + t.Run("timeout and recv fee are refunded", func(t *testing.T) { + actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet) + s.Require().NoError(err) + + // once the relayer has relayed the packets, the timeout and recv fee should be refunded. + expected := testvalues.StartingTokenAmount - chainBWalletAmount.Amount - testFee.AckFee.AmountOf(chainADenom).Int64() + s.Require().Equal(expected, actualBalance) + }) + }) +} + +func (s *IncentivizedTransferTestSuite) TestMsgPayPacketFee_AsyncMultipleSenders_Succeeds() { + t := s.T() + ctx := context.TODO() + + relayer, channelA := s.SetupChainsRelayerAndChannel(ctx, feeMiddlewareChannelOptions()) + chainA, chainB := s.GetChains() + + var ( + chainADenom = chainA.Config().Denom + testFee = testvalues.DefaultFee(chainADenom) + chainATx ibc.Tx + payPacketFeeTxResp sdk.TxResponse + ) + + chainAWallet1 := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + chainAWallet2 := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + + t.Run("relayer wallets recovered", func(t *testing.T) { + err := s.RecoverRelayerWallets(ctx, relayer) + s.Require().NoError(err) + }) + + chainARelayerWallet, chainBRelayerWallet, err := s.GetRelayerWallets(relayer) + s.Require().NoError(err) + s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB), "failed to wait for blocks") + + _, chainBRelayerUser := s.GetRelayerUsers(ctx) + + t.Run("register counterparty payee", func(t *testing.T) { + resp, err := s.RegisterCounterPartyPayee(ctx, chainB, chainBRelayerUser, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, chainBRelayerWallet.FormattedAddress(), chainARelayerWallet.FormattedAddress()) + s.Require().NoError(err) + s.AssertValidTxResponse(resp) + }) + + t.Run("verify counterparty payee", func(t *testing.T) { + address, err := s.QueryCounterPartyPayee(ctx, chainB, chainBRelayerWallet.FormattedAddress(), channelA.Counterparty.ChannelID) + s.Require().NoError(err) + s.Require().Equal(chainARelayerWallet.FormattedAddress(), address) + }) + + walletAmount1 := ibc.WalletAmount{ + Address: chainAWallet1.FormattedAddress(), // destination address + Denom: chainADenom, + Amount: testvalues.IBCTransferAmount, + } + + t.Run("send IBC transfer", func(t *testing.T) { + chainATx, err = chainA.SendIBCTransfer(ctx, channelA.ChannelID, chainAWallet1.KeyName(), walletAmount1, ibc.TransferOptions{}) + s.Require().NoError(err) + s.Require().NoError(chainATx.Validate(), "chain-a ibc transfer tx is invalid") + }) + + t.Run("tokens are escrowed", func(t *testing.T) { + actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet1) + s.Require().NoError(err) + + expected := testvalues.StartingTokenAmount - walletAmount1.Amount + s.Require().Equal(expected, actualBalance) + }) + + t.Run("pay packet fee", func(t *testing.T) { + t.Run("no incentivized packets", func(t *testing.T) { + packets, err := s.QueryIncentivizedPacketsForChannel(ctx, chainA, channelA.PortID, channelA.ChannelID) + s.Require().NoError(err) + s.Require().Empty(packets) + }) + + packetId := channeltypes.NewPacketID(channelA.PortID, channelA.ChannelID, chainATx.Packet.Sequence) + packetFee1 := feetypes.NewPacketFee(testFee, chainAWallet1.FormattedAddress(), nil) + packetFee2 := feetypes.NewPacketFee(testFee, chainAWallet2.FormattedAddress(), nil) + + t.Run("paying packetFee1 should succeed", func(t *testing.T) { + payPacketFeeTxResp, err = s.PayPacketFeeAsync(ctx, chainA, chainAWallet1, packetId, packetFee1) + s.Require().NoError(err) + s.AssertValidTxResponse(payPacketFeeTxResp) + }) + t.Run("paying packetFee2 should succeed", func(t *testing.T) { + payPacketFeeTxResp, err = s.PayPacketFeeAsync(ctx, chainA, chainAWallet2, packetId, packetFee2) + s.Require().NoError(err) + s.AssertValidTxResponse(payPacketFeeTxResp) + }) + + t.Run("there should be incentivized packets", func(t *testing.T) { + packets, err := s.QueryIncentivizedPacketsForChannel(ctx, chainA, channelA.PortID, channelA.ChannelID) + s.Require().NoError(err) + s.Require().Len(packets, 1) + actualFee1 := packets[0].PacketFees[0].Fee + actualFee2 := packets[0].PacketFees[1].Fee + s.Require().Len(packets[0].PacketFees, 2) + + s.Require().True(actualFee1.RecvFee.IsEqual(testFee.RecvFee)) + s.Require().True(actualFee1.AckFee.IsEqual(testFee.AckFee)) + s.Require().True(actualFee1.TimeoutFee.IsEqual(testFee.TimeoutFee)) + + s.Require().True(actualFee2.RecvFee.IsEqual(testFee.RecvFee)) + s.Require().True(actualFee2.AckFee.IsEqual(testFee.AckFee)) + s.Require().True(actualFee2.TimeoutFee.IsEqual(testFee.TimeoutFee)) + }) + + t.Run("balance of chainAWallet1 should be lowered by sum of recv ack and timeout", func(t *testing.T) { + actualBalance1, err := s.GetChainANativeBalance(ctx, chainAWallet1) + s.Require().NoError(err) + + expected1 := testvalues.StartingTokenAmount - walletAmount1.Amount - testFee.Total().AmountOf(chainADenom).Int64() + s.Require().Equal(expected1, actualBalance1) + }) + + t.Run("balance of chainAWallet2 should be lowered by sum of recv ack and timeout", func(t *testing.T) { + actualBalance2, err := s.GetChainANativeBalance(ctx, chainAWallet2) + s.Require().NoError(err) + + expected2 := testvalues.StartingTokenAmount - testFee.Total().AmountOf(chainADenom).Int64() + s.Require().Equal(expected2, actualBalance2) + }) + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer) + }) + + t.Run("packets are relayed", func(t *testing.T) { + packets, err := s.QueryIncentivizedPacketsForChannel(ctx, chainA, channelA.PortID, channelA.ChannelID) + s.Require().NoError(err) + s.Require().Empty(packets) + }) + + t.Run("timeout fee is refunded", func(t *testing.T) { + actualBalance1, err := s.GetChainANativeBalance(ctx, chainAWallet1) + s.Require().NoError(err) + + // once the relayer has relayed the packets, the timeout fee should be refunded. + expected1 := testvalues.StartingTokenAmount - walletAmount1.Amount - testFee.AckFee.AmountOf(chainADenom).Int64() - testFee.RecvFee.AmountOf(chainADenom).Int64() + s.Require().Equal(expected1, actualBalance1) + + actualBalance2, err := s.GetChainANativeBalance(ctx, chainAWallet2) + s.Require().NoError(err) + + // once the relayer has relayed the packets, the timeout fee should be refunded. + expected2 := testvalues.StartingTokenAmount - testFee.AckFee.AmountOf(chainADenom).Int64() - testFee.RecvFee.AmountOf(chainADenom).Int64() + s.Require().Equal(expected2, actualBalance2) + }) +} + +// feeMiddlewareChannelOptions configures both of the chains to have fee middleware enabled. +func feeMiddlewareChannelOptions() func(options *ibc.CreateChannelOptions) { + return func(opts *ibc.CreateChannelOptions) { + opts.Version = "{\"fee_version\":\"ics29-1\",\"app_version\":\"ics20-1\"}" + opts.DestPortName = "transfer" + opts.SourcePortName = "transfer" + } +} diff --git a/e2e/tests/upgrades/upgrade_test.go b/e2e/tests/upgrades/upgrade_test.go new file mode 100644 index 00000000000..c8500e830dc --- /dev/null +++ b/e2e/tests/upgrades/upgrade_test.go @@ -0,0 +1,638 @@ +package upgrades + +import ( + "context" + "fmt" + "testing" + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" + "github.com/cosmos/gogoproto/proto" + intertxtypes "github.com/cosmos/interchain-accounts/x/inter-tx/types" + interchaintest "github.com/strangelove-ventures/interchaintest/v7" + "github.com/strangelove-ventures/interchaintest/v7/chain/cosmos" + "github.com/strangelove-ventures/interchaintest/v7/ibc" + test "github.com/strangelove-ventures/interchaintest/v7/testutil" + "github.com/stretchr/testify/suite" + "golang.org/x/mod/semver" + + "github.com/cosmos/ibc-go/e2e/testconfig" + "github.com/cosmos/ibc-go/e2e/testsuite" + "github.com/cosmos/ibc-go/e2e/testvalues" + controllertypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/controller/types" + icatypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types" + v7migrations "github.com/cosmos/ibc-go/v7/modules/core/02-client/migrations/v7" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v7/modules/core/exported" + solomachine "github.com/cosmos/ibc-go/v7/modules/light-clients/06-solomachine" + ibctesting "github.com/cosmos/ibc-go/v7/testing" +) + +const ( + haltHeight = uint64(100) + blocksAfterUpgrade = uint64(10) +) + +func TestUpgradeTestSuite(t *testing.T) { + testCfg := testconfig.FromEnv() + if testCfg.UpgradeTag == "" || testCfg.UpgradePlanName == "" { + t.Fatal("upgrade tag and upgrade plan name must be provided in test configuration") + } + + suite.Run(t, new(UpgradeTestSuite)) +} + +type UpgradeTestSuite struct { + testsuite.E2ETestSuite +} + +// UpgradeChain upgrades a chain to a specific version using the planName provided. +// The software upgrade proposal is broadcast by the provided wallet. +func (s *UpgradeTestSuite) UpgradeChain(ctx context.Context, chain *cosmos.CosmosChain, wallet ibc.Wallet, planName, currentVersion, upgradeVersion string) { + plan := upgradetypes.Plan{ + Name: planName, + Height: int64(haltHeight), + Info: fmt.Sprintf("upgrade version test from %s to %s", currentVersion, upgradeVersion), + } + + upgradeProposal := upgradetypes.NewSoftwareUpgradeProposal(fmt.Sprintf("upgrade from %s to %s", currentVersion, upgradeVersion), "upgrade chain E2E test", plan) + s.ExecuteGovProposal(ctx, chain, wallet, upgradeProposal) + + height, err := chain.Height(ctx) + s.Require().NoError(err, "error fetching height before upgrade") + + timeoutCtx, timeoutCtxCancel := context.WithTimeout(ctx, time.Minute*2) + defer timeoutCtxCancel() + + err = test.WaitForBlocks(timeoutCtx, int(haltHeight-height)+1, chain) + s.Require().Error(err, "chain did not halt at halt height") + + err = chain.StopAllNodes(ctx) + s.Require().NoError(err, "error stopping node(s)") + + chain.UpgradeVersion(ctx, s.DockerClient, upgradeVersion) + + err = chain.StartAllNodes(ctx) + s.Require().NoError(err, "error starting upgraded node(s)") + + // we are reinitializing the clients because we need to update the hostGRPCAddress after + // the upgrade and subsequent restarting of nodes + s.InitGRPCClients(chain) + + timeoutCtx, timeoutCtxCancel = context.WithTimeout(ctx, time.Minute*2) + defer timeoutCtxCancel() + + err = test.WaitForBlocks(timeoutCtx, int(blocksAfterUpgrade), chain) + s.Require().NoError(err, "chain did not produce blocks after upgrade") + + height, err = chain.Height(ctx) + s.Require().NoError(err, "error fetching height after upgrade") + + s.Require().Greater(height, haltHeight, "height did not increment after upgrade") +} + +func (s *UpgradeTestSuite) TestIBCChainUpgrade() { + t := s.T() + testCfg := testconfig.FromEnv() + + ctx := context.Background() + relayer, channelA := s.SetupChainsRelayerAndChannel(ctx) + chainA, chainB := s.GetChains() + + var ( + chainADenom = chainA.Config().Denom + chainBIBCToken = testsuite.GetIBCToken(chainADenom, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID) // IBC token sent to chainB + + chainBDenom = chainB.Config().Denom + chainAIBCToken = testsuite.GetIBCToken(chainBDenom, channelA.PortID, channelA.ChannelID) // IBC token sent to chainA + ) + + // create separate user specifically for the upgrade proposal to more easily verify starting + // and end balances of the chainA users. + chainAUpgradeProposalWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + + chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + chainAAddress := chainAWallet.FormattedAddress() + + chainBWallet := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount) + chainBAddress := chainBWallet.FormattedAddress() + + s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB), "failed to wait for blocks") + + t.Run("native IBC token transfer from chainA to chainB, sender is source of tokens", func(t *testing.T) { + transferTxResp, err := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, testvalues.DefaultTransferAmount(chainADenom), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "") + s.Require().NoError(err) + s.AssertValidTxResponse(transferTxResp) + }) + + t.Run("tokens are escrowed", func(t *testing.T) { + actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet) + s.Require().NoError(err) + + expected := testvalues.StartingTokenAmount - testvalues.IBCTransferAmount + s.Require().Equal(expected, actualBalance) + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer) + }) + + t.Run("packets are relayed", func(t *testing.T) { + s.AssertPacketRelayed(ctx, chainA, channelA.PortID, channelA.ChannelID, 1) + + actualBalance, err := chainB.GetBalance(ctx, chainBAddress, chainBIBCToken.IBCDenom()) + s.Require().NoError(err) + + expected := testvalues.IBCTransferAmount + s.Require().Equal(expected, actualBalance) + }) + + s.Require().NoError(test.WaitForBlocks(ctx, 5, chainA, chainB), "failed to wait for blocks") + + t.Run("upgrade chainA", func(t *testing.T) { + s.UpgradeChain(ctx, chainA, chainAUpgradeProposalWallet, testCfg.UpgradePlanName, testCfg.ChainAConfig.Tag, testCfg.UpgradeTag) + }) + + t.Run("restart relayer", func(t *testing.T) { + s.StopRelayer(ctx, relayer) + s.StartRelayer(relayer) + }) + + t.Run("native IBC token transfer from chainA to chainB, sender is source of tokens", func(t *testing.T) { + transferTxResp, err := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, testvalues.DefaultTransferAmount(chainADenom), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "") + s.Require().NoError(err) + s.AssertValidTxResponse(transferTxResp) + }) + + s.Require().NoError(test.WaitForBlocks(ctx, 5, chainA, chainB), "failed to wait for blocks") + + t.Run("packets are relayed", func(t *testing.T) { + s.AssertPacketRelayed(ctx, chainA, channelA.PortID, channelA.ChannelID, 2) + + actualBalance, err := chainB.GetBalance(ctx, chainBAddress, chainBIBCToken.IBCDenom()) + s.Require().NoError(err) + + expected := testvalues.IBCTransferAmount * 2 + s.Require().Equal(expected, actualBalance) + }) + + t.Run("ensure packets can be received, send from chainB to chainA", func(t *testing.T) { + t.Run("send from chainB to chainA", func(t *testing.T) { + transferTxResp, err := s.Transfer(ctx, chainB, chainBWallet, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, testvalues.DefaultTransferAmount(chainBDenom), chainBAddress, chainAAddress, s.GetTimeoutHeight(ctx, chainA), 0, "") + s.Require().NoError(err) + s.AssertValidTxResponse(transferTxResp) + }) + + s.Require().NoError(test.WaitForBlocks(ctx, 5, chainA, chainB), "failed to wait for blocks") + + t.Run("packets are relayed", func(t *testing.T) { + s.AssertPacketRelayed(ctx, chainA, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, 1) + + actualBalance, err := chainA.GetBalance(ctx, chainAAddress, chainAIBCToken.IBCDenom()) + s.Require().NoError(err) + + expected := testvalues.IBCTransferAmount + s.Require().Equal(expected, actualBalance) + }) + }) +} + +func (s *UpgradeTestSuite) TestChainUpgrade() { + t := s.T() + + ctx := context.Background() + chain := s.SetupSingleChain(ctx) + + userWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + userWalletAddr := userWallet.FormattedAddress() + + s.Require().NoError(test.WaitForBlocks(ctx, 1, chain), "failed to wait for blocks") + + t.Run("send funds to test wallet", func(t *testing.T) { + err := chain.SendFunds(ctx, interchaintest.FaucetAccountKeyName, ibc.WalletAmount{ + Address: userWalletAddr, + Amount: testvalues.StartingTokenAmount, + Denom: chain.Config().Denom, + }) + s.Require().NoError(err) + }) + + t.Run("verify tokens sent", func(t *testing.T) { + balance, err := chain.GetBalance(ctx, userWalletAddr, chain.Config().Denom) + s.Require().NoError(err) + + expected := testvalues.StartingTokenAmount * 2 + s.Require().Equal(expected, balance) + }) + + t.Run("upgrade chain", func(t *testing.T) { + testCfg := testconfig.FromEnv() + proposerWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + + s.UpgradeChain(ctx, chain, proposerWallet, testCfg.UpgradePlanName, testCfg.ChainAConfig.Tag, testCfg.UpgradeTag) + }) + + t.Run("send funds to test wallet", func(t *testing.T) { + err := chain.SendFunds(ctx, interchaintest.FaucetAccountKeyName, ibc.WalletAmount{ + Address: userWalletAddr, + Amount: testvalues.StartingTokenAmount, + Denom: chain.Config().Denom, + }) + s.Require().NoError(err) + }) + + t.Run("verify tokens sent", func(t *testing.T) { + balance, err := chain.GetBalance(ctx, userWalletAddr, chain.Config().Denom) + s.Require().NoError(err) + + expected := testvalues.StartingTokenAmount * 3 + s.Require().Equal(expected, balance) + }) +} + +func (s *UpgradeTestSuite) TestV5ToV6ChainUpgrade() { + t := s.T() + testCfg := testconfig.FromEnv() + + ctx := context.Background() + relayer, _ := s.SetupChainsRelayerAndChannel(ctx) + chainA, chainB := s.GetChains() + + // create separate user specifically for the upgrade proposal to more easily verify starting + // and end balances of the chainA users. + chainAUpgradeProposalWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + + s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB), "failed to wait for blocks") + + // setup 2 accounts: controller account on chain A, a second chain B account. + // host account will be created when the ICA is registered + controllerAccount := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + chainBAccount := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount) + var hostAccount string + + t.Run("register interchain account", func(t *testing.T) { + version := getICAVersion(testconfig.GetChainATag(), testconfig.GetChainBTag()) + msgRegisterAccount := intertxtypes.NewMsgRegisterAccount(controllerAccount.FormattedAddress(), ibctesting.FirstConnectionID, version) + err := s.RegisterInterchainAccount(ctx, chainA, controllerAccount, msgRegisterAccount) + s.Require().NoError(err) + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer) + }) + + t.Run("verify interchain account", func(t *testing.T) { + var err error + hostAccount, err = s.QueryInterchainAccount(ctx, chainA, controllerAccount.FormattedAddress(), ibctesting.FirstConnectionID) + s.Require().NoError(err) + s.Require().NotZero(len(hostAccount)) + + channels, err := relayer.GetChannels(ctx, s.GetRelayerExecReporter(), chainA.Config().ChainID) + s.Require().NoError(err) + s.Require().Equal(len(channels), 2) + }) + + t.Run("interchain account executes a bank transfer on behalf of the corresponding owner account", func(t *testing.T) { + t.Run("fund interchain account wallet", func(t *testing.T) { + // fund the host account, so it has some $$ to send + err := chainB.SendFunds(ctx, interchaintest.FaucetAccountKeyName, ibc.WalletAmount{ + Address: hostAccount, + Amount: testvalues.StartingTokenAmount, + Denom: chainB.Config().Denom, + }) + s.Require().NoError(err) + }) + + t.Run("broadcast MsgSubmitTx (legacy)", func(t *testing.T) { + // assemble bank transfer message from host account to user account on host chain + msgSend := &banktypes.MsgSend{ + FromAddress: hostAccount, + ToAddress: chainBAccount.FormattedAddress(), + Amount: sdk.NewCoins(testvalues.DefaultTransferAmount(chainB.Config().Denom)), + } + + // assemble submitMessage tx for intertx + msgSubmitTx, err := intertxtypes.NewMsgSubmitTx( + msgSend, + ibctesting.FirstConnectionID, + controllerAccount.FormattedAddress(), + ) + s.Require().NoError(err) + + // broadcast submitMessage tx from controller account on chain A + // this message should trigger the sending of an ICA packet over channel-1 (channel created between controller and host) + // this ICA packet contains the assembled bank transfer message from above, which will be executed by the host account on the host chain. + resp, err := s.BroadcastMessages( + ctx, + chainA, + controllerAccount, + msgSubmitTx, + ) + + s.AssertValidTxResponse(resp) + s.Require().NoError(err) + + s.Require().NoError(test.WaitForBlocks(ctx, 10, chainA, chainB)) + }) + + t.Run("verify tokens transferred", func(t *testing.T) { + balance, err := chainB.GetBalance(ctx, chainBAccount.FormattedAddress(), chainB.Config().Denom) + s.Require().NoError(err) + + _, err = chainB.GetBalance(ctx, hostAccount, chainB.Config().Denom) + s.Require().NoError(err) + + expected := testvalues.IBCTransferAmount + testvalues.StartingTokenAmount + s.Require().Equal(expected, balance) + }) + }) + + s.Require().NoError(test.WaitForBlocks(ctx, 5, chainA, chainB), "failed to wait for blocks") + + t.Run("upgrade chainA", func(t *testing.T) { + s.UpgradeChain(ctx, chainA, chainAUpgradeProposalWallet, testCfg.UpgradePlanName, testCfg.ChainAConfig.Tag, testCfg.UpgradeTag) + }) + + t.Run("restart relayer", func(t *testing.T) { + s.StopRelayer(ctx, relayer) + s.StartRelayer(relayer) + }) + + t.Run("broadcast MsgSubmitTx (legacy)", func(t *testing.T) { + // assemble bank transfer message from host account to user account on host chain + msgSend := &banktypes.MsgSend{ + FromAddress: hostAccount, + ToAddress: chainBAccount.FormattedAddress(), + Amount: sdk.NewCoins(testvalues.DefaultTransferAmount(chainB.Config().Denom)), + } + + // assemble submitMessage tx for intertx + msgSubmitTx, err := intertxtypes.NewMsgSubmitTx( + msgSend, + ibctesting.FirstConnectionID, + controllerAccount.FormattedAddress(), + ) + s.Require().NoError(err) + + // broadcast submitMessage tx from controller account on chain A + // this message should trigger the sending of an ICA packet over channel-1 (channel created between controller and host) + // this ICA packet contains the assembled bank transfer message from above, which will be executed by the host account on the host chain. + resp, err := s.BroadcastMessages( + ctx, + chainA, + controllerAccount, + msgSubmitTx, + ) + + s.AssertValidTxResponse(resp) + s.Require().NoError(err) + + s.Require().NoError(test.WaitForBlocks(ctx, 10, chainA, chainB)) + }) + + t.Run("verify tokens transferred", func(t *testing.T) { + balance, err := chainB.GetBalance(ctx, chainBAccount.FormattedAddress(), chainB.Config().Denom) + s.Require().NoError(err) + + _, err = chainB.GetBalance(ctx, hostAccount, chainB.Config().Denom) + s.Require().NoError(err) + + expected := (testvalues.IBCTransferAmount * 2) + testvalues.StartingTokenAmount + s.Require().Equal(expected, balance) + }) + + t.Run("broadcast MsgSendTx (MsgServer)", func(t *testing.T) { + // assemble bank transfer message from host account to user account on host chain + msgSend := &banktypes.MsgSend{ + FromAddress: hostAccount, + ToAddress: chainBAccount.FormattedAddress(), + Amount: sdk.NewCoins(testvalues.DefaultTransferAmount(chainB.Config().Denom)), + } + + data, err := icatypes.SerializeCosmosTx(testsuite.Codec(), []proto.Message{msgSend}) + s.Require().NoError(err) + + icaPacketData := icatypes.InterchainAccountPacketData{ + Type: icatypes.EXECUTE_TX, + Data: data, + } + + relativeTimeoutTimestamp := uint64(time.Hour.Nanoseconds()) + msgSendTx := controllertypes.NewMsgSendTx(controllerAccount.FormattedAddress(), ibctesting.FirstConnectionID, relativeTimeoutTimestamp, icaPacketData) + + // broadcast MsgSendTx tx from controller account on chain A + // this message should trigger the sending of an ICA packet over channel-1 (channel created between controller and host) + // this ICA packet contains the assembled bank transfer message from above, which will be executed by the host account on the host chain. + resp, err := s.BroadcastMessages( + ctx, + chainA, + controllerAccount, + msgSendTx, + ) + + s.AssertValidTxResponse(resp) + s.Require().NoError(err) + + s.Require().NoError(test.WaitForBlocks(ctx, 10, chainA, chainB)) + }) + + t.Run("verify tokens transferred", func(t *testing.T) { + balance, err := chainB.GetBalance(ctx, chainBAccount.FormattedAddress(), chainB.Config().Denom) + s.Require().NoError(err) + + _, err = chainB.GetBalance(ctx, hostAccount, chainB.Config().Denom) + s.Require().NoError(err) + + expected := (testvalues.IBCTransferAmount * 3) + testvalues.StartingTokenAmount + s.Require().Equal(expected, balance) + }) +} + +// TestV6ToV7ChainUpgrade will test that an upgrade from a v6 ibc-go binary to a v7 ibc-go binary is successful +// and that the automatic migrations associated with the 02-client module are performed. Namely that the solo machine +// proto definition is migrated in state from the v2 to v3 definition. This is checked by creating a solo machine client +// before the upgrade and asserting that its TypeURL has been changed after the upgrade. The test also ensure packets +// can be sent before and after the upgrade without issue +func (s *UpgradeTestSuite) TestV6ToV7ChainUpgrade() { + t := s.T() + testCfg := testconfig.FromEnv() + + ctx := context.Background() + relayer, channelA := s.SetupChainsRelayerAndChannel(ctx) + chainA, chainB := s.GetChains() + + var ( + chainADenom = chainA.Config().Denom + chainBIBCToken = testsuite.GetIBCToken(chainADenom, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID) // IBC token sent to chainB + ) + + chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + chainAAddress := chainAWallet.FormattedAddress() + + chainBWallet := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount) + chainBAddress := chainBWallet.FormattedAddress() + + // create second tendermint client + createClientOptions := ibc.CreateClientOptions{ + TrustingPeriod: ibctesting.TrustingPeriod.String(), + } + + s.SetupClients(ctx, relayer, createClientOptions) + + s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB), "failed to wait for blocks") + + t.Run("check that both tendermint clients are active", func(t *testing.T) { + status, err := s.QueryClientStatus(ctx, chainA, testvalues.TendermintClientID(0)) + s.Require().NoError(err) + s.Require().Equal(exported.Active.String(), status) + + status, err = s.QueryClientStatus(ctx, chainA, testvalues.TendermintClientID(1)) + s.Require().NoError(err) + s.Require().Equal(exported.Active.String(), status) + }) + + // create solo machine client using the solomachine implementation from ibctesting + // TODO: the solomachine clientID should be updated when after fix of this issue: https://github.com/cosmos/ibc-go/issues/2907 + solo := ibctesting.NewSolomachine(t, testsuite.Codec(), "solomachine", "testing", 1) + + legacyConsensusState := &v7migrations.ConsensusState{ + PublicKey: solo.ConsensusState().PublicKey, + Diversifier: solo.ConsensusState().Diversifier, + Timestamp: solo.ConsensusState().Timestamp, + } + + legacyClientState := &v7migrations.ClientState{ + Sequence: solo.ClientState().Sequence, + IsFrozen: solo.ClientState().IsFrozen, + ConsensusState: legacyConsensusState, + AllowUpdateAfterProposal: true, + } + + msgCreateSoloMachineClient, err := clienttypes.NewMsgCreateClient(legacyClientState, legacyConsensusState, chainAAddress) + s.Require().NoError(err) + + resp, err := s.BroadcastMessages( + ctx, + chainA, + chainAWallet, + msgCreateSoloMachineClient, + ) + + s.AssertValidTxResponse(resp) + s.Require().NoError(err) + + t.Run("check that the solomachine is now active and that the clientstate is a pre-upgrade v2 solomachine clientstate", func(t *testing.T) { + status, err := s.QueryClientStatus(ctx, chainA, testvalues.SolomachineClientID(2)) + s.Require().NoError(err) + s.Require().Equal(exported.Active.String(), status) + + res, err := s.ClientState(ctx, chainA, testvalues.SolomachineClientID(2)) + s.Require().NoError(err) + s.Require().Equal(fmt.Sprint("/", proto.MessageName(&v7migrations.ClientState{})), res.ClientState.TypeUrl) + }) + + s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB), "failed to wait for blocks") + + t.Run("IBC token transfer from chainA to chainB, sender is source of tokens", func(t *testing.T) { + transferTxResp, err := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, testvalues.DefaultTransferAmount(chainADenom), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "") + s.Require().NoError(err) + s.AssertValidTxResponse(transferTxResp) + }) + + t.Run("tokens are escrowed", func(t *testing.T) { + actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet) + s.Require().NoError(err) + + expected := testvalues.StartingTokenAmount - testvalues.IBCTransferAmount + s.Require().Equal(expected, actualBalance) + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer) + }) + + t.Run("packets are relayed", func(t *testing.T) { + s.AssertPacketRelayed(ctx, chainA, channelA.PortID, channelA.ChannelID, 1) + + actualBalance, err := chainB.GetBalance(ctx, chainBAddress, chainBIBCToken.IBCDenom()) + s.Require().NoError(err) + + expected := testvalues.IBCTransferAmount + s.Require().Equal(expected, actualBalance) + }) + + // create separate user specifically for the upgrade proposal to more easily verify starting + // and end balances of the chainA users. + chainAUpgradeProposalWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + + t.Run("upgrade chainA", func(t *testing.T) { + s.UpgradeChain(ctx, chainA, chainAUpgradeProposalWallet, testCfg.UpgradePlanName, testCfg.ChainAConfig.Tag, testCfg.UpgradeTag) + }) + + t.Run("check that the tendermint clients are active again after upgrade", func(t *testing.T) { + status, err := s.QueryClientStatus(ctx, chainA, testvalues.TendermintClientID(0)) + s.Require().NoError(err) + s.Require().Equal(exported.Active.String(), status) + + status, err = s.QueryClientStatus(ctx, chainA, testvalues.TendermintClientID(1)) + s.Require().NoError(err) + s.Require().Equal(exported.Active.String(), status) + }) + + t.Run("IBC token transfer from chainA to chainB, to make sure the upgrade did not break the packet flow", func(t *testing.T) { + transferTxResp, err := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, testvalues.DefaultTransferAmount(chainADenom), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "") + s.Require().NoError(err) + s.AssertValidTxResponse(transferTxResp) + }) + + t.Run("packets are relayed", func(t *testing.T) { + s.AssertPacketRelayed(ctx, chainA, channelA.PortID, channelA.ChannelID, 1) + + actualBalance, err := chainB.GetBalance(ctx, chainBAddress, chainBIBCToken.IBCDenom()) + s.Require().NoError(err) + + expected := testvalues.IBCTransferAmount * 2 + s.Require().Equal(expected, actualBalance) + }) + + t.Run("check that the v2 solo machine clientstate has been updated to the v3 solo machine clientstate", func(t *testing.T) { + res, err := s.ClientState(ctx, chainA, testvalues.SolomachineClientID(2)) + s.Require().NoError(err) + s.Require().Equal(fmt.Sprint("/", proto.MessageName(&solomachine.ClientState{})), res.ClientState.TypeUrl) + }) +} + +// RegisterInterchainAccount will attempt to register an interchain account on the counterparty chain. +func (s *UpgradeTestSuite) RegisterInterchainAccount(ctx context.Context, chain *cosmos.CosmosChain, user ibc.Wallet, msgRegisterAccount *intertxtypes.MsgRegisterAccount) error { + txResp, err := s.BroadcastMessages(ctx, chain, user, msgRegisterAccount) + s.Require().NoError(err) + s.AssertValidTxResponse(txResp) + return err +} + +// getICAVersion returns the version which should be used in the MsgRegisterAccount broadcast from the +// controller chain. +func getICAVersion(chainAVersion, chainBVersion string) string { + chainBIsGreaterThanOrEqualToChainA := semver.Compare(chainAVersion, chainBVersion) <= 0 + if chainBIsGreaterThanOrEqualToChainA { + // allow version to be specified by the controller chain + return "" + } + // explicitly set the version string because the host chain might not yet support incentivized channels. + return icatypes.NewDefaultMetadataString(ibctesting.FirstConnectionID, ibctesting.FirstConnectionID) +} + +// ClientState queries the current ClientState by clientID +func (s *UpgradeTestSuite) ClientState(ctx context.Context, chain ibc.Chain, clientID string) (*clienttypes.QueryClientStateResponse, error) { + queryClient := s.GetChainGRCPClients(chain).ClientQueryClient + res, err := queryClient.ClientState(ctx, &clienttypes.QueryClientStateRequest{ + ClientId: clientID, + }) + if err != nil { + return res, err + } + + return res, nil +} diff --git a/e2e/testsuite/codec.go b/e2e/testsuite/codec.go new file mode 100644 index 00000000000..5ad4a78febe --- /dev/null +++ b/e2e/testsuite/codec.go @@ -0,0 +1,66 @@ +package testsuite + +import ( + "github.com/cosmos/cosmos-sdk/codec" + sdkcodec "github.com/cosmos/cosmos-sdk/crypto/codec" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/cosmos/cosmos-sdk/x/authz" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + govv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" + govv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" + grouptypes "github.com/cosmos/cosmos-sdk/x/group" + proposaltypes "github.com/cosmos/cosmos-sdk/x/params/types/proposal" + upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" + intertxtypes "github.com/cosmos/interchain-accounts/x/inter-tx/types" + + icacontrollertypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/controller/types" + feetypes "github.com/cosmos/ibc-go/v7/modules/apps/29-fee/types" + transfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" + v7migrations "github.com/cosmos/ibc-go/v7/modules/core/02-client/migrations/v7" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + connectiontypes "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + solomachine "github.com/cosmos/ibc-go/v7/modules/light-clients/06-solomachine" + ibctmtypes "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" + simappparams "github.com/cosmos/ibc-go/v7/testing/simapp/params" +) + +func Codec() *codec.ProtoCodec { + cdc, _ := codecAndEncodingConfig() + return cdc +} + +func EncodingConfig() simappparams.EncodingConfig { + _, cfg := codecAndEncodingConfig() + return cfg +} + +func codecAndEncodingConfig() (*codec.ProtoCodec, simappparams.EncodingConfig) { + cfg := simappparams.MakeTestEncodingConfig() + + // ibc types + icacontrollertypes.RegisterInterfaces(cfg.InterfaceRegistry) + feetypes.RegisterInterfaces(cfg.InterfaceRegistry) + intertxtypes.RegisterInterfaces(cfg.InterfaceRegistry) + solomachine.RegisterInterfaces(cfg.InterfaceRegistry) + v7migrations.RegisterInterfaces(cfg.InterfaceRegistry) + transfertypes.RegisterInterfaces(cfg.InterfaceRegistry) + clienttypes.RegisterInterfaces(cfg.InterfaceRegistry) + channeltypes.RegisterInterfaces(cfg.InterfaceRegistry) + connectiontypes.RegisterInterfaces(cfg.InterfaceRegistry) + ibctmtypes.RegisterInterfaces(cfg.InterfaceRegistry) + + // all other types + upgradetypes.RegisterInterfaces(cfg.InterfaceRegistry) + banktypes.RegisterInterfaces(cfg.InterfaceRegistry) + govv1beta1.RegisterInterfaces(cfg.InterfaceRegistry) + govv1.RegisterInterfaces(cfg.InterfaceRegistry) + authtypes.RegisterInterfaces(cfg.InterfaceRegistry) + sdkcodec.RegisterInterfaces(cfg.InterfaceRegistry) + grouptypes.RegisterInterfaces(cfg.InterfaceRegistry) + proposaltypes.RegisterInterfaces(cfg.InterfaceRegistry) + authz.RegisterInterfaces(cfg.InterfaceRegistry) + + cdc := codec.NewProtoCodec(cfg.InterfaceRegistry) + return cdc, cfg +} diff --git a/e2e/testsuite/grpc_query.go b/e2e/testsuite/grpc_query.go new file mode 100644 index 00000000000..ff38ae0f09e --- /dev/null +++ b/e2e/testsuite/grpc_query.go @@ -0,0 +1,159 @@ +package testsuite + +import ( + "context" + + govtypesv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" + govtypesbeta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" + intertxtypes "github.com/cosmos/interchain-accounts/x/inter-tx/types" + "github.com/strangelove-ventures/interchaintest/v7/chain/cosmos" + "github.com/strangelove-ventures/interchaintest/v7/ibc" + + controllertypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/controller/types" + feetypes "github.com/cosmos/ibc-go/v7/modules/apps/29-fee/types" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + ibcexported "github.com/cosmos/ibc-go/v7/modules/core/exported" +) + +// QueryClientState queries the client state on the given chain for the provided clientID. +func (s *E2ETestSuite) QueryClientState(ctx context.Context, chain ibc.Chain, clientID string) (ibcexported.ClientState, error) { + queryClient := s.GetChainGRCPClients(chain).ClientQueryClient + res, err := queryClient.ClientState(ctx, &clienttypes.QueryClientStateRequest{ + ClientId: clientID, + }) + if err != nil { + return nil, err + } + + clientState, err := clienttypes.UnpackClientState(res.ClientState) + if err != nil { + return nil, err + } + + return clientState, nil +} + +// QueryClientStatus queries the status of the client by clientID +func (s *E2ETestSuite) QueryClientStatus(ctx context.Context, chain ibc.Chain, clientID string) (string, error) { + queryClient := s.GetChainGRCPClients(chain).ClientQueryClient + res, err := queryClient.ClientStatus(ctx, &clienttypes.QueryClientStatusRequest{ + ClientId: clientID, + }) + if err != nil { + return "", err + } + + return res.Status, nil +} + +// QueryChannel queries the channel on a given chain for the provided portID and channelID +func (s *E2ETestSuite) QueryChannel(ctx context.Context, chain ibc.Chain, portID, channelID string) (channeltypes.Channel, error) { + queryClient := s.GetChainGRCPClients(chain).ChannelQueryClient + res, err := queryClient.Channel(ctx, &channeltypes.QueryChannelRequest{ + PortId: portID, + ChannelId: channelID, + }) + if err != nil { + return channeltypes.Channel{}, err + } + + return *res.Channel, nil +} + +// QueryPacketCommitment queries the packet commitment on the given chain for the provided channel and sequence. +func (s *E2ETestSuite) QueryPacketCommitment(ctx context.Context, chain ibc.Chain, portID, channelID string, sequence uint64) ([]byte, error) { + queryClient := s.GetChainGRCPClients(chain).ChannelQueryClient + res, err := queryClient.PacketCommitment(ctx, &channeltypes.QueryPacketCommitmentRequest{ + PortId: portID, + ChannelId: channelID, + Sequence: sequence, + }) + if err != nil { + return nil, err + } + return res.Commitment, nil +} + +// QueryInterchainAccount queries the interchain account for the given owner and connectionID. +func (s *E2ETestSuite) QueryInterchainAccount(ctx context.Context, chain ibc.Chain, owner, connectionID string) (string, error) { + queryClient := s.GetChainGRCPClients(chain).ICAQueryClient + res, err := queryClient.InterchainAccount(ctx, &controllertypes.QueryInterchainAccountRequest{ + Owner: owner, + ConnectionId: connectionID, + }) + if err != nil { + return "", err + } + return res.Address, nil +} + +// QueryInterchainAccountLegacy queries the interchain account for the given owner and connectionID using the intertx module. +func (s *E2ETestSuite) QueryInterchainAccountLegacy(ctx context.Context, chain ibc.Chain, owner, connectionID string) (string, error) { + queryClient := s.GetChainGRCPClients(chain).InterTxQueryClient + res, err := queryClient.InterchainAccount(ctx, &intertxtypes.QueryInterchainAccountRequest{ + Owner: owner, + ConnectionId: connectionID, + }) + if err != nil { + return "", err + } + + return res.InterchainAccountAddress, nil +} + +// QueryIncentivizedPacketsForChannel queries the incentivized packets on the specified channel. +func (s *E2ETestSuite) QueryIncentivizedPacketsForChannel( + ctx context.Context, + chain *cosmos.CosmosChain, + portId, + channelId string, +) ([]*feetypes.IdentifiedPacketFees, error) { + queryClient := s.GetChainGRCPClients(chain).FeeQueryClient + res, err := queryClient.IncentivizedPacketsForChannel(ctx, &feetypes.QueryIncentivizedPacketsForChannelRequest{ + PortId: portId, + ChannelId: channelId, + }) + if err != nil { + return nil, err + } + return res.IncentivizedPackets, err +} + +// QueryCounterPartyPayee queries the counterparty payee of the given chain and relayer address on the specified channel. +func (s *E2ETestSuite) QueryCounterPartyPayee(ctx context.Context, chain ibc.Chain, relayerAddress, channelID string) (string, error) { + queryClient := s.GetChainGRCPClients(chain).FeeQueryClient + res, err := queryClient.CounterpartyPayee(ctx, &feetypes.QueryCounterpartyPayeeRequest{ + ChannelId: channelID, + Relayer: relayerAddress, + }) + if err != nil { + return "", err + } + return res.CounterpartyPayee, nil +} + +// QueryProposal queries the governance proposal on the given chain with the given proposal ID. +func (s *E2ETestSuite) QueryProposal(ctx context.Context, chain ibc.Chain, proposalID uint64) (govtypesbeta1.Proposal, error) { + queryClient := s.GetChainGRCPClients(chain).GovQueryClient + res, err := queryClient.Proposal(ctx, &govtypesbeta1.QueryProposalRequest{ + ProposalId: proposalID, + }) + if err != nil { + return govtypesbeta1.Proposal{}, err + } + + return res.Proposal, nil +} + +func (s *E2ETestSuite) QueryProposalV1(ctx context.Context, chain ibc.Chain, proposalID uint64) (govtypesv1.Proposal, error) { + queryClient := s.GetChainGRCPClients(chain).GovQueryClientV1 + res, err := queryClient.Proposal(ctx, &govtypesv1.QueryProposalRequest{ + ProposalId: proposalID, + }) + if err != nil { + return govtypesv1.Proposal{}, err + } + + return *res.Proposal, nil +} diff --git a/e2e/testsuite/relayer.go b/e2e/testsuite/relayer.go deleted file mode 100644 index 123a54465d1..00000000000 --- a/e2e/testsuite/relayer.go +++ /dev/null @@ -1,24 +0,0 @@ -package testsuite - -import ( - "testing" - - "e2e/testconfig" - - dockerclient "github.com/docker/docker/client" - "github.com/strangelove-ventures/ibctest" - "github.com/strangelove-ventures/ibctest/ibc" - "github.com/strangelove-ventures/ibctest/relayer" - "go.uber.org/zap" -) - -const ( - cosmosRelayerRepository = "ghcr.io/cosmos/relayer" -) - -// newCosmosRelayer returns an instance of the go relayer. -func newCosmosRelayer(t *testing.T, tc testconfig.TestConfig, logger *zap.Logger, dockerClient *dockerclient.Client, network string) ibc.Relayer { - return ibctest.NewBuiltinRelayerFactory(ibc.CosmosRly, logger, relayer.CustomDockerImage(cosmosRelayerRepository, tc.RlyTag)).Build( - t, dockerClient, network, - ) -} diff --git a/e2e/testsuite/testsuite.go b/e2e/testsuite/testsuite.go index 7874f956639..edd4642dcb5 100644 --- a/e2e/testsuite/testsuite.go +++ b/e2e/testsuite/testsuite.go @@ -2,28 +2,45 @@ package testsuite import ( "context" + "errors" "fmt" - "io/ioutil" + "strconv" "strings" "time" - "e2e/testconfig" - + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/tx" sdk "github.com/cosmos/cosmos-sdk/types" + signingtypes "github.com/cosmos/cosmos-sdk/types/tx/signing" + authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/cosmos/cosmos-sdk/x/authz" + govtypesv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" + govtypesv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" + grouptypes "github.com/cosmos/cosmos-sdk/x/group" + paramsproposaltypes "github.com/cosmos/cosmos-sdk/x/params/types/proposal" + intertxtypes "github.com/cosmos/interchain-accounts/x/inter-tx/types" dockerclient "github.com/docker/docker/client" - "github.com/strangelove-ventures/ibctest" - "github.com/strangelove-ventures/ibctest/broadcast" - "github.com/strangelove-ventures/ibctest/chain/cosmos" - "github.com/strangelove-ventures/ibctest/ibc" - "github.com/strangelove-ventures/ibctest/test" - "github.com/strangelove-ventures/ibctest/testreporter" + interchaintest "github.com/strangelove-ventures/interchaintest/v7" + "github.com/strangelove-ventures/interchaintest/v7/chain/cosmos" + "github.com/strangelove-ventures/interchaintest/v7/ibc" + "github.com/strangelove-ventures/interchaintest/v7/testreporter" + test "github.com/strangelove-ventures/interchaintest/v7/testutil" "github.com/stretchr/testify/suite" "go.uber.org/zap" "go.uber.org/zap/zaptest" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" - feetypes "github.com/cosmos/ibc-go/v4/modules/apps/29-fee/types" + "github.com/cosmos/ibc-go/e2e/relayer" + "github.com/cosmos/ibc-go/e2e/semverutil" + "github.com/cosmos/ibc-go/e2e/testconfig" + "github.com/cosmos/ibc-go/e2e/testvalues" + controllertypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/controller/types" + feetypes "github.com/cosmos/ibc-go/v7/modules/apps/29-fee/types" + transfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" ) const ( @@ -31,6 +48,8 @@ const ( ChainARelayerName = "rlyA" // ChainBRelayerName is the name given to the relayer wallet on ChainB ChainBRelayerName = "rlyB" + // DefaultGasValue is the default gas value used to configure tx.Factory + DefaultGasValue = 500000 ) // E2ETestSuite has methods and functionality which can be shared among all test suites. @@ -43,13 +62,28 @@ type E2ETestSuite struct { DockerClient *dockerclient.Client network string startRelayerFn func(relayer ibc.Relayer) + + // pathNameIndex is the latest index to be used for generating paths + pathNameIndex uint64 } // GRPCClients holds a reference to any GRPC clients that are needed by the tests. // These should typically be used for query clients only. If we need to make changes, we should // use E2ETestSuite.BroadcastMessages to broadcast transactions instead. type GRPCClients struct { - FeeQueryClient feetypes.QueryClient + ClientQueryClient clienttypes.QueryClient + ChannelQueryClient channeltypes.QueryClient + FeeQueryClient feetypes.QueryClient + ICAQueryClient controllertypes.QueryClient + InterTxQueryClient intertxtypes.QueryClient + + // SDK query clients + GovQueryClient govtypesv1beta1.QueryClient + GovQueryClientV1 govtypesv1.QueryClient + GroupsQueryClient grouptypes.QueryClient + ParamsQueryClient paramsproposaltypes.QueryClient + AuthQueryClient authtypes.QueryClient + AuthZQueryClient authz.QueryClient } // path is a pairing of two chains which will be used in a test. @@ -65,9 +99,9 @@ func newPath(chainA, chainB *cosmos.CosmosChain) path { } } -// GetRelayerUsers returns two ibctest.User instances which can be used for the relayer users +// GetRelayerUsers returns two ibc.Wallet instances which can be used for the relayer users // on the two chains. -func (s *E2ETestSuite) GetRelayerUsers(ctx context.Context, chainOpts ...testconfig.ChainOptionConfiguration) (*ibctest.User, *ibctest.User) { +func (s *E2ETestSuite) GetRelayerUsers(ctx context.Context, chainOpts ...testconfig.ChainOptionConfiguration) (ibc.Wallet, ibc.Wallet) { chainA, chainB := s.GetChains(chainOpts...) chainAAccountBytes, err := chainA.GetAddress(ctx, ChainARelayerName) s.Require().NoError(err) @@ -75,16 +109,10 @@ func (s *E2ETestSuite) GetRelayerUsers(ctx context.Context, chainOpts ...testcon chainBAccountBytes, err := chainB.GetAddress(ctx, ChainBRelayerName) s.Require().NoError(err) - chainARelayerUser := ibctest.User{ - Address: chainAAccountBytes, - KeyName: ChainARelayerName, - } + chainARelayerUser := cosmos.NewWallet(ChainARelayerName, chainAAccountBytes, "", chainA.Config()) + chainBRelayerUser := cosmos.NewWallet(ChainBRelayerName, chainBAccountBytes, "", chainB.Config()) - chainBRelayerUser := ibctest.User{ - Address: chainBAccountBytes, - KeyName: ChainBRelayerName, - } - return &chainARelayerUser, &chainBRelayerUser + return chainARelayerUser, chainBRelayerUser } // SetupChainsRelayerAndChannel create two chains, a relayer, establishes a connection and creates a channel @@ -93,37 +121,33 @@ func (s *E2ETestSuite) GetRelayerUsers(ctx context.Context, chainOpts ...testcon // This should be called at the start of every test, unless fine grained control is required. func (s *E2ETestSuite) SetupChainsRelayerAndChannel(ctx context.Context, channelOpts ...func(*ibc.CreateChannelOptions)) (ibc.Relayer, ibc.ChannelOutput) { chainA, chainB := s.GetChains() - home, err := ioutil.TempDir("", "") - s.Require().NoError(err) - r := newCosmosRelayer(s.T(), testconfig.FromEnv(), s.logger, s.DockerClient, s.network) + r := relayer.New(s.T(), testconfig.FromEnv().RelayerConfig, s.logger, s.DockerClient, s.network) - pathName := fmt.Sprintf("%s-path", s.T().Name()) - pathName = strings.ReplaceAll(pathName, "/", "-") - - ic := ibctest.NewInterchain(). - AddChain(chainA). - AddChain(chainB). - AddRelayer(r, "r"). - AddLink(ibctest.InterchainLink{ - Chain1: chainA, - Chain2: chainB, - Relayer: r, - Path: pathName, - }) + pathName := s.generatePathName() channelOptions := ibc.DefaultChannelOpts() for _, opt := range channelOpts { opt(&channelOptions) } - eRep := s.getRelayerExecReporter() - s.Require().NoError(ic.Build(ctx, eRep, ibctest.InterchainBuildOptions{ - TestName: s.T().Name(), - HomeDir: home, - Client: s.DockerClient, - NetworkID: s.network, - CreateChannelOpts: channelOptions, + ic := interchaintest.NewInterchain(). + AddChain(chainA). + AddChain(chainB). + AddRelayer(r, "r"). + AddLink(interchaintest.InterchainLink{ + Chain1: chainA, + Chain2: chainB, + Relayer: r, + Path: pathName, + CreateChannelOpts: channelOptions, + }) + + eRep := s.GetRelayerExecReporter() + s.Require().NoError(ic.Build(ctx, eRep, interchaintest.InterchainBuildOptions{ + TestName: s.T().Name(), + Client: s.DockerClient, + NetworkID: s.network, })) s.startRelayerFn = func(relayer ibc.Relayer) { @@ -140,14 +164,70 @@ func (s *E2ETestSuite) SetupChainsRelayerAndChannel(ctx context.Context, channel time.Sleep(time.Second * 10) } - s.initGRPCClients(chainA) - s.initGRPCClients(chainB) + s.InitGRPCClients(chainA) + s.InitGRPCClients(chainB) chainAChannels, err := r.GetChannels(ctx, eRep, chainA.Config().ChainID) s.Require().NoError(err) return r, chainAChannels[len(chainAChannels)-1] } +// SetupSingleChain creates and returns a single CosmosChain for usage in e2e tests. +// This is useful for testing single chain functionality when performing coordinated upgrades as well as testing localhost ibc client functionality. +// TODO: Actually setup a single chain. Seeing panic: runtime error: index out of range [0] with length 0 when using a single chain. +// issue: https://github.com/strangelove-ventures/interchaintest/issues/401 +func (s *E2ETestSuite) SetupSingleChain(ctx context.Context) *cosmos.CosmosChain { + chainA, chainB := s.GetChains() + + ic := interchaintest.NewInterchain().AddChain(chainA).AddChain(chainB) + + eRep := s.GetRelayerExecReporter() + s.Require().NoError(ic.Build(ctx, eRep, interchaintest.InterchainBuildOptions{ + TestName: s.T().Name(), + Client: s.DockerClient, + NetworkID: s.network, + SkipPathCreation: true, + })) + + s.InitGRPCClients(chainA) + s.InitGRPCClients(chainB) + + return chainA +} + +// generatePathName generates the path name using the test suites name +func (s *E2ETestSuite) generatePathName() string { + pathName := fmt.Sprintf("%s-path-%d", s.T().Name(), s.pathNameIndex) + s.pathNameIndex++ + return strings.ReplaceAll(pathName, "/", "-") +} + +// generatePath generates the path name using the test suites name +func (s *E2ETestSuite) generatePath(ctx context.Context, relayer ibc.Relayer) string { + chainA, chainB := s.GetChains() + chainAID := chainA.Config().ChainID + chainBID := chainB.Config().ChainID + + pathName := s.generatePathName() + err := relayer.GeneratePath(ctx, s.GetRelayerExecReporter(), chainAID, chainBID, pathName) + s.Require().NoError(err) + + return pathName +} + +// SetupClients creates clients on chainA and chainB using the provided create client options +func (s *E2ETestSuite) SetupClients(ctx context.Context, relayer ibc.Relayer, opts ibc.CreateClientOptions) { + pathName := s.generatePath(ctx, relayer) + err := relayer.CreateClients(ctx, s.GetRelayerExecReporter(), pathName, opts) + s.Require().NoError(err) +} + +// UpdateClients updates clients on chainA and chainB +func (s *E2ETestSuite) UpdateClients(ctx context.Context, relayer ibc.Relayer, pathName string) { + err := relayer.UpdateClients(ctx, s.GetRelayerExecReporter(), pathName) + s.Require().NoError(err) +} + // GetChains returns two chains that can be used in a test. The pair returned // is unique to the current test being run. Note: this function does not create containers. func (s *E2ETestSuite) GetChains(chainOpts ...testconfig.ChainOptionConfiguration) (*cosmos.CosmosChain, *cosmos.CosmosChain) { @@ -174,9 +254,21 @@ func (s *E2ETestSuite) GetChains(chainOpts ...testconfig.ChainOptionConfiguratio // BroadcastMessages broadcasts the provided messages to the given chain and signs them on behalf of the provided user. // Once the broadcast response is returned, we wait for a few blocks to be created on both chain A and chain B. -func (s *E2ETestSuite) BroadcastMessages(ctx context.Context, chain *cosmos.CosmosChain, user broadcast.User, msgs ...sdk.Msg) (sdk.TxResponse, error) { +func (s *E2ETestSuite) BroadcastMessages(ctx context.Context, chain *cosmos.CosmosChain, user ibc.Wallet, msgs ...sdk.Msg) (sdk.TxResponse, error) { broadcaster := cosmos.NewBroadcaster(s.T(), chain) - resp, err := ibctest.BroadcastTx(ctx, broadcaster, user, msgs...) + + broadcaster.ConfigureClientContextOptions(func(clientContext client.Context) client.Context { + // use a codec with all the types our tests care about registered. + // BroadcastTx will deserialize the response and will not be able to otherwise. + cdc := Codec() + return clientContext.WithCodec(cdc).WithTxConfig(authtx.NewTxConfig(cdc, []signingtypes.SignMode{signingtypes.SignMode_SIGN_MODE_DIRECT})) + }) + + broadcaster.ConfigureFactoryOptions(func(factory tx.Factory) tx.Factory { + return factory.WithGas(DefaultGasValue) + }) + + resp, err := cosmos.BroadcastTx(ctx, broadcaster, user, msgs...) if err != nil { return sdk.TxResponse{}, err } @@ -186,17 +278,37 @@ func (s *E2ETestSuite) BroadcastMessages(ctx context.Context, chain *cosmos.Cosm return resp, err } +// RegisterCounterPartyPayee broadcasts a MsgRegisterCounterpartyPayee message. +func (s *E2ETestSuite) RegisterCounterPartyPayee(ctx context.Context, chain *cosmos.CosmosChain, + user ibc.Wallet, portID, channelID, relayerAddr, counterpartyPayeeAddr string, +) (sdk.TxResponse, error) { + msg := feetypes.NewMsgRegisterCounterpartyPayee(portID, channelID, relayerAddr, counterpartyPayeeAddr) + return s.BroadcastMessages(ctx, chain, user, msg) +} + +// PayPacketFeeAsync broadcasts a MsgPayPacketFeeAsync message. +func (s *E2ETestSuite) PayPacketFeeAsync( + ctx context.Context, + chain *cosmos.CosmosChain, + user ibc.Wallet, + packetID channeltypes.PacketId, + packetFee feetypes.PacketFee, +) (sdk.TxResponse, error) { + msg := feetypes.NewMsgPayPacketFeeAsync(packetID, packetFee) + return s.BroadcastMessages(ctx, chain, user, msg) +} + // GetRelayerWallets returns the relayer wallets associated with the chains. -func (s *E2ETestSuite) GetRelayerWallets(relayer ibc.Relayer) (ibc.RelayerWallet, ibc.RelayerWallet, error) { +func (s *E2ETestSuite) GetRelayerWallets(relayer ibc.Relayer) (ibc.Wallet, ibc.Wallet, error) { chainA, chainB := s.GetChains() chainARelayerWallet, ok := relayer.GetWallet(chainA.Config().ChainID) if !ok { - return ibc.RelayerWallet{}, ibc.RelayerWallet{}, fmt.Errorf("unable to find chain A relayer wallet") + return nil, nil, fmt.Errorf("unable to find chain A relayer wallet") } chainBRelayerWallet, ok := relayer.GetWallet(chainB.Config().ChainID) if !ok { - return ibc.RelayerWallet{}, ibc.RelayerWallet{}, fmt.Errorf("unable to find chain B relayer wallet") + return nil, nil, fmt.Errorf("unable to find chain B relayer wallet") } return chainARelayerWallet, chainBRelayerWallet, nil } @@ -211,15 +323,23 @@ func (s *E2ETestSuite) RecoverRelayerWallets(ctx context.Context, relayer ibc.Re chainA, chainB := s.GetChains() - if err := chainA.RecoverKey(ctx, ChainARelayerName, chainARelayerWallet.Mnemonic); err != nil { + if err := chainA.RecoverKey(ctx, ChainARelayerName, chainARelayerWallet.Mnemonic()); err != nil { return fmt.Errorf("could not recover relayer wallet on chain A: %s", err) } - if err := chainB.RecoverKey(ctx, ChainBRelayerName, chainBRelayerWallet.Mnemonic); err != nil { + if err := chainB.RecoverKey(ctx, ChainBRelayerName, chainBRelayerWallet.Mnemonic()); err != nil { return fmt.Errorf("could not recover relayer wallet on chain B: %s", err) } return nil } +// Transfer broadcasts a MsgTransfer message. +func (s *E2ETestSuite) Transfer(ctx context.Context, chain *cosmos.CosmosChain, user ibc.Wallet, + portID, channelID string, token sdk.Coin, sender, receiver string, timeoutHeight clienttypes.Height, timeoutTimestamp uint64, memo string, +) (sdk.TxResponse, error) { + msg := transfertypes.NewMsgTransfer(portID, channelID, token, sender, receiver, timeoutHeight, timeoutTimestamp, memo) + return s.BroadcastMessages(ctx, chain, user, msg) +} + // StartRelayer starts the given relayer. func (s *E2ETestSuite) StartRelayer(relayer ibc.Relayer) { if s.startRelayerFn == nil { @@ -229,26 +349,32 @@ func (s *E2ETestSuite) StartRelayer(relayer ibc.Relayer) { s.startRelayerFn(relayer) } +// StopRelayer stops the given relayer. +func (s *E2ETestSuite) StopRelayer(ctx context.Context, relayer ibc.Relayer) { + err := relayer.StopRelayer(ctx, s.GetRelayerExecReporter()) + s.Require().NoError(err) +} + // CreateUserOnChainA creates a user with the given amount of funds on chain A. -func (s *E2ETestSuite) CreateUserOnChainA(ctx context.Context, amount int64) *ibctest.User { +func (s *E2ETestSuite) CreateUserOnChainA(ctx context.Context, amount int64) ibc.Wallet { chainA, _ := s.GetChains() - return ibctest.GetAndFundTestUsers(s.T(), ctx, strings.ReplaceAll(s.T().Name(), " ", "-"), amount, chainA)[0] + return interchaintest.GetAndFundTestUsers(s.T(), ctx, strings.ReplaceAll(s.T().Name(), " ", "-"), amount, chainA)[0] } // CreateUserOnChainB creates a user with the given amount of funds on chain B. -func (s *E2ETestSuite) CreateUserOnChainB(ctx context.Context, amount int64) *ibctest.User { +func (s *E2ETestSuite) CreateUserOnChainB(ctx context.Context, amount int64) ibc.Wallet { _, chainB := s.GetChains() - return ibctest.GetAndFundTestUsers(s.T(), ctx, strings.ReplaceAll(s.T().Name(), " ", "-"), amount, chainB)[0] + return interchaintest.GetAndFundTestUsers(s.T(), ctx, strings.ReplaceAll(s.T().Name(), " ", "-"), amount, chainB)[0] } // GetChainANativeBalance gets the balance of a given user on chain A. -func (s *E2ETestSuite) GetChainANativeBalance(ctx context.Context, user *ibctest.User) (int64, error) { +func (s *E2ETestSuite) GetChainANativeBalance(ctx context.Context, user ibc.Wallet) (int64, error) { chainA, _ := s.GetChains() return GetNativeChainBalance(ctx, chainA, user) } // GetChainBNativeBalance gets the balance of a given user on chain B. -func (s *E2ETestSuite) GetChainBNativeBalance(ctx context.Context, user *ibctest.User) (int64, error) { +func (s *E2ETestSuite) GetChainBNativeBalance(ctx context.Context, user ibc.Wallet) (int64, error) { _, chainB := s.GetChains() return GetNativeChainBalance(ctx, chainB, user) } @@ -260,9 +386,9 @@ func (s *E2ETestSuite) GetChainGRCPClients(chain ibc.Chain) GRPCClients { return cs } -// initGRPCClients establishes GRPC clients with the given chain. +// InitGRPCClients establishes GRPC clients with the given chain. // The created GRPCClients can be retrieved with GetChainGRCPClients. -func (s *E2ETestSuite) initGRPCClients(chain *cosmos.CosmosChain) { +func (s *E2ETestSuite) InitGRPCClients(chain *cosmos.CosmosChain) { // Create a connection to the gRPC server. grpcConn, err := grpc.Dial( chain.GetHostGRPCAddress(), @@ -280,25 +406,42 @@ func (s *E2ETestSuite) initGRPCClients(chain *cosmos.CosmosChain) { } s.grpcClients[chain.Config().ChainID] = GRPCClients{ - FeeQueryClient: feetypes.NewQueryClient(grpcConn), + ClientQueryClient: clienttypes.NewQueryClient(grpcConn), + ChannelQueryClient: channeltypes.NewQueryClient(grpcConn), + FeeQueryClient: feetypes.NewQueryClient(grpcConn), + ICAQueryClient: controllertypes.NewQueryClient(grpcConn), + InterTxQueryClient: intertxtypes.NewQueryClient(grpcConn), + GovQueryClient: govtypesv1beta1.NewQueryClient(grpcConn), + GovQueryClientV1: govtypesv1.NewQueryClient(grpcConn), + GroupsQueryClient: grouptypes.NewQueryClient(grpcConn), + ParamsQueryClient: paramsproposaltypes.NewQueryClient(grpcConn), + AuthQueryClient: authtypes.NewQueryClient(grpcConn), + AuthZQueryClient: authz.NewQueryClient(grpcConn), } } // AssertValidTxResponse verifies that an sdk.TxResponse // has non-empty values. func (s *E2ETestSuite) AssertValidTxResponse(resp sdk.TxResponse) { - respLogsMsg := resp.Logs.String() - s.Require().NotEqual(int64(0), resp.GasUsed, respLogsMsg) - s.Require().NotEqual(int64(0), resp.GasWanted, respLogsMsg) - s.Require().NotEmpty(resp.Events, respLogsMsg) - s.Require().NotEmpty(resp.Data, respLogsMsg) + errorMsg := fmt.Sprintf("%+v", resp) + s.Require().NotEmpty(resp.TxHash, errorMsg) + s.Require().NotEqual(int64(0), resp.GasUsed, errorMsg) + s.Require().NotEqual(int64(0), resp.GasWanted, errorMsg) + s.Require().NotEmpty(resp.Events, errorMsg) + s.Require().NotEmpty(resp.Data, errorMsg) +} + +// AssertPacketRelayed asserts that the packet commitment does not exist on the sending chain. +// The packet commitment will be deleted upon a packet acknowledgement or timeout. +func (s *E2ETestSuite) AssertPacketRelayed(ctx context.Context, chain *cosmos.CosmosChain, portID, channelID string, sequence uint64) { + commitment, _ := s.QueryPacketCommitment(ctx, chain, portID, channelID, sequence) + s.Require().Empty(commitment) } // createCosmosChains creates two separate chains in docker containers. // test and can be retrieved with GetChains. func (s *E2ETestSuite) createCosmosChains(chainOptions testconfig.ChainOptions) (*cosmos.CosmosChain, *cosmos.CosmosChain) { - ctx := context.Background() - client, network := ibctest.DockerSetup(s.T()) + client, network := interchaintest.DockerSetup(s.T()) s.logger = zap.NewExample() s.DockerClient = client @@ -306,35 +449,156 @@ func (s *E2ETestSuite) createCosmosChains(chainOptions testconfig.ChainOptions) logger := zaptest.NewLogger(s.T()) - // TODO(chatton): allow for controller over number of validators and full nodes. - chainA := cosmos.NewCosmosChain(s.T().Name(), *chainOptions.ChainAConfig, 1, 0, logger) - chainB := cosmos.NewCosmosChain(s.T().Name(), *chainOptions.ChainBConfig, 1, 0, logger) - - s.T().Cleanup(func() { - if !s.T().Failed() { - for _, c := range []*cosmos.CosmosChain{chainA, chainB} { - if err := c.Cleanup(ctx); err != nil { - s.T().Logf("Chain cleanup for %s failed: %v", c.Config().ChainID, err) - } - } - } - }) + numValidators, numFullNodes := getValidatorsAndFullNodes() + chainA := cosmos.NewCosmosChain(s.T().Name(), *chainOptions.ChainAConfig, numValidators, numFullNodes, logger) + chainB := cosmos.NewCosmosChain(s.T().Name(), *chainOptions.ChainBConfig, numValidators, numFullNodes, logger) return chainA, chainB } -// getRelayerExecReporter returns a testreporter.RelayerExecReporter instances +// GetRelayerExecReporter returns a testreporter.RelayerExecReporter instances // using the current test's testing.T. -func (s *E2ETestSuite) getRelayerExecReporter() *testreporter.RelayerExecReporter { +func (s *E2ETestSuite) GetRelayerExecReporter() *testreporter.RelayerExecReporter { rep := testreporter.NewNopReporter() return rep.RelayerExecReporter(s.T()) } +// GetTimeoutHeight returns a timeout height of 1000 blocks above the current block height. +// This function should be used when the timeout is never expected to be reached +func (s *E2ETestSuite) GetTimeoutHeight(ctx context.Context, chain *cosmos.CosmosChain) clienttypes.Height { + height, err := chain.Height(ctx) + s.Require().NoError(err) + return clienttypes.NewHeight(clienttypes.ParseChainID(chain.Config().ChainID), uint64(height)+1000) +} + // GetNativeChainBalance returns the balance of a specific user on a chain using the native denom. -func GetNativeChainBalance(ctx context.Context, chain ibc.Chain, user *ibctest.User) (int64, error) { - bal, err := chain.GetBalance(ctx, user.Bech32Address(chain.Config().Bech32Prefix), chain.Config().Denom) +func GetNativeChainBalance(ctx context.Context, chain ibc.Chain, user ibc.Wallet) (int64, error) { + bal, err := chain.GetBalance(ctx, user.FormattedAddress(), chain.Config().Denom) if err != nil { return -1, err } return bal, nil } + +// ExecuteGovProposal submits the given governance proposal using the provided user and uses all validators to vote yes on the proposal. +// It ensures the proposal successfully passes. +func (s *E2ETestSuite) ExecuteGovProposal(ctx context.Context, chain *cosmos.CosmosChain, user ibc.Wallet, content govtypesv1beta1.Content) { + sender, err := sdk.AccAddressFromBech32(user.FormattedAddress()) + s.Require().NoError(err) + + msgSubmitProposal, err := govtypesv1beta1.NewMsgSubmitProposal(content, sdk.NewCoins(sdk.NewCoin(chain.Config().Denom, govtypesv1beta1.DefaultMinDepositTokens)), sender) + s.Require().NoError(err) + + txResp, err := s.BroadcastMessages(ctx, chain, user, msgSubmitProposal) + s.Require().NoError(err) + s.AssertValidTxResponse(txResp) + + // TODO: replace with parsed proposal ID from MsgSubmitProposalResponse + // https://github.com/cosmos/ibc-go/issues/2122 + + proposal, err := s.QueryProposal(ctx, chain, 1) + s.Require().NoError(err) + s.Require().Equal(govtypesv1beta1.StatusVotingPeriod, proposal.Status) + + err = chain.VoteOnProposalAllValidators(ctx, "1", cosmos.ProposalVoteYes) + s.Require().NoError(err) + + // ensure voting period has not passed before validators finished voting + proposal, err = s.QueryProposal(ctx, chain, 1) + s.Require().NoError(err) + s.Require().Equal(govtypesv1beta1.StatusVotingPeriod, proposal.Status) + + time.Sleep(testvalues.VotingPeriod) // pass proposal + + proposal, err = s.QueryProposal(ctx, chain, 1) + s.Require().NoError(err) + s.Require().Equal(govtypesv1beta1.StatusPassed, proposal.Status) +} + +// govv1ProposalTitleAndSummary represents the releases that support the new title and summary fields. +var govv1ProposalTitleAndSummary = semverutil.FeatureReleases{ + MajorVersion: "v7", +} + +// ExecuteGovProposalV1 submits a governance proposal using the provided user and message and uses all validators +// to vote yes on the proposal. It ensures the proposal successfully passes. +func (s *E2ETestSuite) ExecuteGovProposalV1(ctx context.Context, msg sdk.Msg, chain *cosmos.CosmosChain, user ibc.Wallet, proposalID uint64) { + sender, err := sdk.AccAddressFromBech32(user.FormattedAddress()) + s.Require().NoError(err) + + msgs := []sdk.Msg{msg} + msgSubmitProposal, err := govtypesv1.NewMsgSubmitProposal(msgs, sdk.NewCoins(sdk.NewCoin(chain.Config().Denom, govtypesv1.DefaultMinDepositTokens)), sender.String(), "", fmt.Sprintf("e2e gov proposal: %d", proposalID), fmt.Sprintf("executing gov proposal %d", proposalID)) + s.Require().NoError(err) + + if !govv1ProposalTitleAndSummary.IsSupported(chain.Nodes()[0].Image.Version) { + msgSubmitProposal.Title = "" + msgSubmitProposal.Summary = "" + } + + resp, err := s.BroadcastMessages(ctx, chain, user, msgSubmitProposal) + s.AssertValidTxResponse(resp) + s.Require().NoError(err) + + s.Require().NoError(chain.VoteOnProposalAllValidators(ctx, strconv.Itoa(int(proposalID)), cosmos.ProposalVoteYes)) + + time.Sleep(testvalues.VotingPeriod) + + proposal, err := s.QueryProposalV1(ctx, chain, proposalID) + s.Require().NoError(err) + s.Require().Equal(govtypesv1.StatusPassed, proposal.Status) +} + +// QueryModuleAccountAddress returns the sdk.AccAddress of a given module name. +func (s *E2ETestSuite) QueryModuleAccountAddress(ctx context.Context, moduleName string, chain *cosmos.CosmosChain) (sdk.AccAddress, error) { + authClient := s.GetChainGRCPClients(chain).AuthQueryClient + + resp, err := authClient.ModuleAccountByName(ctx, &authtypes.QueryModuleAccountByNameRequest{ + Name: moduleName, + }) + if err != nil { + return nil, err + } + + cfg := EncodingConfig() + + var account authtypes.AccountI + if err := cfg.InterfaceRegistry.UnpackAny(resp.Account, &account); err != nil { + return nil, err + } + moduleAccount, ok := account.(authtypes.ModuleAccountI) + if !ok { + return nil, errors.New(fmt.Sprintf("failed to cast account: %T as ModuleAccount", moduleAccount)) + } + + return moduleAccount.GetAddress(), nil +} + +// QueryGranterGrants returns all GrantAuthorizations for the given granterAddress. +func (s *E2ETestSuite) QueryGranterGrants(ctx context.Context, chain *cosmos.CosmosChain, granterAddress string) ([]*authz.GrantAuthorization, error) { + authzClient := s.GetChainGRCPClients(chain).AuthZQueryClient + queryRequest := &authz.QueryGranterGrantsRequest{ + Granter: granterAddress, + } + + grants, err := authzClient.GranterGrants(ctx, queryRequest) + if err != nil { + return nil, err + } + + return grants.Grants, nil +} + +// GetIBCToken returns the denomination of the full token denom sent to the receiving channel +func GetIBCToken(fullTokenDenom string, portID, channelID string) transfertypes.DenomTrace { + return transfertypes.ParseDenomTrace(fmt.Sprintf("%s/%s/%s", portID, channelID, fullTokenDenom)) +} + +// getValidatorsAndFullNodes returns the number of validators and full nodes respectively that should be used for +// the test. If the test is running in CI, more nodes are used, when running locally a single node is used to +// use less resources and allow the tests to run faster. +func getValidatorsAndFullNodes() (int, int) { + if testconfig.IsCI() { + return 4, 1 + } + return 1, 0 +} diff --git a/e2e/testvalues/values.go b/e2e/testvalues/values.go index ce11c09bc98..c4c09e68781 100644 --- a/e2e/testvalues/values.go +++ b/e2e/testvalues/values.go @@ -1,15 +1,20 @@ package testvalues import ( + "fmt" + "time" + sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/strangelove-ventures/ibctest/ibc" + "github.com/strangelove-ventures/interchaintest/v7/ibc" - feetypes "github.com/cosmos/ibc-go/v4/modules/apps/29-fee/types" + feetypes "github.com/cosmos/ibc-go/v7/modules/apps/29-fee/types" ) const ( - StartingTokenAmount int64 = 10_000_000 - IBCTransferAmount int64 = 10_000 + StartingTokenAmount int64 = 100_000_000 + IBCTransferAmount int64 = 10_000 + InvalidAddress string = "" + VotingPeriod time.Duration = time.Second * 30 ) // ImmediatelyTimeout returns an ibc.IBCTimeout which will cause an IBC transfer to timeout immediately. @@ -30,3 +35,11 @@ func DefaultFee(denom string) feetypes.Fee { func DefaultTransferAmount(denom string) sdk.Coin { return sdk.Coin{Denom: denom, Amount: sdk.NewInt(IBCTransferAmount)} } + +func TendermintClientID(id int) string { + return fmt.Sprintf("07-tendermint-%d", id) +} + +func SolomachineClientID(id int) string { + return fmt.Sprintf("06-solomachine-%d", id) +} diff --git a/go.mod b/go.mod index 7ca10a06af4..bce5260d10e 100644 --- a/go.mod +++ b/go.mod @@ -1,124 +1,162 @@ -go 1.18 +go 1.19 -module github.com/cosmos/ibc-go/v4 - -replace github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alpha.regen.1 +module github.com/cosmos/ibc-go/v7 require ( - github.com/armon/go-metrics v0.4.0 - github.com/confio/ics23/go v0.7.0 - github.com/cosmos/cosmos-sdk v0.45.6 - github.com/gogo/protobuf v1.3.3 + cosmossdk.io/api v0.3.1 + cosmossdk.io/errors v1.0.0-beta.7 + cosmossdk.io/math v1.0.0-beta.6 + cosmossdk.io/simapp v0.0.0-20221216140705-ee8890cf30e7 + cosmossdk.io/tools/rosetta v0.2.0 + github.com/armon/go-metrics v0.4.1 + github.com/cosmos/cosmos-proto v1.0.0-beta.2 + github.com/cosmos/cosmos-sdk v0.47.0-rc2 + github.com/cosmos/gogoproto v1.4.6 + github.com/cosmos/ics23/go v0.9.1-0.20221207100636-b1abd8678aab github.com/golang/protobuf v1.5.2 github.com/gorilla/mux v1.8.0 github.com/grpc-ecosystem/grpc-gateway v1.16.0 github.com/rakyll/statik v0.1.7 - github.com/regen-network/cosmos-proto v0.3.1 github.com/spf13/cast v1.5.0 - github.com/spf13/cobra v1.5.0 - github.com/spf13/viper v1.12.0 - github.com/stretchr/testify v1.8.0 - github.com/tendermint/tendermint v0.34.19 - github.com/tendermint/tm-db v0.6.6 - google.golang.org/genproto v0.0.0-20220519153652-3a47de7e79bd - google.golang.org/grpc v1.48.0 - google.golang.org/protobuf v1.28.0 + github.com/spf13/cobra v1.6.1 + github.com/spf13/viper v1.15.0 + github.com/stretchr/testify v1.8.2 + github.com/tendermint/tendermint v0.37.0-rc2 + github.com/tendermint/tm-db v0.6.7 + google.golang.org/genproto v0.0.0-20230202175211-008b39050e57 + google.golang.org/grpc v1.53.0 + google.golang.org/protobuf v1.28.1 gopkg.in/yaml.v2 v2.4.0 ) -require github.com/gin-gonic/gin v1.7.0 // indirect - require ( - filippo.io/edwards25519 v1.0.0-beta.2 // indirect - github.com/99designs/keyring v1.1.6 // indirect + cloud.google.com/go v0.107.0 // indirect + cloud.google.com/go/compute v1.15.1 // indirect + cloud.google.com/go/compute/metadata v0.2.3 // indirect + cloud.google.com/go/iam v0.8.0 // indirect + cloud.google.com/go/storage v1.27.0 // indirect + cosmossdk.io/core v0.3.2 // indirect + cosmossdk.io/depinject v1.0.0-alpha.3 // indirect + filippo.io/edwards25519 v1.0.0-rc.1 // indirect + github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect + github.com/99designs/keyring v1.2.1 // indirect github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d // indirect - github.com/DataDog/zstd v1.4.5 // indirect - github.com/Workiva/go-datastructures v1.0.53 // indirect + github.com/aws/aws-sdk-go v1.40.45 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/bgentry/speakeasy v0.1.0 // indirect - github.com/btcsuite/btcd v0.22.0-beta // indirect + github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect + github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 // indirect + github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect + github.com/cenkalti/backoff/v4 v4.1.3 // indirect github.com/cespare/xxhash v1.1.0 // indirect - github.com/cespare/xxhash/v2 v2.1.2 // indirect - github.com/coinbase/rosetta-sdk-go v0.7.0 // indirect - github.com/cosmos/btcutil v1.0.4 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e // indirect + github.com/cockroachdb/apd/v2 v2.0.2 // indirect + github.com/confio/ics23/go v0.9.0 // indirect + github.com/cosmos/btcutil v1.0.5 // indirect github.com/cosmos/go-bip39 v1.0.0 // indirect - github.com/cosmos/iavl v0.17.3 // indirect - github.com/cosmos/ledger-cosmos-go v0.11.1 // indirect - github.com/cosmos/ledger-go v0.9.2 // indirect - github.com/danieljoos/wincred v1.0.2 // indirect + github.com/cosmos/gogogateway v1.2.0 // indirect + github.com/cosmos/gorocksdb v1.2.0 // indirect + github.com/cosmos/iavl v0.19.5-rc.1 // indirect + github.com/cosmos/ledger-cosmos-go v0.12.1 // indirect + github.com/cosmos/rosetta-sdk-go v0.9.0 // indirect + github.com/creachadair/taskgroup v0.3.2 // indirect + github.com/danieljoos/wincred v1.1.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect - github.com/dgraph-io/badger/v2 v2.2007.2 // indirect - github.com/dgraph-io/ristretto v0.0.3 // indirect + github.com/dgraph-io/badger/v2 v2.2007.4 // indirect + github.com/dgraph-io/ristretto v0.1.1 // indirect github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect github.com/dustin/go-humanize v1.0.0 // indirect - github.com/dvsekhvalnov/jose2go v0.0.0-20200901110807-248326c1351b // indirect - github.com/felixge/httpsnoop v1.0.1 // indirect - github.com/fsnotify/fsnotify v1.5.4 // indirect + github.com/dvsekhvalnov/jose2go v1.5.0 // indirect + github.com/felixge/httpsnoop v1.0.2 // indirect + github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/go-kit/kit v0.12.0 // indirect - github.com/go-kit/log v0.2.0 // indirect + github.com/go-kit/log v0.2.1 // indirect github.com/go-logfmt/logfmt v0.5.1 // indirect github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect - github.com/gogo/gateway v1.1.0 // indirect - github.com/golang/snappy v0.0.3 // indirect - github.com/google/btree v1.0.0 // indirect + github.com/gogo/googleapis v1.4.1-0.20201022092350-68b0159b7869 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/glog v1.0.0 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/golang/mock v1.6.0 // indirect + github.com/golang/snappy v0.0.4 // indirect + github.com/google/btree v1.1.2 // indirect + github.com/google/go-cmp v0.5.9 // indirect github.com/google/orderedcode v0.0.1 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.2.1 // indirect + github.com/googleapis/gax-go/v2 v2.7.0 // indirect github.com/gorilla/handlers v1.5.1 // indirect github.com/gorilla/websocket v1.5.0 // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect github.com/gtank/merlin v0.1.1 // indirect github.com/gtank/ristretto255 v0.1.2 // indirect + github.com/hashicorp/go-cleanhttp v0.5.2 // indirect + github.com/hashicorp/go-getter v1.6.2 // indirect github.com/hashicorp/go-immutable-radix v1.3.1 // indirect - github.com/hashicorp/golang-lru v0.5.4 // indirect + github.com/hashicorp/go-safetemp v1.0.0 // indirect + github.com/hashicorp/go-version v1.6.0 // indirect + github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d // indirect github.com/hashicorp/hcl v1.0.0 // indirect - github.com/hdevalence/ed25519consensus v0.0.0-20210204194344-59a8610d2b87 // indirect - github.com/improbable-eng/grpc-web v0.14.1 // indirect - github.com/inconshreveable/mousetrap v1.0.0 // indirect + github.com/hdevalence/ed25519consensus v0.0.0-20220222234857-c00d1f31bab3 // indirect + github.com/huandu/skiplist v1.2.0 // indirect + github.com/improbable-eng/grpc-web v0.15.0 // indirect + github.com/inconshreveable/mousetrap v1.0.1 // indirect + github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/jmhodges/levigo v1.0.0 // indirect - github.com/keybase/go-keychain v0.0.0-20190712205309-48d3d31d256d // indirect - github.com/klauspost/compress v1.13.6 // indirect - github.com/lib/pq v1.10.4 // indirect - github.com/libp2p/go-buffer-pool v0.0.2 // indirect - github.com/magiconair/properties v1.8.6 // indirect - github.com/mattn/go-isatty v0.0.14 // indirect + github.com/klauspost/compress v1.15.12 // indirect + github.com/lib/pq v1.10.7 // indirect + github.com/libp2p/go-buffer-pool v0.1.0 // indirect + github.com/magiconair/properties v1.8.7 // indirect + github.com/manifoldco/promptui v0.9.0 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.16 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect - github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643 // indirect + github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0 // indirect github.com/minio/highwayhash v1.0.2 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/mitchellh/go-testing-interface v1.0.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mtibben/percent v0.2.1 // indirect - github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pelletier/go-toml/v2 v2.0.1 // indirect + github.com/pelletier/go-toml/v2 v2.0.6 // indirect github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_golang v1.12.1 // indirect - github.com/prometheus/client_model v0.2.0 // indirect - github.com/prometheus/common v0.32.1 // indirect - github.com/prometheus/procfs v0.7.3 // indirect - github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 // indirect + github.com/prometheus/client_golang v1.14.0 // indirect + github.com/prometheus/client_model v0.3.0 // indirect + github.com/prometheus/common v0.37.0 // indirect + github.com/prometheus/procfs v0.8.0 // indirect + github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect github.com/rs/cors v1.8.2 // indirect - github.com/rs/zerolog v1.26.1 // indirect - github.com/sasha-s/go-deadlock v0.2.1-0.20190427202633-1595213edefa // indirect - github.com/spf13/afero v1.8.2 // indirect + github.com/rs/zerolog v1.28.0 // indirect + github.com/sasha-s/go-deadlock v0.3.1 // indirect + github.com/spf13/afero v1.9.3 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/subosito/gotenv v1.3.0 // indirect - github.com/syndtr/goleveldb v1.0.1-0.20200815110645-5c35d600f0ca // indirect - github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c // indirect - github.com/tendermint/btcd v0.1.1 // indirect - github.com/tendermint/crypto v0.0.0-20191022145703-50d29ede1e15 // indirect + github.com/subosito/gotenv v1.4.2 // indirect + github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect github.com/tendermint/go-amino v0.16.0 // indirect - github.com/zondax/hid v0.9.0 // indirect + github.com/tidwall/btree v1.5.2 // indirect + github.com/ulikunitz/xz v0.5.8 // indirect + github.com/zondax/hid v0.9.1 // indirect + github.com/zondax/ledger-go v0.14.0 // indirect go.etcd.io/bbolt v1.3.6 // indirect - golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 // indirect - golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2 // indirect - golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect - golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect - golang.org/x/text v0.3.7 // indirect - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect - gopkg.in/ini.v1 v1.66.4 // indirect + go.opencensus.io v0.24.0 // indirect + golang.org/x/crypto v0.4.0 // indirect + golang.org/x/exp v0.0.0-20230131160201-f062dba9d201 // indirect + golang.org/x/net v0.6.0 // indirect + golang.org/x/oauth2 v0.4.0 // indirect + golang.org/x/sys v0.5.0 // indirect + golang.org/x/term v0.5.0 // indirect + golang.org/x/text v0.7.0 // indirect + golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect + google.golang.org/api v0.107.0 // indirect + google.golang.org/appengine v1.6.7 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect nhooyr.io/websocket v1.8.6 // indirect + pgregory.net/rapid v0.5.5 // indirect + sigs.k8s.io/yaml v1.3.0 // indirect ) diff --git a/go.sum b/go.sum index 01f587eac52..bcc0cb0255c 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,3 @@ -bazil.org/fuse v0.0.0-20200407214033-5883e5a4b512/go.mod h1:FbcW6z/2VytnFDhZfumh8Ss8zxHE6qpMP5sHTRe0EaM= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= @@ -18,27 +17,23 @@ cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHOb cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= -cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= -cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= -cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= -cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= -cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= -cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= -cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= -cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= -cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= -cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= -cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= +cloud.google.com/go v0.107.0 h1:qkj22L7bgkl6vIeZDlOY2po43Mx/TIa2Wsa7VR+PEww= +cloud.google.com/go v0.107.0/go.mod h1:wpc2eNrD7hXUTy8EKS10jkxpZBjASrORK7goS+3YX2I= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/compute v1.15.1 h1:7UGq3QknM33pw5xATlpzeoomNxsacIVvTqTTvbfajmE= +cloud.google.com/go/compute v1.15.1/go.mod h1:bjjoF/NtFUrkD/urWfdHaKuOPDR5nWIs63rR+SXhcpA= +cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= +cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= -cloud.google.com/go/firestore v1.6.1/go.mod h1:asNXNOzBdyVQmEU+ggO8UPodTkEVFW5Qx+rwHnAz+EY= +cloud.google.com/go/iam v0.8.0 h1:E2osAkZzxI/+8pZcxVLcDtAQx/u+hZXVryUaYQ5O0Kk= +cloud.google.com/go/iam v0.8.0/go.mod h1:lga0/y3iH6CX7sYqypWJ33hf7kkfXJag67naqGESjkE= +cloud.google.com/go/longrunning v0.3.0 h1:NjljC+FYPV3uh5/OwWT6pVU+doBqMg2x/rZlE+CamDs= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= @@ -49,241 +44,188 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= +cloud.google.com/go/storage v1.27.0 h1:YOO045NZI9RKfCj1c5A/ZtuuENUc8OAW+gHdGnDgyMQ= +cloud.google.com/go/storage v1.27.0/go.mod h1:x9DOL8TK/ygDUMieqwfhdpQryTeEkhGKMi80i/iqR2s= +cosmossdk.io/api v0.3.1 h1:NNiOclKRR0AOlO4KIqeaG6PS6kswOMhHD0ir0SscNXE= +cosmossdk.io/api v0.3.1/go.mod h1:DfHfMkiNA2Uhy8fj0JJlOCYOBp4eWUUJ1te5zBGNyIw= +cosmossdk.io/core v0.3.2 h1:KlQIufpJHJvOs7YLGTZsZcCo1WlkencDXepsr8STKZQ= +cosmossdk.io/core v0.3.2/go.mod h1:CO7vbe+evrBvHc0setFHL/u7nlY7HJGzdRSBkT/sirc= +cosmossdk.io/depinject v1.0.0-alpha.3 h1:6evFIgj//Y3w09bqOUOzEpFj5tsxBqdc5CfkO7z+zfw= +cosmossdk.io/depinject v1.0.0-alpha.3/go.mod h1:eRbcdQ7MRpIPEM5YUJh8k97nxHpYbc3sMUnEtt8HPWU= +cosmossdk.io/errors v1.0.0-beta.7 h1:gypHW76pTQGVnHKo6QBkb4yFOJjC+sUGRc5Al3Odj1w= +cosmossdk.io/errors v1.0.0-beta.7/go.mod h1:mz6FQMJRku4bY7aqS/Gwfcmr/ue91roMEKAmDUDpBfE= +cosmossdk.io/math v1.0.0-beta.6 h1:WF29SiFYNde5eYvqO2kdOM9nYbDb44j3YW5B8M1m9KE= +cosmossdk.io/math v1.0.0-beta.6/go.mod h1:gUVtWwIzfSXqcOT+lBVz2jyjfua8DoBdzRsIyaUAT/8= +cosmossdk.io/simapp v0.0.0-20221216140705-ee8890cf30e7 h1:8uwZLyQ6NMZNT0janUI86Q4vF1sIIquSUsijmOkgmp8= +cosmossdk.io/simapp v0.0.0-20221216140705-ee8890cf30e7/go.mod h1:zvJ3aFHDzqQ6NvkCcCHyolr5Wh2BJ9Rg4qVcN1T6Re0= +cosmossdk.io/tools/rosetta v0.2.0 h1:Ae499UiZ9yPNCXvjOBO/R9I1pksCJfxoqWauEZgA/gs= +cosmossdk.io/tools/rosetta v0.2.0/go.mod h1:3mn8QuE2wLUdTi77/gbDXdFqXZdBdiBJhgAWUTSXPv8= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -filippo.io/edwards25519 v1.0.0-beta.2 h1:/BZRNzm8N4K4eWfK28dL4yescorxtO7YG1yun8fy+pI= -filippo.io/edwards25519 v1.0.0-beta.2/go.mod h1:X+pm78QAUPtFLi1z9PYIlS/bdDnvbCOGKtZ+ACWEf7o= -github.com/99designs/keyring v1.1.6 h1:kVDC2uCgVwecxCk+9zoCt2uEL6dt+dfVzMvGgnVcIuM= -github.com/99designs/keyring v1.1.6/go.mod h1:16e0ds7LGQQcT59QqkTg72Hh5ShM51Byv5PEmW6uoRU= -github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4= -github.com/Azure/azure-pipeline-go v0.2.2/go.mod h1:4rQ/NZncSvGqNkkOsNpOU1tgoNuIlp9AfUH5G1tvCHc= -github.com/Azure/azure-sdk-for-go/sdk/azcore v0.19.0/go.mod h1:h6H6c8enJmmocHUbLiiGY6sx7f9i+X3m1CHdd5c6Rdw= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v0.11.0/go.mod h1:HcM1YX14R7CJcghJGOYCgdezslRSVzqwLf/q+4Y2r/0= -github.com/Azure/azure-sdk-for-go/sdk/internal v0.7.0/go.mod h1:yqy467j36fJxcRV2TzfVZ1pCb5vxm4BtZPUdYWe/Xo8= -github.com/Azure/azure-storage-blob-go v0.7.0/go.mod h1:f9YQKtsG1nMisotuTPpO0tjNuEjKRYAcJU8/ydDI++4= -github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= +filippo.io/edwards25519 v1.0.0-rc.1 h1:m0VOOB23frXZvAOK44usCgLWvtsxIoMCTBGJZlpmGfU= +filippo.io/edwards25519 v1.0.0-rc.1/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= +github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 h1:/vQbFIOMbk2FiG/kXiLl8BRyzTWDw7gX/Hz7Dd5eDMs= +github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4/go.mod h1:hN7oaIRCjzsZ2dE+yG5k+rsdt3qcwykqK6HVGcKwsw4= +github.com/99designs/keyring v1.2.1 h1:tYLp1ULvO7i3fI5vE21ReQuj99QFSs7lGm0xWyJo87o= +github.com/99designs/keyring v1.2.1/go.mod h1:fc+wB5KTk9wQ9sDx0kFXB3A0MaeGHM9AwRStKOQ5vOA= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= -github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= -github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= -github.com/Azure/go-autorest/autorest/adal v0.8.0/go.mod h1:Z6vX6WXXuyieHAXwMj0S6HY6e6wcHn37qQMBQlvY3lc= -github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= -github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g= -github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= -github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= -github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM= -github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= -github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d h1:nalkkPQcITbvhmL4+C4cKA87NW0tfm3Kl9VXRoPywFg= github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d/go.mod h1:URdX5+vg25ts3aCh8H5IFZybJYKWhJHYMTnf+ULtoC4= -github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= -github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= -github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= -github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= -github.com/HdrHistogram/hdrhistogram-go v1.1.0/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= -github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= -github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= -github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= -github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= +github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= -github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= -github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= -github.com/VictoriaMetrics/fastcache v1.5.7/go.mod h1:ptDBkNMQI4RtmVo8VS/XwRY6RoTu1dAWCbrk+6WsEM8= github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= -github.com/Workiva/go-datastructures v1.0.52/go.mod h1:Z+F2Rca0qCsVYDS8z7bAGm8f3UkzuWYS/oBZz5a7VVA= -github.com/Workiva/go-datastructures v1.0.53 h1:J6Y/52yX10Xc5JjXmGtWoSSxs3mZnGSaq37xZZh7Yig= -github.com/Workiva/go-datastructures v1.0.53/go.mod h1:1yZL+zfsztete+ePzZz/Zb1/t5BnDuE2Ya2MMGhzP6A= -github.com/Zilliqa/gozilliqa-sdk v1.2.1-0.20201201074141-dd0ecada1be6/go.mod h1:eSYp2T6f0apnuW8TzhV3f6Aff2SE8Dwio++U4ha4yEM= -github.com/adlio/schema v1.1.13/go.mod h1:L5Z7tw+7lRK1Fnpi/LT/ooCP1elkXn0krMWBQHUhEDE= -github.com/adlio/schema v1.3.0 h1:eSVYLxYWbm/6ReZBCkLw4Fz7uqC+ZNoPvA39bOwi52A= -github.com/adlio/schema v1.3.0/go.mod h1:51QzxkpeFs6lRY11kPye26IaFPOV+HqEj01t5aXXKfs= -github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= +github.com/adlio/schema v1.3.3 h1:oBJn8I02PyTB466pZO1UZEn1TV5XLlifBSyMrmHl/1I= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= -github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= +github.com/alecthomas/participle/v2 v2.0.0-alpha7 h1:cK4vjj0VSgb3lN1nuKA5F7dw+1s1pWBe5bx7nNCnN+c= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= -github.com/aristanetworks/goarista v0.0.0-20170210015632-ea17b1a17847/go.mod h1:D/tb0zPVXnP7fmsLZjtdUhSsumbK/ij54UXjjVgMGxQ= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-metrics v0.3.9/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= -github.com/armon/go-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= -github.com/armon/go-metrics v0.4.0 h1:yCQqn7dwca4ITXb+CbubHmedzaQYHhNhrEXLYUeEe8Q= -github.com/armon/go-metrics v0.4.0/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= +github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA= +github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= -github.com/aws/aws-sdk-go v1.25.48/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go v1.15.78/go.mod h1:E3/ieXAlvM0XWO57iftYVDLLvQ824smPP3ATZkfNZeM= github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go v1.40.45 h1:QN1nsY27ssD/JmW4s83qmSb+uL6DG4GmCDzjmJB4xUI= github.com/aws/aws-sdk-go v1.40.45/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= -github.com/aws/aws-sdk-go-v2 v1.9.1/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4= -github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.8.1/go.mod h1:CM+19rL1+4dFWnOQKwDc7H1KwXTz+h61oUSHyhV0b3o= -github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= -github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= +github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas= +github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= -github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= -github.com/btcsuite/btcd v0.0.0-20171128150713-2e60448ffcc6/go.mod h1:Dmm/EzmjnCiweXmzRIAiUWCInVmPgjkzgv5k4tVyXiQ= -github.com/btcsuite/btcd v0.0.0-20190115013929-ed77733ec07d/go.mod h1:d3C0AkH6BRcvO8T0UEPu53cnw4IbV63x1bEjildYhO0= -github.com/btcsuite/btcd v0.0.0-20190315201642-aa6e0f35703c/go.mod h1:DrZx5ec/dmnfpw9KyYoQyYo7d0KEvTkk/5M/vbZjAr8= -github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= -github.com/btcsuite/btcd v0.21.0-beta/go.mod h1:ZSWyehm27aAuS9bvkATT+Xte3hjHZ+MRgMY/8NJ7K94= -github.com/btcsuite/btcd v0.22.0-beta h1:LTDpDKUM5EeOFBPM8IXpinEcmZ6FWfNZbE3lfrfdnWo= -github.com/btcsuite/btcd v0.22.0-beta/go.mod h1:9n5ntfhhHQBIhUvlhDvD3Qg6fRUj4jkN0VB8L8svzOA= -github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= -github.com/btcsuite/btcutil v0.0.0-20180706230648-ab6388e0c60a/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= -github.com/btcsuite/btcutil v0.0.0-20190207003914-4c204d697803/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= -github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= -github.com/btcsuite/btcutil v1.0.2/go.mod h1:j9HUFwoQRsZL3V4n+qG+CUnEGHOarIxfC3Le2Yhbcts= -github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce h1:YtWJF7RHm2pYCvA5t0RPmAaLUhREsKuKd+SLhxFbFeQ= -github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce/go.mod h1:0DVlHczLPewLcPGEIeUEzfOJhqGPQ0mJJRDBtD307+o= -github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= -github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= -github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I= -github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= -github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= -github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= -github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= +github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 h1:41iFGWnSlI2gVpmOtVTJZNodLdLQLn/KsJqFvXwnd/s= +github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U= +github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= +github.com/btcsuite/btcd/btcutil v1.1.2 h1:XLMbX8JQEiwMcYft2EGi8zPUkoa0abKIU6/BJSRsjzQ= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= -github.com/casbin/casbin/v2 v2.37.0/go.mod h1:vByNa/Fchek0KZUgG5wEsl7iFsiviAYKRtgrQfcJqHg= github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= +github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4= +github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cheggaaa/pb v1.0.27/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s= +github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= -github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng= github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cloudflare/cloudflare-go v0.10.2-0.20190916151808-a80f83b9add9/go.mod h1:1MxXX1Ux4x6mqPmjkUgTP1CdXIBXKX7T+Jk9Gxrmx+U= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cockroachdb/apd/v2 v2.0.2 h1:weh8u7Cneje73dDh+2tEVLUvyBc89iwepWCD8b8034E= +github.com/cockroachdb/apd/v2 v2.0.2/go.mod h1:DDxRlzC2lo3/vSlmSoS7JkqbbrARPuFOGr0B9pvN3Gw= +github.com/cockroachdb/apd/v3 v3.1.0 h1:MK3Ow7LH0W8zkd5GMKA1PvS9qG3bWFI95WaVNfyZJ/w= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= -github.com/coinbase/rosetta-sdk-go v0.7.0 h1:lmTO/JEpCvZgpbkOITL95rA80CPKb5CtMzLaqF2mCNg= -github.com/coinbase/rosetta-sdk-go v0.7.0/go.mod h1:7nD3oBPIiHqhRprqvMgPoGxe/nyq3yftRmpsy29coWE= -github.com/confio/ics23/go v0.6.6/go.mod h1:E45NqnlpxGnpfTWL/xauN7MRwEE28T4Dd4uraToOaKg= -github.com/confio/ics23/go v0.7.0 h1:00d2kukk7sPoHWL4zZBZwzxnpA2pec1NPdwbSokJ5w8= -github.com/confio/ics23/go v0.7.0/go.mod h1:E45NqnlpxGnpfTWL/xauN7MRwEE28T4Dd4uraToOaKg= -github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ= -github.com/containerd/continuity v0.0.0-20190827140505-75bee3e2ccb6/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= -github.com/containerd/continuity v0.2.1 h1:/EeEo2EtN3umhbbgCveyjifoMYg0pS+nMMEemaYw634= -github.com/containerd/continuity v0.2.1/go.mod h1:wCYX+dRqZdImhGucXOqTQn05AhX6EUDaGEMUzTFFpLg= -github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/confio/ics23/go v0.9.0 h1:cWs+wdbS2KRPZezoaaj+qBleXgUk5WOQFMP3CQFGTr4= +github.com/confio/ics23/go v0.9.0/go.mod h1:4LPZ2NYqnYIVRklaozjNR1FScgDJ2s5Xrp+e/mYVRak= +github.com/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvAXZJ/eg= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/cosmos/btcutil v1.0.4 h1:n7C2ngKXo7UC9gNyMNLbzqz7Asuf+7Qv4gnX/rOdQ44= -github.com/cosmos/btcutil v1.0.4/go.mod h1:Ffqc8Hn6TJUdDgHBwIZLtrLQC1KdJ9jGJl/TvgUaxbU= -github.com/cosmos/cosmos-sdk v0.45.6 h1:bnYLOcDp0cKWMLeUTTJIttq6xxRep52ulPxXC3BCfuQ= -github.com/cosmos/cosmos-sdk v0.45.6/go.mod h1:bPeeVMEtVvH3y3xAGHVbK+/CZlpaazzh77hG8ZrcJpI= +github.com/cosmos/btcutil v1.0.5 h1:t+ZFcX77LpKtDBhjucvnOH8C2l2ioGsBNEQ3jef8xFk= +github.com/cosmos/btcutil v1.0.5/go.mod h1:IyB7iuqZMJlthe2tkIFL33xPyzbFYP0XVdS8P5lUPis= +github.com/cosmos/cosmos-proto v1.0.0-beta.2 h1:X3OKvWgK9Gsejo0F1qs5l8Qn6xJV/AzgIWR2wZ8Nua8= +github.com/cosmos/cosmos-proto v1.0.0-beta.2/go.mod h1:+XRCLJ14pr5HFEHIUcn51IKXD1Fy3rkEQqt4WqmN4V0= +github.com/cosmos/cosmos-sdk v0.47.0-rc2 h1:BwQC41zQXG/pN9DdLaWzYJrC911St5lYOQIoW4Hf5wQ= +github.com/cosmos/cosmos-sdk v0.47.0-rc2/go.mod h1:e0ZEpY/nhVoXAkijdHPdFOJNOXCddfvyFrFLp2QmCCY= github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d/go.mod h1:tSxLoYXyBmiFeKpvmq4dzayMdCjCnu8uqmCysIGBT2Y= github.com/cosmos/go-bip39 v1.0.0 h1:pcomnQdrdH22njcAatO0yWojsUnCO3y2tNoV1cb6hHY= github.com/cosmos/go-bip39 v1.0.0/go.mod h1:RNJv0H/pOIVgxw6KS7QeX2a0Uo0aKUlfhZ4xuwvCdJw= -github.com/cosmos/iavl v0.17.3 h1:s2N819a2olOmiauVa0WAhoIJq9EhSXE9HDBAoR9k+8Y= -github.com/cosmos/iavl v0.17.3/go.mod h1:prJoErZFABYZGDHka1R6Oay4z9PrNeFFiMKHDAMOi4w= -github.com/cosmos/ledger-cosmos-go v0.11.1 h1:9JIYsGnXP613pb2vPjFeMMjBI5lEDsEaF6oYorTy6J4= -github.com/cosmos/ledger-cosmos-go v0.11.1/go.mod h1:J8//BsAGTo3OC/vDLjMRFLW6q0WAaXvHnVc7ZmE8iUY= -github.com/cosmos/ledger-go v0.9.2 h1:Nnao/dLwaVTk1Q5U9THldpUMMXU94BOTWPddSmVB6pI= -github.com/cosmos/ledger-go v0.9.2/go.mod h1:oZJ2hHAZROdlHiwTg4t7kP+GKIIkBT+o6c9QWFanOyI= +github.com/cosmos/gogogateway v1.2.0 h1:Ae/OivNhp8DqBi/sh2A8a1D0y638GpL3tkmLQAiKxTE= +github.com/cosmos/gogogateway v1.2.0/go.mod h1:iQpLkGWxYcnCdz5iAdLcRBSw3h7NXeOkZ4GUkT+tbFI= +github.com/cosmos/gogoproto v1.4.2/go.mod h1:cLxOsn1ljAHSV527CHOtaIP91kK6cCrZETRBrkzItWU= +github.com/cosmos/gogoproto v1.4.6 h1:Ee7z15dWJaGlgM2rWrK8N2IX7PQcuccu8oG68jp5RL4= +github.com/cosmos/gogoproto v1.4.6/go.mod h1:VS/ASYmPgv6zkPKLjR9EB91lwbLHOzaGCirmKKhncfI= +github.com/cosmos/gorocksdb v1.2.0 h1:d0l3jJG8M4hBouIZq0mDUHZ+zjOx044J3nGRskwTb4Y= +github.com/cosmos/gorocksdb v1.2.0/go.mod h1:aaKvKItm514hKfNJpUJXnnOWeBnk2GL4+Qw9NHizILw= +github.com/cosmos/iavl v0.19.5-rc.1 h1:4PjF2PdScyPbN1WbXpiQU21YtyonnrMU31xN74g8Rkg= +github.com/cosmos/iavl v0.19.5-rc.1/go.mod h1:X9PKD3J0iFxdmgNLa7b2LYWdsGd90ToV5cAONApkEPw= +github.com/cosmos/ics23/go v0.9.1-0.20221207100636-b1abd8678aab h1:I9ialKTQo7248V827Bba4OuKPmk+FPzmTVHsLXaIJWw= +github.com/cosmos/ics23/go v0.9.1-0.20221207100636-b1abd8678aab/go.mod h1:2CwqasX5dSD7Hbp/9b6lhK6BwoBDCBldx7gPKRukR60= +github.com/cosmos/ledger-cosmos-go v0.12.1 h1:sMBxza5p/rNK/06nBSNmsI/WDqI0pVJFVNihy1Y984w= +github.com/cosmos/ledger-cosmos-go v0.12.1/go.mod h1:dhO6kj+Y+AHIOgAe4L9HL/6NDdyyth4q238I9yFpD2g= +github.com/cosmos/rosetta-sdk-go v0.9.0 h1:3mj2naR+GUhUXabtb96WWSsPFZDCYkdtp6r0jffgugg= +github.com/cosmos/rosetta-sdk-go v0.9.0/go.mod h1:2v41yXL25xxAXrczVSnbDHcQH9CgildruDlGQGKW/JU= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/creachadair/taskgroup v0.3.2 h1:zlfutDS+5XG40AOxcHDSThxKzns8Tnr9jnr6VqkYlkM= +github.com/creachadair/taskgroup v0.3.2/go.mod h1:wieWwecHVzsidg2CsUnFinW1faVN4+kq+TDlRJQ0Wbk= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= -github.com/danieljoos/wincred v1.0.2 h1:zf4bhty2iLuwgjgpraD2E9UbvO+fe54XXGJbOwe23fU= -github.com/danieljoos/wincred v1.0.2/go.mod h1:SnuYRW9lp1oJrZX/dXJqr0cPK5gYXqx3EJbmjhLdK9U= -github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/cucumber/common/gherkin/go/v22 v22.0.0 h1:4K8NqptbvdOrjL9DEea6HFjSpbdT9+Q5kgLpmmsHYl0= +github.com/cucumber/common/messages/go/v17 v17.1.1 h1:RNqopvIFyLWnKv0LfATh34SWBhXeoFTJnSrgm9cT/Ts= +github.com/danieljoos/wincred v1.1.2 h1:QLdCxFs1/Yl4zduvBdcHB8goaYk9RARS2SgLLRuAyr0= +github.com/danieljoos/wincred v1.1.2/go.mod h1:GijpziifJoIBfYh+S7BbkdUTU4LfM+QnGqR5Vl2tAx0= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ= -github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= -github.com/denisenkom/go-mssqldb v0.12.0/go.mod h1:iiK0YP1ZeepvmBQk/QpLEhhTNJgfzrpArPY/aFvc9yU= +github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 h1:HbphB4TFFXpv7MNrT52FGrrgVXF1owhMVTHFZIlnvd4= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0/go.mod h1:DZGJHZMqrU4JJqFAWUS2UO1+lbSKsdiOoYi9Zzey7Fc= github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f h1:U5y3Y5UE0w7amNe7Z5G/twsBW0KEalRQXZzf8ufSh9I= github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f/go.mod h1:xH/i4TFMt8koVQZ6WFms69WAsDWr2XsYL3Hkl7jkoLE= -github.com/dgraph-io/badger/v2 v2.2007.2 h1:EjjK0KqwaFMlPin1ajhP943VPENHJdEz1KLIegjaI3k= -github.com/dgraph-io/badger/v2 v2.2007.2/go.mod h1:26P/7fbL4kUZVEVKLAKXkBXKOydDmM2p1e+NhhnBCAE= +github.com/dgraph-io/badger/v2 v2.2007.4 h1:TRWBQg8UrlUhaFdco01nO2uXwzKS7zd+HVdwV/GHc4o= +github.com/dgraph-io/badger/v2 v2.2007.4/go.mod h1:vSw/ax2qojzbN6eXHIx6KPKtCSHJN/Uz0X0VPruTIhk= github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= -github.com/dgraph-io/ristretto v0.0.3 h1:jh22xisGBjrEVnRZ1DVTpBVQm0Xndu8sMl0CWDzSIBI= -github.com/dgraph-io/ristretto v0.0.3/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= +github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8= +github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y= github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= -github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= -github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= -github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= -github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= -github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498/go.mod h1:Mw6PkjjMXWbTj+nnj4s3QPXq1jaT0s5pC0iFD4+BOAA= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/dvsekhvalnov/jose2go v0.0.0-20200901110807-248326c1351b h1:HBah4D48ypg3J7Np4N+HY/ZR76fx3HEUGxDU6Uk39oQ= -github.com/dvsekhvalnov/jose2go v0.0.0-20200901110807-248326c1351b/go.mod h1:7BvyPhdbLxMXIYTFPLsyJRFMsKmOZnQmzh6Gb+uquuM= -github.com/dvyukov/go-fuzz v0.0.0-20200318091601-be3528f3a813/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw= +github.com/dvsekhvalnov/jose2go v1.5.0 h1:3j8ya4Z4kMCwT5nXIKFSV84YS+HdqSSO0VsTQxaLAeM= +github.com/dvsekhvalnov/jose2go v1.5.0/go.mod h1:QsHjhyTlD/lAVqn/NSbVZmSCGeDehTB/mPZadG+mhXU= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= -github.com/edsrzf/mmap-go v0.0.0-20160512033002-935e0e8a636c/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -291,49 +233,29 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= -github.com/envoyproxy/go-control-plane v0.10.1/go.mod h1:AY7fTTXNdv/aJ2O5jwpxAPOWUZ7hQAEvzN5Pf27BkQQ= github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/envoyproxy/protoc-gen-validate v0.6.2/go.mod h1:2t7qjJNvHPx8IjnBOzl9E9/baC+qXE/TeeyBRzgJDws= -github.com/ethereum/go-ethereum v1.9.25/go.mod h1:vMkFiYLHI4tgPw4k2j4MHKoovchFE8plZ0M9VMk4/oM= -github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51 h1:0JZ+dUmQeA8IIVUMzysrX4/AKuQwWhV2dYQuPZdvdSQ= -github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64= +github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c h1:8ISkoahWXwZR41ois5lSJBSVw4D0OV19Ht/JSTzvSv0= github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojtoVVWjGfOF9635RETekkoH6Cc9SX0A= -github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqLaRiH3MsBH8va0n7s1pQYcu3uTb8G4tygF4Zg= -github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870 h1:E2s37DuLxFhQDg5gKsWoLBOB0n+ZW8s599zru8FJ2/Y= -github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0= -github.com/fatih/color v1.3.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4 h1:7HZCaLC5+BZpmbhCOZJ293Lz68O7PYrF2EzeiFMwCLk= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= -github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= -github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= -github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ= github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/fjl/memsize v0.0.0-20180418122429-ca190fb6ffbc/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= -github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= +github.com/felixge/httpsnoop v1.0.2 h1:+nS9g82KMXccJ/wp0zyRW9ZBHFETmMGtkk+2CTTrW4o= +github.com/felixge/httpsnoop v1.0.2/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= -github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= -github.com/franela/goblin v0.0.0-20210519012713-85d372ac71e2/go.mod h1:VzmDKDJVZI3aJmnRI9VjAn9nJ8qPPsN1fqzr9dqInIo= github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= -github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= -github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= -github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= -github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= +github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14= github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= -github.com/gin-gonic/gin v1.7.0 h1:jGB9xAJQ12AIGNB4HguylppmDK1Am9ppF7XnGXXJuoU= -github.com/gin-gonic/gin v1.7.0/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -343,28 +265,23 @@ github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgO github.com/go-kit/kit v0.12.0 h1:e4o3o3IsBfAKQh5Qbbiqyfu97Ku7jrO/JbohvztANh4= github.com/go-kit/kit v0.12.0/go.mod h1:lHd+EkCZPIwYItmGDDRdhinkzX2A1sj+M9biaEaizzs= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= -github.com/go-kit/log v0.2.0 h1:7i2K3eKTos3Vc0enKCfnVcgHh2olr/MyfboYq7cAcFw= github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= +github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= +github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA= github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= -github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY= github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= -github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE= -github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= -github.com/go-sourcemap/sourcemap v2.1.2+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= -github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= -github.com/go-zookeeper/zk v1.0.2/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw= github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0= github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8= @@ -374,19 +291,24 @@ github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/E github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 h1:ZpnhV/YsD2/4cESfV5+Hoeu/iUR3ruzNvZ+yQfO03a0= github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/gogo/gateway v1.1.0 h1:u0SuhL9+Il+UbjM9VIE3ntfRujKbvVpFvNB4HbjeVQ0= -github.com/gogo/gateway v1.1.0/go.mod h1:S7rR8FRQyG3QFESeSv4l2WnsyzlCLG0CzBbUUo/mbic= +github.com/gofrs/uuid v4.2.0+incompatible h1:yyYWMnhkhrKwwr8gAOcOCYxOOscHgDS9yZgBrnJfGa0= github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= -github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= -github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= -github.com/golang-sql/sqlexp v0.0.0-20170517235910-f1bb20e5a188/go.mod h1:vXjM/+wXQnTPR4KqTKDgJukSZ6amVRtWMPEjE6sQoK8= -github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= +github.com/gogo/googleapis v1.4.1-0.20201022092350-68b0159b7869 h1:kRpU4zq+Pzh4feET49aEWPOzwQy3U2SsbZEQ7QEcif0= +github.com/gogo/googleapis v1.4.1-0.20201022092350-68b0159b7869/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ= +github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= @@ -395,7 +317,6 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -414,17 +335,16 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.3-0.20201103224600-674baa8c7fc3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= +github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -437,15 +357,16 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa h1:Q75Upo5UN4JbPFURXZ8nLKYUvF85dyFRop/vQ0Rv+64= -github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= +github.com/google/martian/v3 v3.2.1 h1:d8MncMlErDFTwQGBK1xhv026j9kqhvw1Qv9IbWT1VLQ= github.com/google/orderedcode v0.0.1 h1:UzfcAexk9Vhv8+9pNOgRu41f16lHq725vPwnSeiG/Us= github.com/google/orderedcode v0.0.1/go.mod h1:iVyU4/qPKHY5h/wSd6rZZCDcLJNxiWO6dvsYES2Sb20= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= @@ -458,19 +379,17 @@ github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/enterprise-certificate-proxy v0.2.1 h1:RY7tHKZcRlk788d5WSo/e83gOyyy742E8GSs771ySpg= +github.com/googleapis/enterprise-certificate-proxy v0.2.1/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= -github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= +github.com/googleapis/gax-go/v2 v2.7.0 h1:IcsPKeInNvYi7eqSaDjiZqDDKu5rsmunY0Y1YupQSSQ= +github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= @@ -481,22 +400,14 @@ github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2z github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.4.1-0.20190629185528-ae1634f6a989/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gotestyourself/gotestyourself v2.2.0+incompatible/go.mod h1:zZKM6oeNM8k+FRljX1mnzVYeS8wiGgQyvST1/GafPbY= -github.com/graph-gophers/graphql-go v0.0.0-20191115155744-f33e81362277/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.2.2/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI= github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw= github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= @@ -507,83 +418,71 @@ github.com/gtank/merlin v0.1.1 h1:eQ90iG7K9pOhtereWsmyRJ6RAwcP4tHTDBHXNg+u5is= github.com/gtank/merlin v0.1.1/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s= github.com/gtank/ristretto255 v0.1.2 h1:JEqUCPA1NvLq5DwYtuzigd7ss8fwbYay9fi4/5uMzcc= github.com/gtank/ristretto255 v0.1.2/go.mod h1:Ph5OpO6c7xKUGROZfWVLiJf9icMDwUeIvY4OmlYW69o= -github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= -github.com/hashicorp/consul/api v1.10.1/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M= -github.com/hashicorp/consul/api v1.12.0/go.mod h1:6pVBMo0ebnYdt2S3H87XhekM/HHrUoTD2XXb/VrZVy0= -github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= -github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= -github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= -github.com/hashicorp/go-hclog v0.16.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= -github.com/hashicorp/go-hclog v1.0.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-getter v1.6.2 h1:7jX7xcB+uVCliddZgeKyNxv0xoT7qL5KDtH7rU4IqIk= +github.com/hashicorp/go-getter v1.6.2/go.mod h1:IZCrswsZPeWv9IkVnLElzRU/gz/QPi6pZHn4tv6vbwA= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= -github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= +github.com/hashicorp/go-safetemp v1.0.0 h1:2HR189eFNrjHQyENnQMMpCiBAsRxzbTMIgBhEyExpmo= +github.com/hashicorp/go-safetemp v1.0.0/go.mod h1:oaerMy3BhqiTbVye6QuFhFtIceqFoDHxNAB65b+Rj1I= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= +github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= -github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d h1:dg1dEPuWpEqDnvIw251EVy4zlP8gWbsGj4BsUKCRpYs= +github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= -github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY= -github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= -github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= -github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= -github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= -github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4= -github.com/hdevalence/ed25519consensus v0.0.0-20210204194344-59a8610d2b87 h1:uUjLpLt6bVvZ72SQc/B4dXcPBw4Vgd7soowdRl52qEM= -github.com/hdevalence/ed25519consensus v0.0.0-20210204194344-59a8610d2b87/go.mod h1:XGsKKeXxeRr95aEOgipvluMPlgjr7dGlk9ZTWOjcUcg= -github.com/holiman/uint256 v1.1.1/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw= +github.com/hdevalence/ed25519consensus v0.0.0-20220222234857-c00d1f31bab3 h1:aSVUgRRRtOrZOC1fYmY9gV0e9z/Iu+xNVSASWjsuyGU= +github.com/hdevalence/ed25519consensus v0.0.0-20220222234857-c00d1f31bab3/go.mod h1:5PC6ZNPde8bBqU/ewGZig35+UIZtw9Ytxez8/q5ZyFE= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/huandu/go-assert v1.1.5 h1:fjemmA7sSfYHJD7CUqs9qTwwfdNAx7/j2/ZlHXzNB3c= +github.com/huandu/go-assert v1.1.5/go.mod h1:yOLvuqZwmcHIC5rIzrBhT7D3Q9c3GFnd0JrPVhn/06U= +github.com/huandu/skiplist v1.2.0 h1:gox56QD77HzSC0w+Ws3MH3iie755GBJU1OER3h5VsYw= +github.com/huandu/skiplist v1.2.0/go.mod h1:7v3iFjLcSAzO4fN5B8dvebvo/qsfumiLiDXMrPiHF9w= github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= -github.com/hudl/fargo v1.4.0/go.mod h1:9Ai6uvFy5fQNq6VPKtg+Ceq1+eTY4nKUlR2JElEOcDo= -github.com/huin/goupnp v1.0.0/go.mod h1:n9v9KO1tAxYH82qOn+UTIFQDmx5n1Zxd/ClZDMX7Bnc= -github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o= -github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/improbable-eng/grpc-web v0.14.1 h1:NrN4PY71A6tAz2sKDvC5JCauENWp0ykG8Oq1H3cpFvw= -github.com/improbable-eng/grpc-web v0.14.1/go.mod h1:zEjGHa8DAlkoOXmswrNvhUGEYQA9UI7DhrGeHR1DMGU= -github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= +github.com/improbable-eng/grpc-web v0.15.0 h1:BN+7z6uNXZ1tQGcNAuaU1YjsLTApzkjt2tzCixLaUPQ= +github.com/improbable-eng/grpc-web v0.15.0/go.mod h1:1sy9HKV4Jt9aEs9JSnkWlRJPuPtwNr0l57L4f878wP8= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/influxdata/influxdb v1.2.3-0.20180221223340-01288bdb0883/go.mod h1:qZna6X/4elxqT3yI9iZYdZrWWdeFOOprn86kgg4+IzY= +github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= +github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= -github.com/influxdata/influxdb1-client v0.0.0-20200827194710-b269163b24ab/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= -github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= -github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e/go.mod h1:G1CVv03EnqU1wYL2dFwXxW2An0az9JTl/ZsqXQeBlkU= -github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jhump/protoreflect v1.9.0 h1:npqHz788dryJiR/l6K/RUQAyh2SwV91+d1dnh4RjO9w= +github.com/jhump/protoreflect v1.12.1-0.20220721211354-060cc04fc18b h1:izTof8BKh/nE1wrKOrloNA5q4odOarjf+Xpe+4qow98= +github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jmhodges/levigo v1.0.0 h1:q5EC36kV79HWeTBWsod3mG11EgStG3qArTKcvlksN1U= github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= -github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -595,101 +494,74 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/julienschmidt/httprouter v1.1.1-0.20170430222011-975b5c4c7c21/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= -github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= -github.com/karalabe/usb v0.0.0-20190919080040-51dc0efba356/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= -github.com/keybase/go-keychain v0.0.0-20190712205309-48d3d31d256d h1:Z+RDyXzjKE0i2sTjZ/b1uxiGtPhFy34Ou/Tk0qwN0kM= -github.com/keybase/go-keychain v0.0.0-20190712205309-48d3d31d256d/go.mod h1:JJNrCn9otv/2QP4D7SMJBgaleKpOf66PnW6F5WGNRIc= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.11.2/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= -github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc= -github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= +github.com/klauspost/compress v1.15.12 h1:YClS/PImqYbn+UILDnqxQCZ3RehC9N318SU3kElDUEM= +github.com/klauspost/compress v1.15.12/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= -github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.10.4 h1:SO9z7FRPzA03QhHKJrH5BXA6HU1rS4V2nIVrrNC1iYk= -github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/libp2p/go-buffer-pool v0.0.2 h1:QNK2iAFa8gjAe1SPz6mHSMuCcjs+X1wlHzeOSqcmlfs= -github.com/libp2p/go-buffer-pool v0.0.2/go.mod h1:MvaB6xw5vOrDl8rYZGLFdKAuk/hRoRZd1Vi32+RXyFM= +github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw= +github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8= +github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg= github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= -github.com/lucasjones/reggen v0.0.0-20180717132126-cdb49ff09d77/go.mod h1:5ELEyG+X8f+meRWHuqUOewBOhvHkl7M76pdGEansxW4= -github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w= github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= -github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= -github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA= +github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-colorable v0.1.0/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= -github.com/mattn/go-ieproxy v0.0.0-20190610004146-91bb50d98149/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc= -github.com/mattn/go-ieproxy v0.0.0-20190702010315-6dee0af9227d/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.5-0.20180830101745-3fb116b82035/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= -github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= -github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= -github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4= -github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643 h1:hLDRPB66XQT/8+wG9WsDpiCvZf1yKO7sz7scAjSlBa0= github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM= -github.com/minio/highwayhash v1.0.1/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= +github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0 h1:QRUSJEgZn2Snx0EmT/QLXibWjSUDjKWvXIT19NBVp94= +github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM= github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g= github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= -github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -697,220 +569,154 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8= -github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= github.com/mtibben/percent v0.2.1 h1:5gssi8Nqo8QU/r2pynCm+hBQHpkB/uNK7BJCFogWdzs= github.com/mtibben/percent v0.2.1/go.mod h1:KG9uO+SZkUp+VkRHsCdYQV3XSZrrSpR3O9ibNBTZrns= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/grpc-proxy v0.0.0-20181017164139-0f1106ef9c76/go.mod h1:x5OoJHDHqxHS801UIuhqGl6QdSAEJvtausosHSdazIo= -github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0= -github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E= github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= -github.com/nats-io/jwt v1.2.2/go.mod h1:/xX356yQA6LuXI9xWW7mZNpxgF2mBmGecH+Fj34sP5Q= -github.com/nats-io/jwt/v2 v2.0.3/go.mod h1:VRP+deawSXyhNjXmxPCHskrR6Mq50BqpEI5SEcNiGlY= github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= -github.com/nats-io/nats-server/v2 v2.5.0/go.mod h1:Kj86UtrXAL6LwYRA6H4RqzkHhK0Vcv2ZnKD5WbQ1t3g= github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= -github.com/nats-io/nats.go v1.12.1/go.mod h1:BPko4oXsySz4aSWeFgOHLZs3G4Jq4ZAyE6/zMCxRT6w= github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= -github.com/nats-io/nkeys v0.2.0/go.mod h1:XdZpAbhgyyODYqjTawOnIOI7VlbKSarI9Gfy1tqEu/s= -github.com/nats-io/nkeys v0.3.0/go.mod h1:gvUNGjVcM2IPr5rCsRsC6Wb3Hr2CQAm08dsxtV6A5y4= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= -github.com/neilotoole/errgroup v0.1.5/go.mod h1:Q2nLGf+594h0CLBs/Mbg6qOr7GtqDK7C2S41udRnToE= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= -github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= -github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= -github.com/olekukonko/tablewriter v0.0.2-0.20190409134802-7e037d187b0c/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= -github.com/onsi/ginkgo v1.16.2/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E= github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= -github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.13.0 h1:7lLHu94wT9Ij0o6EWWclhu0aOh32VxhkwEJvzuWPeak= -github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY= +github.com/onsi/gomega v1.20.0 h1:8W0cWlwFkflGPLltQvLRB7ZVD5HuP6ng320w2IS245Q= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= -github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= -github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM= -github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/opencontainers/runc v1.0.2/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0= -github.com/opencontainers/runc v1.0.3 h1:1hbqejyQWCJBvtKAfdO0b1FmaEf2z/bxnjqbARass5k= -github.com/opencontainers/runc v1.0.3/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0= -github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8= +github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 h1:rc3tiVYb5z54aKaDfakKn0dDjIyPpTtszkjuMzyt7ec= +github.com/opencontainers/runc v1.1.3 h1:vIXrkId+0/J2Ymu2m7VjGvbSlAId9XNRPhn2p4b+d8w= github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= -github.com/openzipkin/zipkin-go v0.2.5/go.mod h1:KpXfKdgRDnnhsxw4pNIH9Md5lyFqKUa4YDFlwRYAMyE= github.com/ory/dockertest v3.3.5+incompatible h1:iLLK6SQwIhcbrG783Dghaaa3WPzGc+4Emza6EbVUUGA= -github.com/ory/dockertest v3.3.5+incompatible/go.mod h1:1vX4m9wsvi00u5bseYwXaSnhNrne+V0E6LAcBILJdPs= -github.com/otiai10/copy v1.6.0 h1:IinKAryFFuPONZ7cm6T6E2QX/vcJwSnlaA5lfoaXIiQ= github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pborman/uuid v0.0.0-20170112150404-1b00554d8222/go.mod h1:VyrYX9gd7irzKovcSS6BIIEwPRkP2Wm2m9ufcdFSJ34= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= -github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/pelletier/go-toml/v2 v2.0.1 h1:8e3L2cCQzLFi2CR4g7vGFuFxX7Jl1kKX8gW+iV0GUKU= -github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= +github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU= +github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= -github.com/performancecopilot/speed/v4 v4.0.0/go.mod h1:qxrSyuDGrTOWfV+uKRFhfxw6h/4HXRGUiZiufxo49BM= -github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0= github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 h1:q2e307iGHPdTGp0hoxKjt1H5pDo6utceo3dQVK3I5XQ= github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o= -github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= -github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= -github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= -github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= -github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.8.0/go.mod h1:O9VU6huf47PktckDQfMTX0Y8tY0/7TSWwj+ITvv0TnM= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.12.1 h1:ZiaPsmm9uiBeaSMRznKsCDNtPCS0T3JVDGF+06gjBzk= github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= +github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= +github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= +github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.14.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.30.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4= github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= +github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= +github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.3.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/tsdb v0.6.2-0.20190402121629-4f204dcbc150/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= +github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= github.com/rakyll/statik v0.1.7 h1:OF3QCZUuyPxuGEP7B4ypUa7sB/iHtqOTDYZXGM8KOdQ= github.com/rakyll/statik v0.1.7/go.mod h1:AlZONWzMtEnMs7W4e/1LURLiI49pIMmp6V9Unghqrcc= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 h1:MkV+77GLUNo5oJ0jf870itWm3D0Sjh7+Za9gazKc5LQ= -github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/regen-network/cosmos-proto v0.3.1 h1:rV7iM4SSFAagvy8RiyhiACbWEGotmqzywPxOvwMdxcg= -github.com/regen-network/cosmos-proto v0.3.1/go.mod h1:jO0sVX6a1B36nmE8C9xBFXpNwWejXC7QqCOnH3O0+YM= -github.com/regen-network/protobuf v1.3.3-alpha.regen.1 h1:OHEc+q5iIAXpqiqFKeLpu5NwTIkVXUs48vFMwzqpqY4= -github.com/regen-network/protobuf v1.3.3-alpha.regen.1/go.mod h1:2DjTFR1HhMQhiWC5sZ4OhQ3+NtdbZ6oBDKQwq5Ou+FI= -github.com/rjeczalik/notify v0.9.1/go.mod h1:rKwnCoCGeuQnwBtTSPL9Dad03Vh2n40ePRrjvIXnJho= +github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM= +github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/regen-network/gocuke v0.6.2 h1:pHviZ0kKAq2U2hN2q3smKNxct6hS0mGByFMHGnWA97M= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= -github.com/rs/cors v0.0.0-20160617231935-a62a804a8a00/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= +github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/rs/cors v1.8.2 h1:KCooALfAYGs415Cwu5ABvv9n9509fSiG5SQJn/AQo4U= github.com/rs/cors v1.8.2/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= -github.com/rs/xhandler v0.0.0-20160618193221-ed27b6fd6521/go.mod h1:RvLn4FgxWubrpZHtQLnOf6EwhN2hEMusxZOhcW9H3UQ= -github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= -github.com/rs/zerolog v1.26.1 h1:/ihwxqH+4z8UxyI70wM1z9yCvkWcfz/a3mj48k/Zngc= -github.com/rs/zerolog v1.26.1/go.mod h1:/wSSJWX7lVrsOwlbyTRSOJvqRlc+WjWlfes+CiJ+tmc= +github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/zerolog v1.28.0 h1:MirSo27VyNi7RJYP3078AA1+Cyzd2GB66qy3aUHvsWY= +github.com/rs/zerolog v1.28.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/sagikazarmark/crypt v0.4.0/go.mod h1:ALv2SRj7GxYV4HO9elxH9nS6M9gW+xDNxqmyJ6RfDFM= github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= -github.com/sasha-s/go-deadlock v0.2.1-0.20190427202633-1595213edefa h1:0U2s5loxrTy6/VgfVoLuVLFJcURKLH49ie0zSch7gh4= -github.com/sasha-s/go-deadlock v0.2.1-0.20190427202633-1595213edefa/go.mod h1:F73l+cr82YSh10GxyRI6qZiCgK64VaZjwesgfQ1/iLM= +github.com/sasha-s/go-deadlock v0.3.1 h1:sqv7fDNShgjcaxkO0JNcOAlr8B9+cV5Ey/OB71efZx0= +github.com/sasha-s/go-deadlock v0.3.1/go.mod h1:F73l+cr82YSh10GxyRI6qZiCgK64VaZjwesgfQ1/iLM= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= -github.com/segmentio/fasthash v1.0.3/go.mod h1:waKX8l2N8yckOgmSsXJi7x1ZfdKZ4x7KRMzBtS3oedY= -github.com/shirou/gopsutil v2.20.5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= -github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/snikch/goodman v0.0.0-20171125024755-10e37e294daa/go.mod h1:oJyF+mSPHbB5mVY2iO9KV3pTt/QbIkGaO8gQ2WrDbP4= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= -github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= -github.com/spf13/afero v1.8.2 h1:xehSyVa0YnHWsJ49JFljMpg1HX19V6NDZ1fkm1Xznbo= -github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo= +github.com/spf13/afero v1.9.3 h1:41FoI0fD7OR7mGcKE/aOiLkGreyf8ifIOQmJANWogMk= +github.com/spf13/afero v1.9.3/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= -github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= -github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= -github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g= -github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU= -github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM= +github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= +github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= @@ -919,25 +725,16 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= -github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= -github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= -github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= -github.com/spf13/viper v1.10.1/go.mod h1:IGlFPqhNAPKRxohIzWpI5QEy4kuI7tcl5WvR+8qy1rU= -github.com/spf13/viper v1.12.0 h1:CZ7eSOd3kZoaYDLbXnmzgQI5RlciuXBMA+18HwHRfZQ= -github.com/spf13/viper v1.12.0/go.mod h1:b6COn30jlNxbm/V2IqWiNWkJ+vZNiMNksliPCiuKtSI= -github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q= -github.com/steakknife/bloomfilter v0.0.0-20180922174646-6819c0d2a570/go.mod h1:8OR4w3TdeIHIh1g6EMY5p0gVNOovcWC+1vpc7naMuAw= -github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3/go.mod h1:hpGUWaI9xL8pRQCTXQgocU38Qw1g0Us7n5PxxTwTCYU= +github.com/spf13/viper v1.15.0 h1:js3yy885G8xwJa6iOISGFwd+qlUo5AvyXb7CiihdtiU= +github.com/spf13/viper v1.15.0/go.mod h1:fFcTBJxvhhzSJiZy8n+PeW6t8l+KeT/uTARa0jHOQLA= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= -github.com/streadway/amqp v1.0.0/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= -github.com/streadway/handy v0.0.0-20200128134331-0f66f006fb2e/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= -github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -945,77 +742,48 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= -github.com/subosito/gotenv v1.3.0 h1:mjC+YW8QpAdXibNi+vNWgzmgBH4+5l5dCXv8cNysBLI= -github.com/subosito/gotenv v1.3.0/go.mod h1:YzJjq/33h7nrwdY+iHMhEOEEbW0ovIz0tB6t6PwAXzs= -github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= -github.com/syndtr/goleveldb v1.0.1-0.20200815110645-5c35d600f0ca h1:Ld/zXl5t4+D69SiV4JoN7kkfvJdOWlPpfxrzxpLMoUk= -github.com/syndtr/goleveldb v1.0.1-0.20200815110645-5c35d600f0ca/go.mod h1:u2MKkTVTVJWe5D1rCvame8WqhBd88EuIwODJZ1VHCPM= -github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c h1:g+WoO5jjkqGAzHWCjJB1zZfXPIAaDpzXIEJ0eS6B5Ok= -github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c/go.mod h1:ahpPrc7HpcfEWDQRZEmnXMzHY03mLDYMCxeDzy46i+8= -github.com/tendermint/btcd v0.1.1 h1:0VcxPfflS2zZ3RiOAHkBiFUcPvbtRj5O7zHmcJWHV7s= -github.com/tendermint/btcd v0.1.1/go.mod h1:DC6/m53jtQzr/NFmMNEu0rxf18/ktVoVtMrnDD5pN+U= -github.com/tendermint/crypto v0.0.0-20191022145703-50d29ede1e15 h1:hqAk8riJvK4RMWx1aInLzndwxKalgi5rTqgfXxOxbEI= -github.com/tendermint/crypto v0.0.0-20191022145703-50d29ede1e15/go.mod h1:z4YtwM70uOnk8h0pjJYlj3zdYwi9l03By6iAIF5j/Pk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= +github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= +github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= +github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= github.com/tendermint/go-amino v0.16.0 h1:GyhmgQKvqF82e2oZeuMSp9JTN0N09emoSZlb2lyGa2E= github.com/tendermint/go-amino v0.16.0/go.mod h1:TQU0M1i/ImAo+tYpZi73AU3V/dKeCoMC9Sphe2ZwGME= -github.com/tendermint/tendermint v0.34.14/go.mod h1:FrwVm3TvsVicI9Z7FlucHV6Znfd5KBc/Lpp69cCwtk0= -github.com/tendermint/tendermint v0.34.19 h1:y0P1qI5wSa9IRuhKnTDA6IUcOrLi1hXJuALR+R7HFEk= -github.com/tendermint/tendermint v0.34.19/go.mod h1:R5+wgIwSxMdKQcmOaeudL0Cjkr3HDkhpcdum6VeU3R4= -github.com/tendermint/tm-db v0.6.4/go.mod h1:dptYhIpJ2M5kUuenLr+Yyf3zQOv1SgBZcl8/BmWlMBw= -github.com/tendermint/tm-db v0.6.6 h1:EzhaOfR0bdKyATqcd5PNeyeq8r+V4bRPHBfyFdD9kGM= -github.com/tendermint/tm-db v0.6.6/go.mod h1:wP8d49A85B7/erz/r4YbKssKw6ylsO/hKtFk7E1aWZI= -github.com/tidwall/gjson v1.6.7/go.mod h1:zeFuBCIqD4sN/gmqBzZ4j7Jd6UcA2Fc56x7QFsv+8fI= -github.com/tidwall/match v1.0.3/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= -github.com/tidwall/pretty v1.0.2/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= -github.com/tidwall/sjson v1.1.4/go.mod h1:wXpKXu8CtDjKAZ+3DrKY5ROCorDFahq8l0tey/Lx1fg= -github.com/tinylib/msgp v1.1.5/go.mod h1:eQsjooMTnV42mHu917E26IogZ2930nFyBQdofk10Udg= +github.com/tendermint/tendermint v0.37.0-rc2 h1:2n1em+jfbhSv6QnBj8F6KHCpbIzZCB8KgcjidJUQNlY= +github.com/tendermint/tendermint v0.37.0-rc2/go.mod h1:uYQO9DRNPeZROa9X3hJOZpYcVREDC2/HST+EiU5g2+A= +github.com/tendermint/tm-db v0.6.7 h1:fE00Cbl0jayAoqlExN6oyQJ7fR/ZtoVOmvPJ//+shu8= +github.com/tendermint/tm-db v0.6.7/go.mod h1:byQDzFkZV1syXr/ReXS808NxA2xvyuuVgXOJ/088L6I= +github.com/tidwall/btree v1.5.2 h1:5eA83Gfki799V3d3bJo9sWk+yL2LRoTEah3O/SA6/8w= +github.com/tidwall/btree v1.5.2/go.mod h1:twD9XRA5jj9VUQGELzDO4HPQTNJsoWWfYEL+EUQ2cKY= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= -github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c/go.mod h1:hzIxponao9Kjc7aWznkXaL4U4TWaDSs8zcsY4Ka08nM= -github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs= -github.com/tyler-smith/go-bip39 v1.0.2/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs= -github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= +github.com/ulikunitz/xz v0.5.8 h1:ERv8V6GKqVi23rgu5cj9pVfVzJbOqAY2Ntl88O6c2nQ= +github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= -github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= -github.com/vmihailenco/msgpack/v5 v5.1.4/go.mod h1:C5gboKD0TJPqWDTVTtrQNfRbiBwHZGo8UTqP/9/XvLI= -github.com/vmihailenco/tagparser v0.1.2/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= -github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208/go.mod h1:IotVbo4F+mw0EzQ08zFqg7pK3FebNXpaMsRy2RT+Ees= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= -github.com/ybbus/jsonrpc v2.1.2+incompatible/go.mod h1:XJrh1eMSzdIYFbM08flv0wp5G35eRniyeGut1z+LSiE= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/zondax/hid v0.9.0 h1:eiT3P6vNxAEVxXMw66eZUAAnU2zD33JBkfG/EnfAKl8= -github.com/zondax/hid v0.9.0/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM= -go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +github.com/zondax/hid v0.9.1 h1:gQe66rtmyZ8VeGFcOpbuH3r7erYtNEAezCAYu8LdkJo= +github.com/zondax/hid v0.9.1/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM= +github.com/zondax/ledger-go v0.14.0 h1:dlMC7aO8Wss1CxBq2I96kZ69Nh1ligzbs8UWOtq/AsA= +github.com/zondax/ledger-go v0.14.0/go.mod h1:fZ3Dqg6qcdXWSOJFKMG8GCTnD7slO/RL2feOQv8K320= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU= go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= -go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= -go.etcd.io/etcd/api/v3 v3.5.1/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= -go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= -go.etcd.io/etcd/client/pkg/v3 v3.5.1/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= -go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= -go.etcd.io/etcd/client/v2 v2.305.1/go.mod h1:pMEacxZW7o8pg4CrFE7pquyCJJzZvkvdD2RibOCCCGs= -go.etcd.io/etcd/client/v3 v3.5.0/go.mod h1:AIKXXVX/DQXtfTEqBryiLTUXwON+GuvO6Z7lLS/oTh0= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= @@ -1024,61 +792,35 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= -go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= -go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= -go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= -go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= -golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190909091759-094676da4a83/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= -golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20210915214749-c084706c2272/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20211215165025-cf75a172585e/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= -golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 h1:kUhD7nTDoI3fVd9G4ORWrbV5NY0liEs/Jg2pv5f+bBA= -golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/crypto v0.4.0 h1:UVQgzMY87xqpKNgb+kDsll2Igd33HszWHFLmpaRMq/8= +golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4= golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= @@ -1087,7 +829,8 @@ golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw= -golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= +golang.org/x/exp v0.0.0-20230131160201-f062dba9d201 h1:BEABXpNXLEz0WxtA+6CQIz2xkg80e+1zrhWyMcq8VzE= +golang.org/x/exp v0.0.0-20230131160201-f062dba9d201/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -1101,26 +844,20 @@ golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRu golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mobile v0.0.0-20200801112145-973feb4309de/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= -golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1132,14 +869,12 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -1157,29 +892,20 @@ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= -golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= -golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210903162142-ad29c8ab022f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210917221730-978cfadd31cf/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211208012354-db4efeb81f4b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2 h1:NWy5+hlRbC7HK+PmcXVUmW1IMyFce7to56IUvhUFm7Y= -golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.6.0 h1:L4ZwwTvKW9gr0ZMS1yrHD9GZhIuVjOBBnaKH+SPQK0Q= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1189,14 +915,10 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.4.0 h1:NF0gk8LVPg1Ml7SSbGyySuoxdsXitj7TvgvuRxIMc/M= +golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1219,37 +941,28 @@ golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1266,56 +979,38 @@ golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200824131525-c12d262b63d8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210903071746-97244b99971b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210917161153-d61c044b1678/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210819135213-f52c844e1c1c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220517195934-5e4e11fc645e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1324,19 +1019,18 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= @@ -1349,12 +1043,10 @@ golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgw golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -1363,7 +1055,6 @@ golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= @@ -1384,29 +1075,20 @@ golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= -golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= -gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= -gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= -gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= @@ -1427,19 +1109,8 @@ google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz513 google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= -google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= -google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= -google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= -google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= -google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= -google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= -google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= -google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= -google.golang.org/api v0.59.0/go.mod h1:sT2boj7M9YJxZzgeZqXogmhfmRWDtPzT31xkieUbuZU= -google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= -google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= +google.golang.org/api v0.107.0 h1:I2SlFjD8ZWabaIFOfeEDg3pf0BHJDh6iYQ1ic3Yu/UU= +google.golang.org/api v0.107.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -1447,6 +1118,7 @@ google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7 google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= @@ -1470,7 +1142,6 @@ google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200324203455-a04cca1dde73/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= @@ -1484,45 +1155,17 @@ google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201119123407-9b1e624d6bc4/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210126160654-44e461bb6506/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= -google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= -google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= -google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210917145530-b395a37504d4/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211008145708-270636b82663/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211028162531-8db9c33dc351/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220519153652-3a47de7e79bd h1:e0TwkXOdbnH/1x5rc5MZ/VYyiZ4v+RdVfrGMqEwT68I= -google.golang.org/genproto v0.0.0-20220519153652-3a47de7e79bd/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220314164441-57ef72a4c106/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= +google.golang.org/genproto v0.0.0-20230202175211-008b39050e57 h1:vArvWooPH749rNHpBGgVl+U9B9dATjiEhJzcWGlovNs= +google.golang.org/genproto v0.0.0-20230202175211-008b39050e57/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -1545,21 +1188,10 @@ google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= -google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.48.0 h1:rQOsyJ/8+ufEDJd/Gdsz7HG220Mh9HAhFHRGnIjda0w= -google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= +google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/grpc v1.53.0 h1:LAv2ds7cmFV/XTS3XG1NneeENYrXGmorPxsBbptIjNc= +google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -1573,30 +1205,24 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= -google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= +gopkg.in/cheggaaa/pb.v1 v1.0.27/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= -gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.66.4 h1:SsAcf+mM7mRZo2nJNGt8mZCjG8ZRaNGMURJw7BsIST4= -gopkg.in/ini.v1 v1.66.4/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c= -gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200619000410-60c24ae608a6/go.mod h1:uAJfkITjFhyEEuUfm7bsmCZRbW5WRq8s9EY8HZ6hCns= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/urfave/cli.v1 v1.20.0/go.mod h1:vuBzUtMdQeixQj8LVd+/98pzhxNGQoyuPBlsXHOQNO0= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -1612,7 +1238,7 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= +gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -1623,10 +1249,12 @@ honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9 honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= nhooyr.io/websocket v1.8.6 h1:s+C3xAMLwGmlI31Nyn/eAehUlZPwfYZu2JXM621Q5/k= nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= +pgregory.net/rapid v0.5.5 h1:jkgx1TjbQPD/feRoK+S/mXw9e1uj6WilpHrXJowi6oA= +pgregory.net/rapid v0.5.5/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= -sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= +sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= +sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= diff --git a/go.work.example b/go.work.example new file mode 100644 index 00000000000..5481cb3c915 --- /dev/null +++ b/go.work.example @@ -0,0 +1,6 @@ +go 1.19 + +use ( + ./ + ./e2e +) diff --git a/internal/errors/errors.go b/internal/errors/errors.go new file mode 100644 index 00000000000..79b96856a87 --- /dev/null +++ b/internal/errors/errors.go @@ -0,0 +1,63 @@ +package errors + +import ( + errorsmod "cosmossdk.io/errors" + + "github.com/cosmos/ibc-go/v7/modules/core/exported" +) + +const codespace = exported.ModuleName + +var ( + // ErrInvalidSequence is used the sequence number (nonce) is incorrect + // for the signature. + ErrInvalidSequence = errorsmod.Register(codespace, 1, "invalid sequence") + + // ErrUnauthorized is used whenever a request without sufficient + // authorization is handled. + ErrUnauthorized = errorsmod.Register(codespace, 2, "unauthorized") + + // ErrInsufficientFunds is used when the account cannot pay requested amount. + ErrInsufficientFunds = errorsmod.Register(codespace, 3, "insufficient funds") + + // ErrUnknownRequest is used when the request body. + ErrUnknownRequest = errorsmod.Register(codespace, 4, "unknown request") + + // ErrInvalidAddress is used when an address is found to be invalid. + ErrInvalidAddress = errorsmod.Register(codespace, 5, "invalid address") + + // ErrInvalidCoins is used when sdk.Coins are invalid. + ErrInvalidCoins = errorsmod.Register(codespace, 6, "invalid coins") + + // ErrOutOfGas is used when there is not enough gas. + ErrOutOfGas = errorsmod.Register(codespace, 7, "out of gas") + + // ErrInvalidRequest defines an ABCI typed error where the request contains + // invalid data. + ErrInvalidRequest = errorsmod.Register(codespace, 8, "invalid request") + + // ErrInvalidHeight defines an error for an invalid height + ErrInvalidHeight = errorsmod.Register(codespace, 9, "invalid height") + + // ErrInvalidVersion defines a general error for an invalid version + ErrInvalidVersion = errorsmod.Register(codespace, 10, "invalid version") + + // ErrInvalidChainID defines an error when the chain-id is invalid. + ErrInvalidChainID = errorsmod.Register(codespace, 11, "invalid chain-id") + + // ErrInvalidType defines an error an invalid type. + ErrInvalidType = errorsmod.Register(codespace, 12, "invalid type") + + // ErrPackAny defines an error when packing a protobuf message to Any fails. + ErrPackAny = errorsmod.Register(codespace, 13, "failed packing protobuf message to Any") + + // ErrUnpackAny defines an error when unpacking a protobuf message from Any fails. + ErrUnpackAny = errorsmod.Register(codespace, 14, "failed unpacking protobuf message from Any") + + // ErrLogic defines an internal logic error, e.g. an invariant or assertion + // that is violated. It is a programmer error, not a user-facing error. + ErrLogic = errorsmod.Register(codespace, 15, "internal logic error") + + // ErrNotFound defines an error when requested entity doesn't exist in the state. + ErrNotFound = errorsmod.Register(codespace, 16, "not found") +) diff --git a/internal/logging/logging.go b/internal/logging/logging.go new file mode 100644 index 00000000000..03a326a7d45 --- /dev/null +++ b/internal/logging/logging.go @@ -0,0 +1,23 @@ +package logging + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// SdkEventsToLogArguments converts a given sdk.Events and returns a slice of strings that provide human +// readable values for the event attributes. +func SdkEventsToLogArguments(events sdk.Events) []string { + logArgs := []string{"events"} + for _, e := range events { + logArgs = append(logArgs, fmt.Sprintf("type=%s", e.Type)) + for _, attr := range e.Attributes { + if len(attr.Value) == 0 { + continue + } + logArgs = append(logArgs, fmt.Sprintf("%s=%s", attr.Key, attr.Value)) + } + } + return logArgs +} diff --git a/maintainership.png b/maintainership.png new file mode 100644 index 0000000000000000000000000000000000000000..4dd79a8f0ddb989afd89b2548dbbab1993e5f3b7 GIT binary patch literal 718328 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
      p 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�OhKRGfvUt_h^S?K56Oc{6D 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

      9xze1!wfbAg0dg;hkX#)Q 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'&td?+-Ia^NXoJyc-kCowEuBGtIx8 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≻y-;n9?$Xx1Imf7w*hr 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 literal 0 HcmV?d00001 diff --git a/modules/apps/27-interchain-accounts/client/cli/cli.go b/modules/apps/27-interchain-accounts/client/cli/cli.go index 92dbb031255..e36c3c8bd80 100644 --- a/modules/apps/27-interchain-accounts/client/cli/cli.go +++ b/modules/apps/27-interchain-accounts/client/cli/cli.go @@ -3,8 +3,8 @@ package cli import ( "github.com/spf13/cobra" - controllercli "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/controller/client/cli" - hostcli "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/host/client/cli" + controllercli "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/controller/client/cli" + hostcli "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/host/client/cli" ) // GetQueryCmd returns the query commands for the interchain-accounts submodule @@ -12,7 +12,7 @@ func GetQueryCmd() *cobra.Command { icaQueryCmd := &cobra.Command{ Use: "interchain-accounts", Aliases: []string{"ica"}, - Short: "interchain-accounts subcommands", + Short: "IBC interchain accounts query subcommands", DisableFlagParsing: true, SuggestionsMinimumDistance: 2, } @@ -24,3 +24,21 @@ func GetQueryCmd() *cobra.Command { return icaQueryCmd } + +// NewTxCmd returns the tx commands for the interchain-accounts submodule +func NewTxCmd() *cobra.Command { + icaTxCmd := &cobra.Command{ + Use: "interchain-accounts", + Aliases: []string{"ica"}, + Short: "IBC interchain accounts transaction subcommands", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + } + + icaTxCmd.AddCommand( + controllercli.NewTxCmd(), + hostcli.NewTxCmd(), + ) + + return icaTxCmd +} 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..a863649c901 100644 --- a/modules/apps/27-interchain-accounts/controller/client/cli/cli.go +++ b/modules/apps/27-interchain-accounts/controller/client/cli/cli.go @@ -2,20 +2,41 @@ package cli import ( "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client" ) // GetQueryCmd returns the query commands for the ICA controller submodule func GetQueryCmd() *cobra.Command { queryCmd := &cobra.Command{ Use: "controller", - Short: "interchain-accounts controller subcommands", + Short: "IBC interchain accounts controller query subcommands", DisableFlagParsing: true, SuggestionsMinimumDistance: 2, } queryCmd.AddCommand( + GetCmdQueryInterchainAccount(), GetCmdParams(), ) return queryCmd } + +// NewTxCmd creates and returns the tx command +func NewTxCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "controller", + Short: "IBC interchain accounts controller transaction subcommands", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + cmd.AddCommand( + newRegisterInterchainAccountCmd(), + newSendTxCmd(), + ) + + return cmd +} 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 b7636715c7d..ca71a96652a 100644 --- a/modules/apps/27-interchain-accounts/controller/client/cli/query.go +++ b/modules/apps/27-interchain-accounts/controller/client/cli/query.go @@ -8,9 +8,43 @@ import ( "github.com/cosmos/cosmos-sdk/version" "github.com/spf13/cobra" - "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/controller/types" + "github.com/cosmos/ibc-go/v7/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/client/cli/tx.go b/modules/apps/27-interchain-accounts/controller/client/cli/tx.go new file mode 100644 index 00000000000..44094f7b152 --- /dev/null +++ b/modules/apps/27-interchain-accounts/controller/client/cli/tx.go @@ -0,0 +1,109 @@ +package cli + +import ( + "fmt" + "os" + "strings" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/client/tx" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/spf13/cobra" + + "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/controller/types" + icatypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types" +) + +const ( + // The controller chain channel version + flagVersion = "version" + flagRelativePacketTimeout = "relative-packet-timeout" +) + +func newRegisterInterchainAccountCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "register [connection-id]", + Short: "Register an interchain account on the provided connection.", + Long: strings.TrimSpace(`Register an account on the counterparty chain via the +connection id from the source chain. Connection identifier should be for the source chain +and the interchain account will be created on the counterparty chain. Callers are expected to +provide the appropriate application version string via {version} flag. Generates a new +port identifier using the provided owner string, binds to the port identifier and claims +the associated capability.`), + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + connectionID := args[0] + owner := clientCtx.GetFromAddress().String() + version, err := cmd.Flags().GetString(flagVersion) + if err != nil { + return err + } + + msg := types.NewMsgRegisterInterchainAccount(connectionID, owner, version) + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + }, + } + + cmd.Flags().String(flagVersion, "", "Controller chain channel version") + flags.AddTxFlagsToCmd(cmd) + + return cmd +} + +func newSendTxCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "send-tx [connection-id] [path/to/packet_msg.json]", + Short: "Send an interchain account tx on the provided connection.", + Long: strings.TrimSpace(`Submits pre-built packet data containing messages to be executed on the host chain +and attempts to send the packet. Packet data is provided as json, file or string. An +appropriate relative timeoutTimestamp must be provided with flag {relative-packet-timeout}`), + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + cdc := codec.NewProtoCodec(clientCtx.InterfaceRegistry) + + connectionID := args[0] + owner := clientCtx.GetFromAddress().String() + + // attempt to unmarshal ica msg data argument + var icaMsgData icatypes.InterchainAccountPacketData + msgContentOrFileName := args[1] + if err := cdc.UnmarshalJSON([]byte(msgContentOrFileName), &icaMsgData); err != nil { + + // check for file path if JSON input is not provided + contents, err := os.ReadFile(msgContentOrFileName) + if err != nil { + return fmt.Errorf("neither JSON input nor path to .json file for packet data with messages were provided: %w", err) + } + + if err := cdc.UnmarshalJSON(contents, &icaMsgData); err != nil { + return fmt.Errorf("error unmarshalling packet data with messages file: %w", err) + } + } + + relativeTimeoutTimestamp, err := cmd.Flags().GetUint64(flagRelativePacketTimeout) + if err != nil { + return err + } + + msg := types.NewMsgSendTx(owner, connectionID, relativeTimeoutTimestamp, icaMsgData) + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + }, + } + + cmd.Flags().Uint64(flagRelativePacketTimeout, icatypes.DefaultRelativePacketTimeoutTimestamp, "Relative packet timeout in nanoseconds from now. Default is 10 minutes.") + flags.AddTxFlagsToCmd(cmd) + + return cmd +} diff --git a/modules/apps/27-interchain-accounts/controller/ibc_middleware.go b/modules/apps/27-interchain-accounts/controller/ibc_middleware.go index 53adf6a090e..56a0d529a8a 100644 --- a/modules/apps/27-interchain-accounts/controller/ibc_middleware.go +++ b/modules/apps/27-interchain-accounts/controller/ibc_middleware.go @@ -1,16 +1,19 @@ package controller import ( + errorsmod "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/controller/keeper" - "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" - channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" - porttypes "github.com/cosmos/ibc-go/v4/modules/core/05-port/types" - ibcexported "github.com/cosmos/ibc-go/v4/modules/core/exported" + ibcerrors "github.com/cosmos/ibc-go/v7/internal/errors" + "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/controller/keeper" + "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/controller/types" + icatypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + porttypes "github.com/cosmos/ibc-go/v7/modules/core/05-port/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + ibcexported "github.com/cosmos/ibc-go/v7/modules/core/exported" ) var _ porttypes.Middleware = &IBCMiddleware{} @@ -22,7 +25,7 @@ type IBCMiddleware struct { keeper keeper.Keeper } -// IBCMiddleware creates a new IBCMiddleware given the associated keeper and underlying application +// NewIBCMiddleware creates a new IBCMiddleware given the associated keeper and underlying application func NewIBCMiddleware(app porttypes.IBCModule, k keeper.Keeper) IBCMiddleware { return IBCMiddleware{ app: app, @@ -55,11 +58,17 @@ func (im IBCMiddleware) OnChanOpenInit( return "", err } + if err := im.keeper.ClaimCapability(ctx, chanCap, host.ChannelCapabilityPath(portID, channelID)); err != nil { + return "", err + } + // call underlying app's OnChanOpenInit callback with the passed in version // the version returned is discarded as the ica-auth module does not have permission to edit the version string. // ics27 will always return the version string containing the Metadata struct which is created during the `RegisterInterchainAccount` call. - if _, err := im.app.OnChanOpenInit(ctx, order, connectionHops, portID, channelID, chanCap, counterparty, version); err != nil { - return "", err + if im.app != nil && im.keeper.IsMiddlewareEnabled(ctx, portID, connectionHops[0]) { + if _, err := im.app.OnChanOpenInit(ctx, order, connectionHops, portID, channelID, nil, counterparty, version); err != nil { + return "", err + } } return version, nil @@ -76,7 +85,7 @@ func (im IBCMiddleware) OnChanOpenTry( counterparty channeltypes.Counterparty, counterpartyVersion string, ) (string, error) { - return "", sdkerrors.Wrap(icatypes.ErrInvalidChannelFlow, "channel handshake must be initiated by controller chain") + return "", errorsmod.Wrap(icatypes.ErrInvalidChannelFlow, "channel handshake must be initiated by controller chain") } // OnChanOpenAck implements the IBCMiddleware interface @@ -100,17 +109,26 @@ func (im IBCMiddleware) OnChanOpenAck( return err } + connectionID, err := im.keeper.GetConnectionID(ctx, portID, channelID) + if err != nil { + return err + } + // call underlying app's OnChanOpenAck callback with the counterparty app version. - return im.app.OnChanOpenAck(ctx, portID, channelID, counterpartyChannelID, counterpartyVersion) + if im.app != nil && im.keeper.IsMiddlewareEnabled(ctx, portID, connectionID) { + return im.app.OnChanOpenAck(ctx, portID, channelID, counterpartyChannelID, counterpartyVersion) + } + + return nil } -// OnChanOpenAck implements the IBCMiddleware interface +// OnChanOpenConfirm implements the IBCMiddleware interface func (im IBCMiddleware) OnChanOpenConfirm( ctx sdk.Context, portID, channelID string, ) error { - return sdkerrors.Wrap(icatypes.ErrInvalidChannelFlow, "channel handshake must be initiated by controller chain") + return errorsmod.Wrap(icatypes.ErrInvalidChannelFlow, "channel handshake must be initiated by controller chain") } // OnChanCloseInit implements the IBCMiddleware interface @@ -120,7 +138,7 @@ func (im IBCMiddleware) OnChanCloseInit( channelID string, ) error { // Disallow user-initiated channel closing for interchain account channels - return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "user cannot close channel") + return errorsmod.Wrap(ibcerrors.ErrInvalidRequest, "user cannot close channel") } // OnChanCloseConfirm implements the IBCMiddleware interface @@ -129,7 +147,20 @@ func (im IBCMiddleware) OnChanCloseConfirm( portID, channelID string, ) error { - return im.keeper.OnChanCloseConfirm(ctx, portID, channelID) + if err := im.keeper.OnChanCloseConfirm(ctx, portID, channelID); err != nil { + return err + } + + connectionID, err := im.keeper.GetConnectionID(ctx, portID, channelID) + if err != nil { + return err + } + + if im.app != nil && im.keeper.IsMiddlewareEnabled(ctx, portID, connectionID) { + return im.app.OnChanCloseConfirm(ctx, portID, channelID) + } + + return nil } // OnRecvPacket implements the IBCMiddleware interface @@ -138,7 +169,7 @@ func (im IBCMiddleware) OnRecvPacket( packet channeltypes.Packet, _ sdk.AccAddress, ) ibcexported.Acknowledgement { - err := sdkerrors.Wrapf(icatypes.ErrInvalidChannelFlow, "cannot receive packet on controller chain") + err := errorsmod.Wrapf(icatypes.ErrInvalidChannelFlow, "cannot receive packet on controller chain") ack := channeltypes.NewErrorAcknowledgement(err) keeper.EmitAcknowledgementEvent(ctx, packet, ack, err) return ack @@ -155,8 +186,17 @@ func (im IBCMiddleware) OnAcknowledgementPacket( return types.ErrControllerSubModuleDisabled } + connectionID, err := im.keeper.GetConnectionID(ctx, packet.GetSourcePort(), packet.GetSourceChannel()) + if err != nil { + return err + } + // call underlying app's OnAcknowledgementPacket callback. - return im.app.OnAcknowledgementPacket(ctx, packet, acknowledgement, relayer) + if im.app != nil && im.keeper.IsMiddlewareEnabled(ctx, packet.GetSourcePort(), connectionID) { + return im.app.OnAcknowledgementPacket(ctx, packet, acknowledgement, relayer) + } + + return nil } // OnTimeoutPacket implements the IBCMiddleware interface @@ -173,15 +213,28 @@ func (im IBCMiddleware) OnTimeoutPacket( return err } - return im.app.OnTimeoutPacket(ctx, packet, relayer) + connectionID, err := im.keeper.GetConnectionID(ctx, packet.GetSourcePort(), packet.GetSourceChannel()) + if err != nil { + return err + } + + if im.app != nil && im.keeper.IsMiddlewareEnabled(ctx, packet.GetSourcePort(), connectionID) { + return im.app.OnTimeoutPacket(ctx, packet, relayer) + } + + return nil } // SendPacket implements the ICS4 Wrapper interface func (im IBCMiddleware) SendPacket( ctx sdk.Context, chanCap *capabilitytypes.Capability, - packet ibcexported.PacketI, -) error { + sourcePort string, + sourceChannel string, + timeoutHeight clienttypes.Height, + timeoutTimestamp uint64, + data []byte, +) (uint64, error) { panic("SendPacket not supported for ICA controller module. Please use SendTx") } 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 b326c0bb71f..3826dea0c79 100644 --- a/modules/apps/27-interchain-accounts/controller/ibc_middleware_test.go +++ b/modules/apps/27-interchain-accounts/controller/ibc_middleware_test.go @@ -7,31 +7,26 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" "github.com/stretchr/testify/suite" - "github.com/tendermint/tendermint/crypto" - - "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" - fee "github.com/cosmos/ibc-go/v4/modules/apps/29-fee" - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/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" + + "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/controller" + controllerkeeper "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/controller/keeper" + "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/controller/types" + icatypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types" + fee "github.com/cosmos/ibc-go/v7/modules/apps/29-fee" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + ibctesting "github.com/cosmos/ibc-go/v7/testing" ) 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 = "cosmos17dtl0mjt3t77kpuhg2edqzjpszulwhgzuj9ljs" - // TestPortID defines a resuable port identifier for testing purposes + // TestPortID defines a reusable port identifier for testing purposes TestPortID, _ = icatypes.NewControllerPortID(TestOwnerAddress) - // TestVersion defines a resuable interchainaccounts version string for testing purposes + // TestVersion defines a reusable interchainaccounts version string for testing purposes TestVersion = string(icatypes.ModuleCdc.MustMarshalJSON(&icatypes.Metadata{ Version: icatypes.Version, ControllerConnectionId: ibctesting.FirstConnectionID, @@ -65,8 +60,8 @@ func (suite *InterchainAccountsTestSuite) SetupTest() { func NewICAPath(chainA, chainB *ibctesting.TestChain) *ibctesting.Path { path := ibctesting.NewPath(chainA, chainB) - path.EndpointA.ChannelConfig.PortID = icatypes.PortID - path.EndpointB.ChannelConfig.PortID = icatypes.PortID + path.EndpointA.ChannelConfig.PortID = icatypes.HostPortID + path.EndpointB.ChannelConfig.PortID = icatypes.HostPortID path.EndpointA.ChannelConfig.Order = channeltypes.ORDERED path.EndpointB.ChannelConfig.Order = channeltypes.ORDERED path.EndpointA.ChannelConfig.Version = TestVersion @@ -120,7 +115,11 @@ func SetupICAPath(path *ibctesting.Path, owner string) error { } func (suite *InterchainAccountsTestSuite) TestOnChanOpenInit() { - var channel *channeltypes.Channel + var ( + channel *channeltypes.Channel + isNilApp bool + path *ibctesting.Path + ) testCases := []struct { name string @@ -130,6 +129,20 @@ func (suite *InterchainAccountsTestSuite) TestOnChanOpenInit() { { "success", func() {}, true, }, + { + "ICA auth module does not claim channel capability", func() { + suite.chainA.GetSimApp().ICAAuthModule.IBCApp.OnChanOpenInit = func(ctx sdk.Context, order channeltypes.Order, connectionHops []string, + portID, channelID string, chanCap *capabilitytypes.Capability, + counterparty channeltypes.Counterparty, version string, + ) (string, error) { + if chanCap != nil { + return "", fmt.Errorf("channel capability should be nil") + } + + return version, nil + } + }, true, + }, { "ICA auth module modification of channel version is ignored", func() { // NOTE: explicitly modify the channel version via the auth module callback, @@ -162,6 +175,23 @@ func (suite *InterchainAccountsTestSuite) TestOnChanOpenInit() { } }, false, }, + { + "nil underlying app", func() { + isNilApp = true + }, true, + }, + { + "middleware disabled", func() { + suite.chainA.GetSimApp().ICAControllerKeeper.DeleteMiddlewareEnabled(suite.chainA.GetContext(), path.EndpointA.ChannelConfig.PortID, path.EndpointA.ConnectionID) + + suite.chainA.GetSimApp().ICAAuthModule.IBCApp.OnChanOpenInit = func(ctx sdk.Context, order channeltypes.Order, connectionHops []string, + portID, channelID string, chanCap *capabilitytypes.Capability, + counterparty channeltypes.Counterparty, version string, + ) (string, error) { + return "", fmt.Errorf("error should be unreachable") + } + }, true, + }, } for _, tc := range testCases { @@ -169,8 +199,9 @@ func (suite *InterchainAccountsTestSuite) TestOnChanOpenInit() { suite.Run(tc.name, func() { suite.SetupTest() // reset + isNilApp = false - path := NewICAPath(suite.chainA, suite.chainB) + path = NewICAPath(suite.chainA, suite.chainB) suite.coordinator.SetupConnections(path) // mock init interchain account @@ -178,11 +209,13 @@ func (suite *InterchainAccountsTestSuite) TestOnChanOpenInit() { suite.Require().NoError(err) portCap := suite.chainA.GetSimApp().IBCKeeper.PortKeeper.BindPort(suite.chainA.GetContext(), portID) - suite.chainA.GetSimApp().ICAControllerKeeper.ClaimCapability(suite.chainA.GetContext(), portCap, host.PortPath(portID)) + suite.chainA.GetSimApp().ICAControllerKeeper.ClaimCapability(suite.chainA.GetContext(), portCap, host.PortPath(portID)) //nolint:errcheck // checking this error isn't needed for the test path.EndpointA.ChannelConfig.PortID = portID path.EndpointA.ChannelID = ibctesting.FirstChannelID + suite.chainA.GetSimApp().ICAControllerKeeper.SetMiddlewareEnabled(suite.chainA.GetContext(), path.EndpointA.ChannelConfig.PortID, path.EndpointA.ConnectionID) + // default values counterparty := channeltypes.NewCounterparty(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID) channel = &channeltypes.Channel{ @@ -207,6 +240,10 @@ func (suite *InterchainAccountsTestSuite) TestOnChanOpenInit() { cbs, ok := suite.chainA.App.GetIBCKeeper().Router.GetRoute(module) suite.Require().True(ok) + if isNilApp { + cbs = controller.NewIBCMiddleware(nil, suite.chainA.GetSimApp().ICAControllerKeeper) + } + version, err := cbs.OnChanOpenInit(suite.chainA.GetContext(), channel.Ordering, channel.GetConnectionHops(), path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, chanCap, channel.Counterparty, channel.GetVersion(), ) @@ -239,12 +276,14 @@ func (suite *InterchainAccountsTestSuite) TestChanOpenTry() { err = RegisterInterchainAccount(path.EndpointB, TestOwnerAddress) suite.Require().NoError(err) - path.EndpointA.UpdateClient() + err = path.EndpointA.UpdateClient() + suite.Require().NoError(err) + channelKey := host.ChannelKey(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID) 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) @@ -271,7 +310,10 @@ func (suite *InterchainAccountsTestSuite) TestChanOpenTry() { } func (suite *InterchainAccountsTestSuite) TestOnChanOpenAck() { - var path *ibctesting.Path + var ( + path *ibctesting.Path + isNilApp bool + ) testCases := []struct { name string @@ -300,6 +342,22 @@ func (suite *InterchainAccountsTestSuite) TestOnChanOpenAck() { } }, false, }, + { + "nil underlying app", func() { + isNilApp = true + }, true, + }, + { + "middleware disabled", func() { + suite.chainA.GetSimApp().ICAControllerKeeper.DeleteMiddlewareEnabled(suite.chainA.GetContext(), path.EndpointA.ChannelConfig.PortID, path.EndpointA.ConnectionID) + + suite.chainA.GetSimApp().ICAAuthModule.IBCApp.OnChanOpenAck = func( + ctx sdk.Context, portID, channelID string, counterpartyChannelID string, counterpartyVersion string, + ) error { + return fmt.Errorf("error should be unreachable") + } + }, true, + }, } for _, tc := range testCases { @@ -307,6 +365,7 @@ func (suite *InterchainAccountsTestSuite) TestOnChanOpenAck() { suite.Run(tc.name, func() { suite.SetupTest() // reset + isNilApp = false path = NewICAPath(suite.chainA, suite.chainB) suite.coordinator.SetupConnections(path) @@ -327,6 +386,10 @@ func (suite *InterchainAccountsTestSuite) TestOnChanOpenAck() { err = cbs.OnChanOpenAck(suite.chainA.GetContext(), path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelID, path.EndpointB.ChannelConfig.Version) + if isNilApp { + cbs = controller.NewIBCMiddleware(nil, suite.chainA.GetSimApp().ICAControllerKeeper) + } + if tc.expPass { suite.Require().NoError(err) } else { @@ -359,7 +422,8 @@ func (suite *InterchainAccountsTestSuite) TestChanOpenConfirm() { // commit state changes so proof can be created suite.chainB.NextBlock() - path.EndpointA.UpdateClient() + err = path.EndpointA.UpdateClient() + suite.Require().NoError(err) // query proof from ChainB channelKey := host.ChannelKey(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID) @@ -407,7 +471,10 @@ func (suite *InterchainAccountsTestSuite) TestOnChanCloseInit() { } func (suite *InterchainAccountsTestSuite) TestOnChanCloseConfirm() { - var path *ibctesting.Path + var ( + path *ibctesting.Path + isNilApp bool + ) testCases := []struct { name string @@ -417,11 +484,17 @@ func (suite *InterchainAccountsTestSuite) TestOnChanCloseConfirm() { { "success", func() {}, true, }, + { + "nil underlying app", func() { + isNilApp = true + }, true, + }, } for _, tc := range testCases { suite.Run(tc.name, func() { suite.SetupTest() // reset + isNilApp = false path = NewICAPath(suite.chainA, suite.chainB) suite.coordinator.SetupConnections(path) @@ -436,6 +509,10 @@ func (suite *InterchainAccountsTestSuite) TestOnChanCloseConfirm() { cbs, ok := suite.chainA.App.GetIBCKeeper().Router.GetRoute(module) suite.Require().True(ok) + if isNilApp { + cbs = controller.NewIBCMiddleware(nil, suite.chainA.GetSimApp().ICAControllerKeeper) + } + err = cbs.OnChanCloseConfirm( suite.chainA.GetContext(), path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) @@ -490,14 +567,17 @@ 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()) }) } } func (suite *InterchainAccountsTestSuite) TestOnAcknowledgementPacket() { - var path *ibctesting.Path + var ( + path *ibctesting.Path + isNilApp bool + ) testCases := []struct { msg string @@ -523,11 +603,28 @@ func (suite *InterchainAccountsTestSuite) TestOnAcknowledgementPacket() { } }, false, }, + { + "nil underlying app", func() { + isNilApp = true + }, true, + }, + { + "middleware disabled", func() { + suite.chainA.GetSimApp().ICAControllerKeeper.DeleteMiddlewareEnabled(suite.chainA.GetContext(), path.EndpointA.ChannelConfig.PortID, path.EndpointA.ConnectionID) + + suite.chainA.GetSimApp().ICAAuthModule.IBCApp.OnAcknowledgementPacket = func( + ctx sdk.Context, packet channeltypes.Packet, acknowledgement []byte, relayer sdk.AccAddress, + ) error { + return fmt.Errorf("error should be unreachable") + } + }, true, + }, } for _, tc := range testCases { suite.Run(tc.msg, func() { suite.SetupTest() // reset + isNilApp = false path = NewICAPath(suite.chainA, suite.chainB) suite.coordinator.SetupConnections(path) @@ -554,6 +651,10 @@ func (suite *InterchainAccountsTestSuite) TestOnAcknowledgementPacket() { cbs, ok := suite.chainA.App.GetIBCKeeper().Router.GetRoute(module) suite.Require().True(ok) + if isNilApp { + cbs = controller.NewIBCMiddleware(nil, suite.chainA.GetSimApp().ICAControllerKeeper) + } + err = cbs.OnAcknowledgementPacket(suite.chainA.GetContext(), packet, []byte("ack"), nil) if tc.expPass { @@ -566,7 +667,10 @@ func (suite *InterchainAccountsTestSuite) TestOnAcknowledgementPacket() { } func (suite *InterchainAccountsTestSuite) TestOnTimeoutPacket() { - var path *ibctesting.Path + var ( + path *ibctesting.Path + isNilApp bool + ) testCases := []struct { msg string @@ -592,11 +696,28 @@ func (suite *InterchainAccountsTestSuite) TestOnTimeoutPacket() { } }, false, }, + { + "nil underlying app", func() { + isNilApp = true + }, true, + }, + { + "middleware disabled", func() { + suite.chainA.GetSimApp().ICAControllerKeeper.DeleteMiddlewareEnabled(suite.chainA.GetContext(), path.EndpointA.ChannelConfig.PortID, path.EndpointA.ConnectionID) + + suite.chainA.GetSimApp().ICAAuthModule.IBCApp.OnTimeoutPacket = func( + ctx sdk.Context, packet channeltypes.Packet, relayer sdk.AccAddress, + ) error { + return fmt.Errorf("error should be unreachable") + } + }, true, + }, } for _, tc := range testCases { suite.Run(tc.msg, func() { suite.SetupTest() // reset + isNilApp = false path = NewICAPath(suite.chainA, suite.chainB) suite.coordinator.SetupConnections(path) @@ -623,6 +744,10 @@ func (suite *InterchainAccountsTestSuite) TestOnTimeoutPacket() { cbs, ok := suite.chainA.App.GetIBCKeeper().Router.GetRoute(module) suite.Require().True(ok) + if isNilApp { + cbs = controller.NewIBCMiddleware(nil, suite.chainA.GetSimApp().ICAControllerKeeper) + } + err = cbs.OnTimeoutPacket(suite.chainA.GetContext(), packet, nil) if tc.expPass { @@ -719,3 +844,80 @@ func (suite *InterchainAccountsTestSuite) TestGetAppVersion() { suite.Require().True(found) suite.Require().Equal(path.EndpointA.ChannelConfig.Version, appVersion) } + +func (suite *InterchainAccountsTestSuite) TestInFlightHandshakeRespectsGoAPICaller() { + path := NewICAPath(suite.chainA, suite.chainB) + suite.coordinator.SetupConnections(path) + + // initiate a channel handshake such that channel.State == INIT + err := RegisterInterchainAccount(path.EndpointA, suite.chainA.SenderAccount.GetAddress().String()) + suite.Require().NoError(err) + + // attempt to start a second handshake via the controller msg server + msgServer := controllerkeeper.NewMsgServerImpl(&suite.chainA.GetSimApp().ICAControllerKeeper) + msgRegisterInterchainAccount := types.NewMsgRegisterInterchainAccount(path.EndpointA.ConnectionID, suite.chainA.SenderAccount.GetAddress().String(), TestVersion) + + res, err := msgServer.RegisterInterchainAccount(suite.chainA.GetContext(), msgRegisterInterchainAccount) + suite.Require().Error(err) + suite.Require().Nil(res) +} + +func (suite *InterchainAccountsTestSuite) TestInFlightHandshakeRespectsMsgServerCaller() { + path := NewICAPath(suite.chainA, suite.chainB) + suite.coordinator.SetupConnections(path) + + // initiate a channel handshake such that channel.State == INIT + msgServer := controllerkeeper.NewMsgServerImpl(&suite.chainA.GetSimApp().ICAControllerKeeper) + msgRegisterInterchainAccount := types.NewMsgRegisterInterchainAccount(path.EndpointA.ConnectionID, suite.chainA.SenderAccount.GetAddress().String(), TestVersion) + + res, err := msgServer.RegisterInterchainAccount(suite.chainA.GetContext(), msgRegisterInterchainAccount) + suite.Require().NotNil(res) + suite.Require().NoError(err) + + // attempt to start a second handshake via the legacy Go API + err = RegisterInterchainAccount(path.EndpointA, suite.chainA.SenderAccount.GetAddress().String()) + suite.Require().Error(err) +} + +func (suite *InterchainAccountsTestSuite) TestClosedChannelReopensWithMsgServer() { + path := NewICAPath(suite.chainA, suite.chainB) + suite.coordinator.SetupConnections(path) + + err := SetupICAPath(path, suite.chainA.SenderAccount.GetAddress().String()) + suite.Require().NoError(err) + + // set the channel state to closed + err = path.EndpointA.SetChannelState(channeltypes.CLOSED) + suite.Require().NoError(err) + err = path.EndpointB.SetChannelState(channeltypes.CLOSED) + suite.Require().NoError(err) + + // reset endpoint channel ids + path.EndpointA.ChannelID = "" + path.EndpointB.ChannelID = "" + + // fetch the next channel sequence before reinitiating the channel handshake + channelSeq := suite.chainA.GetSimApp().GetIBCKeeper().ChannelKeeper.GetNextChannelSequence(suite.chainA.GetContext()) + + // route a new MsgRegisterInterchainAccount in order to reopen the + msgServer := controllerkeeper.NewMsgServerImpl(&suite.chainA.GetSimApp().ICAControllerKeeper) + msgRegisterInterchainAccount := types.NewMsgRegisterInterchainAccount(path.EndpointA.ConnectionID, suite.chainA.SenderAccount.GetAddress().String(), path.EndpointA.ChannelConfig.Version) + + res, err := msgServer.RegisterInterchainAccount(suite.chainA.GetContext(), msgRegisterInterchainAccount) + suite.Require().NoError(err) + suite.Require().Equal(channeltypes.FormatChannelIdentifier(channelSeq), res.ChannelId) + + // assign the channel sequence to endpointA before generating proofs and initiating the TRY step + path.EndpointA.ChannelID = channeltypes.FormatChannelIdentifier(channelSeq) + + path.EndpointA.Chain.NextBlock() + + err = path.EndpointB.ChanOpenTry() + suite.Require().NoError(err) + + err = path.EndpointA.ChanOpenAck() + suite.Require().NoError(err) + + err = path.EndpointB.ChanOpenConfirm() + suite.Require().NoError(err) +} diff --git a/modules/apps/27-interchain-accounts/controller/keeper/account.go b/modules/apps/27-interchain-accounts/controller/keeper/account.go index e16f2d0874d..5044e50b962 100644 --- a/modules/apps/27-interchain-accounts/controller/keeper/account.go +++ b/modules/apps/27-interchain-accounts/controller/keeper/account.go @@ -1,12 +1,15 @@ package keeper import ( + errorsmod "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + authtypes "github.com/cosmos/cosmos-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" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" + ibcerrors "github.com/cosmos/ibc-go/v7/internal/errors" + "github.com/cosmos/ibc-go/v7/internal/logging" + icatypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" ) // RegisterInterchainAccount is the entry point to registering an interchain account: @@ -16,38 +19,72 @@ import ( // - A new MsgChannelOpenInit is routed through the MsgServiceRouter, executing the OnOpenChanInit callback stack as configured. // - An error is returned if the port identifier is already in use. Gaining access to interchain accounts whose channels // have closed cannot be done with this function. A regular MsgChannelOpenInit must be used. +// +// Deprecated: this is a legacy API that is only intended to function correctly in workflows where an underlying authentication application has been set. +// Calling this API will result in all packet callbacks being routed to the underlying application. + +// Please use MsgRegisterInterchainAccount for use cases which do not need to route to an underlying application. + +// Prior to v6.x.x of ibc-go, the controller module was only functional as middleware, with authentication performed +// by the underlying application. For a full summary of the changes in v6.x.x, please see ADR009. +// This API will be removed in later releases. func (k Keeper) RegisterInterchainAccount(ctx sdk.Context, connectionID, owner, version string) error { portID, err := icatypes.NewControllerPortID(owner) if err != nil { return err } + if k.IsMiddlewareDisabled(ctx, portID, connectionID) && !k.IsActiveChannelClosed(ctx, connectionID, portID) { + return errorsmod.Wrap(icatypes.ErrInvalidChannelFlow, "channel is already active or a handshake is in flight") + } + + k.SetMiddlewareEnabled(ctx, portID, connectionID) + + _, err = k.registerInterchainAccount(ctx, connectionID, portID, version) + if err != nil { + return err + } + + return nil +} + +// registerInterchainAccount registers an interchain account, returning the channel id of the MsgChannelOpenInitResponse +// and an error if one occurred. +func (k Keeper) registerInterchainAccount(ctx sdk.Context, connectionID, portID, version string) (string, error) { // if there is an active channel for this portID / connectionID return an error activeChannelID, found := k.GetOpenActiveChannel(ctx, connectionID, portID) if found { - return sdkerrors.Wrapf(icatypes.ErrActiveChannelAlreadySet, "existing active channel %s for portID %s on connection %s for owner %s", activeChannelID, portID, connectionID, owner) + return "", errorsmod.Wrapf(icatypes.ErrActiveChannelAlreadySet, "existing active channel %s for portID %s on connection %s", activeChannelID, portID, connectionID) } switch { case k.portKeeper.IsBound(ctx, portID) && !k.IsBound(ctx, portID): - return sdkerrors.Wrapf(icatypes.ErrPortAlreadyBound, "another module has claimed capability for and bound port with portID: %s", portID) + return "", errorsmod.Wrapf(icatypes.ErrPortAlreadyBound, "another module has claimed capability for and bound port with portID: %s", portID) case !k.portKeeper.IsBound(ctx, portID): cap := k.BindPort(ctx, portID) if err := k.ClaimCapability(ctx, cap, host.PortPath(portID)); err != nil { - return sdkerrors.Wrapf(err, "unable to bind to newly generated portID: %s", portID) + return "", errorsmod.Wrapf(err, "unable to bind to newly generated portID: %s", portID) } } - msg := channeltypes.NewMsgChannelOpenInit(portID, version, channeltypes.ORDERED, []string{connectionID}, icatypes.PortID, icatypes.ModuleName) + msg := channeltypes.NewMsgChannelOpenInit(portID, version, channeltypes.ORDERED, []string{connectionID}, icatypes.HostPortID, authtypes.NewModuleAddress(icatypes.ModuleName).String()) handler := k.msgRouter.Handler(msg) - res, err := handler(ctx, msg) if err != nil { - return err + return "", err } + events := res.GetEvents() + k.Logger(ctx).Debug("emitting interchain account registration events", logging.SdkEventsToLogArguments(events)) + // NOTE: The sdk msg handler creates a new EventManager, so events must be correctly propagated back to the current context - ctx.EventManager().EmitEvents(res.GetEvents()) + ctx.EventManager().EmitEvents(events) - return nil + firstMsgResponse := res.MsgResponses[0] + channelOpenInitResponse, ok := firstMsgResponse.GetCachedValue().(*channeltypes.MsgChannelOpenInitResponse) + if !ok { + return "", errorsmod.Wrapf(ibcerrors.ErrInvalidType, "failed to covert %T message response to %T", firstMsgResponse.GetCachedValue(), &channeltypes.MsgChannelOpenInitResponse{}) + } + + return channelOpenInitResponse.ChannelId, nil } diff --git a/modules/apps/27-interchain-accounts/controller/keeper/account_test.go b/modules/apps/27-interchain-accounts/controller/keeper/account_test.go index 5d8854eea59..33ec4d6448f 100644 --- a/modules/apps/27-interchain-accounts/controller/keeper/account_test.go +++ b/modules/apps/27-interchain-accounts/controller/keeper/account_test.go @@ -1,10 +1,10 @@ package keeper_test import ( - 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" + icatypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + ibctesting "github.com/cosmos/ibc-go/v7/testing" ) func (suite *KeeperTestSuite) TestRegisterInterchainAccount() { diff --git a/modules/apps/27-interchain-accounts/controller/keeper/events.go b/modules/apps/27-interchain-accounts/controller/keeper/events.go index 8381fe7bec7..d712409785a 100644 --- a/modules/apps/27-interchain-accounts/controller/keeper/events.go +++ b/modules/apps/27-interchain-accounts/controller/keeper/events.go @@ -5,8 +5,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" - icatypes "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/types" - "github.com/cosmos/ibc-go/v4/modules/core/exported" + icatypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types" + "github.com/cosmos/ibc-go/v7/modules/core/exported" ) // EmitAcknowledgementEvent emits an event signalling a successful or failed acknowledgement and including the error diff --git a/modules/apps/27-interchain-accounts/controller/keeper/genesis.go b/modules/apps/27-interchain-accounts/controller/keeper/genesis.go index 88ca396b93b..ea5cbd822cd 100644 --- a/modules/apps/27-interchain-accounts/controller/keeper/genesis.go +++ b/modules/apps/27-interchain-accounts/controller/keeper/genesis.go @@ -5,12 +5,12 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" - icatypes "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/types" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" + genesistypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/genesis/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" ) // InitGenesis initializes the interchain accounts controller application state from a provided genesis state -func InitGenesis(ctx sdk.Context, keeper Keeper, state icatypes.ControllerGenesisState) { +func InitGenesis(ctx sdk.Context, keeper Keeper, state genesistypes.ControllerGenesisState) { for _, portID := range state.Ports { if !keeper.IsBound(ctx, portID) { cap := keeper.BindPort(ctx, portID) @@ -22,6 +22,12 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, state icatypes.ControllerGenesi for _, ch := range state.ActiveChannels { keeper.SetActiveChannelID(ctx, ch.ConnectionId, ch.PortId, ch.ChannelId) + + if ch.IsMiddlewareEnabled { + keeper.SetMiddlewareEnabled(ctx, ch.PortId, ch.ConnectionId) + } else { + keeper.SetMiddlewareDisabled(ctx, ch.PortId, ch.ConnectionId) + } } for _, acc := range state.InterchainAccounts { @@ -32,8 +38,8 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, state icatypes.ControllerGenesi } // ExportGenesis returns the interchain accounts controller exported genesis -func ExportGenesis(ctx sdk.Context, keeper Keeper) icatypes.ControllerGenesisState { - return icatypes.NewControllerGenesisState( +func ExportGenesis(ctx sdk.Context, keeper Keeper) genesistypes.ControllerGenesisState { + return genesistypes.NewControllerGenesisState( keeper.GetAllActiveChannels(ctx), keeper.GetAllInterchainAccounts(ctx), keeper.GetAllPorts(ctx), 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..a44ea5dc3d9 100644 --- a/modules/apps/27-interchain-accounts/controller/keeper/genesis_test.go +++ b/modules/apps/27-interchain-accounts/controller/keeper/genesis_test.go @@ -1,28 +1,37 @@ package keeper_test import ( - "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/controller/keeper" - "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" - ibctesting "github.com/cosmos/ibc-go/v4/testing" + "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/controller/keeper" + "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/controller/types" + genesistypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/genesis/types" + icatypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types" + ibctesting "github.com/cosmos/ibc-go/v7/testing" ) func (suite *KeeperTestSuite) TestInitGenesis() { suite.SetupTest() - genesisState := icatypes.ControllerGenesisState{ - ActiveChannels: []icatypes.ActiveChannel{ + interchainAccAddr := icatypes.GenerateAddress(suite.chainB.GetContext(), ibctesting.FirstConnectionID, TestPortID) + genesisState := genesistypes.ControllerGenesisState{ + ActiveChannels: []genesistypes.ActiveChannel{ { - ConnectionId: ibctesting.FirstConnectionID, - PortId: TestPortID, - ChannelId: ibctesting.FirstChannelID, + ConnectionId: ibctesting.FirstConnectionID, + PortId: TestPortID, + ChannelId: ibctesting.FirstChannelID, + IsMiddlewareEnabled: true, + }, + { + ConnectionId: "connection-1", + PortId: "test-port-1", + ChannelId: "channel-1", + IsMiddlewareEnabled: false, }, }, - InterchainAccounts: []icatypes.RegisteredInterchainAccount{ + InterchainAccounts: []genesistypes.RegisteredInterchainAccount{ { ConnectionId: ibctesting.FirstConnectionID, PortId: TestPortID, - AccountAddress: TestAccAddress.String(), + AccountAddress: interchainAccAddr.String(), }, }, Ports: []string{TestPortID}, @@ -34,9 +43,15 @@ func (suite *KeeperTestSuite) TestInitGenesis() { suite.Require().True(found) suite.Require().Equal(ibctesting.FirstChannelID, channelID) + isMiddlewareEnabled := suite.chainA.GetSimApp().ICAControllerKeeper.IsMiddlewareEnabled(suite.chainA.GetContext(), TestPortID, ibctesting.FirstConnectionID) + suite.Require().True(isMiddlewareEnabled) + + isMiddlewareDisabled := suite.chainA.GetSimApp().ICAControllerKeeper.IsMiddlewareDisabled(suite.chainA.GetContext(), "test-port-1", "connection-1") + suite.Require().True(isMiddlewareDisabled) + 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 +67,16 @@ 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().True(genesisState.ActiveChannels[0].IsMiddlewareEnabled) - 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 e23edc5017e..a00b4854d59 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/cosmos/cosmos-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" + "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/controller/types" + icatypes "github.com/cosmos/ibc-go/v7/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 ba54ce876bc..f1f6355f272 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 @@ -3,9 +3,78 @@ package keeper_test import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/controller/types" + "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/controller/types" + ibctesting "github.com/cosmos/ibc-go/v7/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.go b/modules/apps/27-interchain-accounts/controller/keeper/handshake.go index 337610f4a3b..916f32382f2 100644 --- a/modules/apps/27-interchain-accounts/controller/keeper/handshake.go +++ b/modules/apps/27-interchain-accounts/controller/keeper/handshake.go @@ -4,12 +4,12 @@ import ( "fmt" "strings" + errorsmod "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/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" + icatypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" ) // OnChanOpenInit performs basic validation of channel initialization. @@ -30,15 +30,15 @@ func (k Keeper) OnChanOpenInit( version string, ) (string, error) { if order != channeltypes.ORDERED { - return "", sdkerrors.Wrapf(channeltypes.ErrInvalidChannelOrdering, "expected %s channel, got %s", channeltypes.ORDERED, order) + return "", errorsmod.Wrapf(channeltypes.ErrInvalidChannelOrdering, "expected %s channel, got %s", channeltypes.ORDERED, order) } - if !strings.HasPrefix(portID, icatypes.PortPrefix) { - return "", sdkerrors.Wrapf(icatypes.ErrInvalidControllerPort, "expected %s{owner-account-address}, got %s", icatypes.PortPrefix, portID) + if !strings.HasPrefix(portID, icatypes.ControllerPortPrefix) { + return "", errorsmod.Wrapf(icatypes.ErrInvalidControllerPort, "expected %s{owner-account-address}, got %s", icatypes.ControllerPortPrefix, portID) } - if counterparty.PortId != icatypes.PortID { - return "", sdkerrors.Wrapf(icatypes.ErrInvalidHostPort, "expected %s, got %s", icatypes.PortID, counterparty.PortId) + if counterparty.PortId != icatypes.HostPortID { + return "", errorsmod.Wrapf(icatypes.ErrInvalidHostPort, "expected %s, got %s", icatypes.HostPortID, counterparty.PortId) } var metadata icatypes.Metadata @@ -51,7 +51,7 @@ func (k Keeper) OnChanOpenInit( metadata = icatypes.NewDefaultMetadata(connectionHops[0], connection.GetCounterparty().GetConnectionID()) } else { if err := icatypes.ModuleCdc.UnmarshalJSON([]byte(version), &metadata); err != nil { - return "", sdkerrors.Wrapf(icatypes.ErrUnknownDataType, "cannot unmarshal ICS-27 interchain accounts metadata") + return "", errorsmod.Wrapf(icatypes.ErrUnknownDataType, "cannot unmarshal ICS-27 interchain accounts metadata") } } @@ -67,11 +67,16 @@ func (k Keeper) OnChanOpenInit( } if channel.State == channeltypes.OPEN { - return "", sdkerrors.Wrapf(icatypes.ErrActiveChannelAlreadySet, "existing active channel %s for portID %s is already OPEN", activeChannelID, portID) + return "", errorsmod.Wrapf(icatypes.ErrActiveChannelAlreadySet, "existing active channel %s for portID %s is already OPEN", activeChannelID, portID) } - if !icatypes.IsPreviousMetadataEqual(channel.Version, metadata) { - return "", sdkerrors.Wrap(icatypes.ErrInvalidVersion, "previous active channel metadata does not match provided version") + appVersion, found := k.GetAppVersion(ctx, portID, activeChannelID) + if !found { + panic(fmt.Sprintf("active channel mapping set for %s, but channel does not exist in channel store", activeChannelID)) + } + + if !icatypes.IsPreviousMetadataEqual(appVersion, metadata) { + return "", errorsmod.Wrap(icatypes.ErrInvalidVersion, "previous active channel metadata does not match provided version") } } @@ -86,26 +91,26 @@ func (k Keeper) OnChanOpenAck( channelID string, counterpartyVersion string, ) error { - if portID == icatypes.PortID { - return sdkerrors.Wrapf(icatypes.ErrInvalidControllerPort, "portID cannot be host chain port ID: %s", icatypes.PortID) + if portID == icatypes.HostPortID { + return errorsmod.Wrapf(icatypes.ErrInvalidControllerPort, "portID cannot be host chain port ID: %s", icatypes.HostPortID) } - if !strings.HasPrefix(portID, icatypes.PortPrefix) { - return sdkerrors.Wrapf(icatypes.ErrInvalidControllerPort, "expected %s{owner-account-address}, got %s", icatypes.PortPrefix, portID) + if !strings.HasPrefix(portID, icatypes.ControllerPortPrefix) { + return errorsmod.Wrapf(icatypes.ErrInvalidControllerPort, "expected %s{owner-account-address}, got %s", icatypes.ControllerPortPrefix, 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") + return errorsmod.Wrapf(icatypes.ErrUnknownDataType, "cannot unmarshal ICS-27 interchain accounts metadata") } if activeChannelID, found := k.GetOpenActiveChannel(ctx, metadata.ControllerConnectionId, portID); found { - return sdkerrors.Wrapf(icatypes.ErrActiveChannelAlreadySet, "existing active channel %s for portID %s", activeChannelID, portID) + return errorsmod.Wrapf(icatypes.ErrActiveChannelAlreadySet, "existing active channel %s for portID %s", activeChannelID, portID) } channel, found := k.channelKeeper.GetChannel(ctx, portID, channelID) if !found { - return sdkerrors.Wrapf(channeltypes.ErrChannelNotFound, "failed to retrieve channel %s on port %s", channelID, portID) + return errorsmod.Wrapf(channeltypes.ErrChannelNotFound, "failed to retrieve channel %s on port %s", channelID, portID) } if err := icatypes.ValidateControllerMetadata(ctx, k.channelKeeper, channel.ConnectionHops, metadata); err != nil { @@ -113,7 +118,7 @@ func (k Keeper) OnChanOpenAck( } if strings.TrimSpace(metadata.Address) == "" { - return sdkerrors.Wrap(icatypes.ErrInvalidAccountAddress, "interchain account address cannot be empty") + return errorsmod.Wrap(icatypes.ErrInvalidAccountAddress, "interchain account address cannot be empty") } k.SetActiveChannelID(ctx, metadata.ControllerConnectionId, portID, channelID) 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 87338812aba..79df5aadad6 100644 --- a/modules/apps/27-interchain-accounts/controller/keeper/handshake_test.go +++ b/modules/apps/27-interchain-accounts/controller/keeper/handshake_test.go @@ -3,10 +3,10 @@ package keeper_test import ( capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/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" + icatypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + ibctesting "github.com/cosmos/ibc-go/v7/testing" ) func (suite *KeeperTestSuite) TestOnChanOpenInit() { @@ -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.SetChannelState(channeltypes.CLOSED) + suite.Require().NoError(err) + + err = path.EndpointB.SetChannelState(channeltypes.CLOSED) + 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, }, @@ -91,7 +103,7 @@ func (suite *KeeperTestSuite) TestOnChanOpenInit() { { "invalid port ID", func() { - path.EndpointA.ChannelConfig.PortID = "invalid-port-id" + path.EndpointA.ChannelConfig.PortID = "invalid-port-id" //nolint:goconst }, false, }, @@ -225,7 +237,7 @@ func (suite *KeeperTestSuite) TestOnChanOpenInit() { suite.Require().NoError(err) portCap := suite.chainA.GetSimApp().IBCKeeper.PortKeeper.BindPort(suite.chainA.GetContext(), portID) - suite.chainA.GetSimApp().ICAControllerKeeper.ClaimCapability(suite.chainA.GetContext(), portCap, host.PortPath(portID)) + suite.chainA.GetSimApp().ICAControllerKeeper.ClaimCapability(suite.chainA.GetContext(), portCap, host.PortPath(portID)) //nolint:errcheck // this error check isn't needed for tests path.EndpointA.ChannelConfig.PortID = portID // default values @@ -278,7 +290,7 @@ func (suite *KeeperTestSuite) TestOnChanOpenAck() { { "invalid port ID - host chain", func() { - path.EndpointA.ChannelConfig.PortID = icatypes.PortID + path.EndpointA.ChannelConfig.PortID = icatypes.HostPortID }, 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.go b/modules/apps/27-interchain-accounts/controller/keeper/keeper.go index 3ecdb169197..061a20a233c 100644 --- a/modules/apps/27-interchain-accounts/controller/keeper/keeper.go +++ b/modules/apps/27-interchain-accounts/controller/keeper/keeper.go @@ -1,43 +1,47 @@ package keeper import ( + "bytes" "fmt" "strings" - baseapp "github.com/cosmos/cosmos-sdk/baseapp" + errorsmod "cosmossdk.io/errors" "github.com/cosmos/cosmos-sdk/codec" + storetypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" - capabilitykeeper "github.com/cosmos/cosmos-sdk/x/capability/keeper" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" "github.com/tendermint/tendermint/libs/log" - "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" - channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" + "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/controller/types" + genesistypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/genesis/types" + icatypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + porttypes "github.com/cosmos/ibc-go/v7/modules/core/05-port/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + "github.com/cosmos/ibc-go/v7/modules/core/exported" ) // Keeper defines the IBC interchain accounts controller keeper type Keeper struct { - storeKey sdk.StoreKey + storeKey storetypes.StoreKey cdc codec.BinaryCodec paramSpace paramtypes.Subspace - ics4Wrapper icatypes.ICS4Wrapper + ics4Wrapper porttypes.ICS4Wrapper channelKeeper icatypes.ChannelKeeper portKeeper icatypes.PortKeeper - scopedKeeper capabilitykeeper.ScopedKeeper + scopedKeeper exported.ScopedKeeper - msgRouter *baseapp.MsgServiceRouter + msgRouter icatypes.MessageRouter } // NewKeeper creates a new interchain accounts controller Keeper instance func NewKeeper( - cdc codec.BinaryCodec, key sdk.StoreKey, paramSpace paramtypes.Subspace, - ics4Wrapper icatypes.ICS4Wrapper, channelKeeper icatypes.ChannelKeeper, portKeeper icatypes.PortKeeper, - scopedKeeper capabilitykeeper.ScopedKeeper, msgRouter *baseapp.MsgServiceRouter, + cdc codec.BinaryCodec, key storetypes.StoreKey, paramSpace paramtypes.Subspace, + ics4Wrapper porttypes.ICS4Wrapper, channelKeeper icatypes.ChannelKeeper, portKeeper icatypes.PortKeeper, + scopedKeeper exported.ScopedKeeper, msgRouter icatypes.MessageRouter, ) Keeper { // set KeyTable if it has not already been set if !paramSpace.HasKeyTable() { @@ -58,14 +62,23 @@ func NewKeeper( // Logger returns the application logger, scoped to the associated module func (k Keeper) Logger(ctx sdk.Context) log.Logger { - return ctx.Logger().With("module", fmt.Sprintf("x/%s-%s", host.ModuleName, icatypes.ModuleName)) + return ctx.Logger().With("module", fmt.Sprintf("x/%s-%s", exported.ModuleName, icatypes.ModuleName)) +} + +// GetConnectionID returns the connection id for the given port and channelIDs. +func (k Keeper) GetConnectionID(ctx sdk.Context, portID, channelID string) (string, error) { + channel, found := k.channelKeeper.GetChannel(ctx, portID, channelID) + if !found { + return "", errorsmod.Wrapf(channeltypes.ErrChannelNotFound, "port ID (%s) channel ID (%s)", portID, channelID) + } + return channel.ConnectionHops[0], nil } // GetAllPorts returns all ports to which the interchain accounts controller module is bound. Used in ExportGenesis func (k Keeper) GetAllPorts(ctx sdk.Context) []string { store := ctx.KVStore(k.storeKey) iterator := sdk.KVStorePrefixIterator(store, []byte(icatypes.PortKeyPrefix)) - defer iterator.Close() + defer sdk.LogDeferred(ctx.Logger(), func() error { return iterator.Close() }) var ports []string for ; iterator.Valid(); iterator.Next() { @@ -134,20 +147,36 @@ func (k Keeper) GetOpenActiveChannel(ctx sdk.Context, connectionID, portID strin return "", false } +// IsActiveChannelClosed retrieves the active channel from the store and returns true if the channel state is CLOSED, otherwise false +func (k Keeper) IsActiveChannelClosed(ctx sdk.Context, connectionID, portID string) bool { + channelID, found := k.GetActiveChannelID(ctx, connectionID, portID) + if !found { + return false + } + + channel, found := k.channelKeeper.GetChannel(ctx, portID, channelID) + return found && channel.State == channeltypes.CLOSED +} + // GetAllActiveChannels returns a list of all active interchain accounts controller channels and their associated connection and port identifiers -func (k Keeper) GetAllActiveChannels(ctx sdk.Context) []icatypes.ActiveChannel { +func (k Keeper) GetAllActiveChannels(ctx sdk.Context) []genesistypes.ActiveChannel { store := ctx.KVStore(k.storeKey) iterator := sdk.KVStorePrefixIterator(store, []byte(icatypes.ActiveChannelKeyPrefix)) - defer iterator.Close() + defer sdk.LogDeferred(ctx.Logger(), func() error { return iterator.Close() }) - var activeChannels []icatypes.ActiveChannel + var activeChannels []genesistypes.ActiveChannel for ; iterator.Valid(); iterator.Next() { keySplit := strings.Split(string(iterator.Key()), "/") - ch := icatypes.ActiveChannel{ - ConnectionId: keySplit[2], - PortId: keySplit[1], - ChannelId: string(iterator.Value()), + portID := keySplit[1] + connectionID := keySplit[2] + channelID := string(iterator.Value()) + + ch := genesistypes.ActiveChannel{ + ConnectionId: connectionID, + PortId: portID, + ChannelId: channelID, + IsMiddlewareEnabled: k.IsMiddlewareEnabled(ctx, portID, connectionID), } activeChannels = append(activeChannels, ch) @@ -181,15 +210,15 @@ func (k Keeper) GetInterchainAccountAddress(ctx sdk.Context, connectionID, portI } // GetAllInterchainAccounts returns a list of all registered interchain account addresses and their associated connection and controller port identifiers -func (k Keeper) GetAllInterchainAccounts(ctx sdk.Context) []icatypes.RegisteredInterchainAccount { +func (k Keeper) GetAllInterchainAccounts(ctx sdk.Context) []genesistypes.RegisteredInterchainAccount { store := ctx.KVStore(k.storeKey) iterator := sdk.KVStorePrefixIterator(store, []byte(icatypes.OwnerKeyPrefix)) - var interchainAccounts []icatypes.RegisteredInterchainAccount + var interchainAccounts []genesistypes.RegisteredInterchainAccount for ; iterator.Valid(); iterator.Next() { keySplit := strings.Split(string(iterator.Key()), "/") - acc := icatypes.RegisteredInterchainAccount{ + acc := genesistypes.RegisteredInterchainAccount{ ConnectionId: keySplit[2], PortId: keySplit[1], AccountAddress: string(iterator.Value()), @@ -206,3 +235,33 @@ func (k Keeper) SetInterchainAccountAddress(ctx sdk.Context, connectionID, portI store := ctx.KVStore(k.storeKey) store.Set(icatypes.KeyOwnerAccount(portID, connectionID), []byte(address)) } + +// IsMiddlewareEnabled returns true if the underlying application callbacks are enabled for given port and connection identifier pair, otherwise false +func (k Keeper) IsMiddlewareEnabled(ctx sdk.Context, portID, connectionID string) bool { + store := ctx.KVStore(k.storeKey) + return bytes.Equal(icatypes.MiddlewareEnabled, store.Get(icatypes.KeyIsMiddlewareEnabled(portID, connectionID))) +} + +// IsMiddlewareDisabled returns true if the underlying application callbacks are disabled for the given port and connection identifier pair, otherwise false +func (k Keeper) IsMiddlewareDisabled(ctx sdk.Context, portID, connectionID string) bool { + store := ctx.KVStore(k.storeKey) + return bytes.Equal(icatypes.MiddlewareDisabled, store.Get(icatypes.KeyIsMiddlewareEnabled(portID, connectionID))) +} + +// SetMiddlewareEnabled stores a flag to indicate that the underlying application callbacks should be enabled for the given port and connection identifier pair +func (k Keeper) SetMiddlewareEnabled(ctx sdk.Context, portID, connectionID string) { + store := ctx.KVStore(k.storeKey) + store.Set(icatypes.KeyIsMiddlewareEnabled(portID, connectionID), icatypes.MiddlewareEnabled) +} + +// SetMiddlewareDisabled stores a flag to indicate that the underlying application callbacks should be disabled for the given port and connection identifier pair +func (k Keeper) SetMiddlewareDisabled(ctx sdk.Context, portID, connectionID string) { + store := ctx.KVStore(k.storeKey) + store.Set(icatypes.KeyIsMiddlewareEnabled(portID, connectionID), icatypes.MiddlewareDisabled) +} + +// DeleteMiddlewareEnabled deletes the middleware enabled flag stored in state +func (k Keeper) DeleteMiddlewareEnabled(ctx sdk.Context, portID, connectionID string) { + store := ctx.KVStore(k.storeKey) + store.Delete(icatypes.KeyIsMiddlewareEnabled(portID, connectionID)) +} 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 602cb69426f..0583912165b 100644 --- a/modules/apps/27-interchain-accounts/controller/keeper/keeper_test.go +++ b/modules/apps/27-interchain-accounts/controller/keeper/keeper_test.go @@ -3,29 +3,22 @@ package keeper_test import ( "testing" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/suite" - "github.com/tendermint/tendermint/crypto" - 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" - ibctesting "github.com/cosmos/ibc-go/v4/testing" + genesistypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/genesis/types" + icatypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + ibctesting "github.com/cosmos/ibc-go/v7/testing" ) 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 = "cosmos17dtl0mjt3t77kpuhg2edqzjpszulwhgzuj9ljs" - // TestPortID defines a resuable port identifier for testing purposes + // TestPortID defines a reusable port identifier for testing purposes TestPortID, _ = icatypes.NewControllerPortID(TestOwnerAddress) - // TestVersion defines a resuable interchainaccounts version string for testing purposes + // TestVersion defines a reusable interchainaccounts version string for testing purposes TestVersion = string(icatypes.ModuleCdc.MustMarshalJSON(&icatypes.Metadata{ Version: icatypes.Version, ControllerConnectionId: ibctesting.FirstConnectionID, @@ -55,8 +48,8 @@ func (suite *KeeperTestSuite) SetupTest() { func NewICAPath(chainA, chainB *ibctesting.TestChain) *ibctesting.Path { path := ibctesting.NewPath(chainA, chainB) - path.EndpointA.ChannelConfig.PortID = icatypes.PortID - path.EndpointB.ChannelConfig.PortID = icatypes.PortID + path.EndpointA.ChannelConfig.PortID = icatypes.HostPortID + path.EndpointB.ChannelConfig.PortID = icatypes.HostPortID path.EndpointA.ChannelConfig.Order = channeltypes.ORDERED path.EndpointB.ChannelConfig.Order = channeltypes.ORDERED path.EndpointA.ChannelConfig.Version = TestVersion @@ -152,11 +145,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) @@ -165,8 +157,8 @@ func (suite *KeeperTestSuite) TestGetInterchainAccountAddress() { func (suite *KeeperTestSuite) TestGetAllActiveChannels() { var ( - expectedChannelID string = "test-channel" - expectedPortID string = "test-port" + expectedChannelID = "test-channel" + expectedPortID = "test-port" ) suite.SetupTest() @@ -179,16 +171,18 @@ func (suite *KeeperTestSuite) TestGetAllActiveChannels() { suite.chainA.GetSimApp().ICAControllerKeeper.SetActiveChannelID(suite.chainA.GetContext(), ibctesting.FirstConnectionID, expectedPortID, expectedChannelID) - expectedChannels := []icatypes.ActiveChannel{ + expectedChannels := []genesistypes.ActiveChannel{ { - ConnectionId: ibctesting.FirstConnectionID, - PortId: TestPortID, - ChannelId: path.EndpointA.ChannelID, + ConnectionId: ibctesting.FirstConnectionID, + PortId: TestPortID, + ChannelId: path.EndpointA.ChannelID, + IsMiddlewareEnabled: true, }, { - ConnectionId: ibctesting.FirstConnectionID, - PortId: expectedPortID, - ChannelId: expectedChannelID, + ConnectionId: ibctesting.FirstConnectionID, + PortId: expectedPortID, + ChannelId: expectedChannelID, + IsMiddlewareEnabled: false, }, } @@ -199,8 +193,8 @@ func (suite *KeeperTestSuite) TestGetAllActiveChannels() { func (suite *KeeperTestSuite) TestGetAllInterchainAccounts() { var ( - expectedAccAddr string = "test-acc-addr" - expectedPortID string = "test-port" + expectedAccAddr = "test-acc-addr" + expectedPortID = "test-port" ) suite.SetupTest() @@ -211,13 +205,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{ + expectedAccounts := []genesistypes.RegisteredInterchainAccount{ { ConnectionId: ibctesting.FirstConnectionID, PortId: TestPortID, - AccountAddress: TestAccAddress.String(), + AccountAddress: interchainAccAddr, }, { ConnectionId: ibctesting.FirstConnectionID, @@ -248,8 +245,8 @@ func (suite *KeeperTestSuite) TestIsActiveChannel() { func (suite *KeeperTestSuite) TestSetInterchainAccountAddress() { var ( - expectedAccAddr string = "test-acc-addr" - expectedPortID string = "test-port" + expectedAccAddr = "test-acc-addr" + expectedPortID = "test-port" ) suite.chainA.GetSimApp().ICAControllerKeeper.SetInterchainAccountAddress(suite.chainA.GetContext(), ibctesting.FirstConnectionID, expectedPortID, expectedAccAddr) diff --git a/modules/apps/27-interchain-accounts/controller/keeper/migrations.go b/modules/apps/27-interchain-accounts/controller/keeper/migrations.go new file mode 100644 index 00000000000..92a3a21d8e3 --- /dev/null +++ b/modules/apps/27-interchain-accounts/controller/keeper/migrations.go @@ -0,0 +1,50 @@ +package keeper + +import ( + "fmt" + + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" + + controllertypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/controller/types" + icatypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" +) + +// 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} +} + +// AssertChannelCapabilityMigrations checks that all channel capabilities generated using the interchain accounts controller port prefix +// are owned by the controller submodule and ibc. +func (m Migrator) AssertChannelCapabilityMigrations(ctx sdk.Context) error { + if m.keeper != nil { + logger := m.keeper.Logger(ctx) + filteredChannels := m.keeper.channelKeeper.GetAllChannelsWithPortPrefix(ctx, icatypes.ControllerPortPrefix) + for _, ch := range filteredChannels { + name := host.ChannelCapabilityPath(ch.PortId, ch.ChannelId) + capability, found := m.keeper.scopedKeeper.GetCapability(ctx, name) + if !found { + logger.Error(fmt.Sprintf("failed to find capability: %s", name)) + return errorsmod.Wrapf(capabilitytypes.ErrCapabilityNotFound, "failed to find capability: %s", name) + } + + isAuthenticated := m.keeper.scopedKeeper.AuthenticateCapability(ctx, capability, name) + if !isAuthenticated { + logger.Error(fmt.Sprintf("expected capability owner: %s", controllertypes.SubModuleName)) + return errorsmod.Wrapf(capabilitytypes.ErrCapabilityNotOwned, "expected capability owner: %s", controllertypes.SubModuleName) + } + + m.keeper.SetMiddlewareEnabled(ctx, ch.PortId, ch.ConnectionHops[0]) + logger.Info("successfully migrated channel capability", "name", name) + } + } + return nil +} diff --git a/modules/apps/27-interchain-accounts/controller/keeper/migrations_test.go b/modules/apps/27-interchain-accounts/controller/keeper/migrations_test.go new file mode 100644 index 00000000000..00ef1fc8279 --- /dev/null +++ b/modules/apps/27-interchain-accounts/controller/keeper/migrations_test.go @@ -0,0 +1,75 @@ +package keeper_test + +import ( + "fmt" + + "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/controller/keeper" + icatypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + ibctesting "github.com/cosmos/ibc-go/v7/testing" +) + +func (suite *KeeperTestSuite) TestAssertChannelCapabilityMigrations() { + testCases := []struct { + name string + malleate func() + expPass bool + }{ + { + "success", + func() {}, + true, + }, + { + "channel with different port is filtered out", + func() { + portIDWithOutPrefix := ibctesting.MockPort + suite.chainA.GetSimApp().IBCKeeper.ChannelKeeper.SetChannel(suite.chainA.GetContext(), portIDWithOutPrefix, ibctesting.FirstChannelID, channeltypes.Channel{ + ConnectionHops: []string{ibctesting.FirstConnectionID}, + }) + }, + true, + }, + { + "capability not found", + func() { + portIDWithPrefix := fmt.Sprintf("%s%s", icatypes.ControllerPortPrefix, "port-without-capability") + suite.chainA.GetSimApp().IBCKeeper.ChannelKeeper.SetChannel(suite.chainA.GetContext(), portIDWithPrefix, ibctesting.FirstChannelID, channeltypes.Channel{ + ConnectionHops: []string{ibctesting.FirstConnectionID}, + }) + }, + 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) + + tc.malleate() + + migrator := keeper.NewMigrator(&suite.chainA.GetSimApp().ICAControllerKeeper) + err = migrator.AssertChannelCapabilityMigrations(suite.chainA.GetContext()) + + if tc.expPass { + suite.Require().NoError(err) + + isMiddlewareEnabled := suite.chainA.GetSimApp().ICAControllerKeeper.IsMiddlewareEnabled( + suite.chainA.GetContext(), + path.EndpointA.ChannelConfig.PortID, + path.EndpointA.ConnectionID, + ) + + suite.Require().True(isMiddlewareEnabled) + } else { + suite.Require().Error(err) + } + }) + } +} diff --git a/modules/apps/27-interchain-accounts/controller/keeper/msg_server.go b/modules/apps/27-interchain-accounts/controller/keeper/msg_server.go new file mode 100644 index 00000000000..0495d0490b6 --- /dev/null +++ b/modules/apps/27-interchain-accounts/controller/keeper/msg_server.go @@ -0,0 +1,72 @@ +package keeper + +import ( + "context" + + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/controller/types" + icatypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types" +) + +var _ types.MsgServer = msgServer{} + +type msgServer struct { + *Keeper +} + +// NewMsgServerImpl returns an implementation of the ICS27 MsgServer interface +// for the provided Keeper. +func NewMsgServerImpl(keeper *Keeper) types.MsgServer { + return &msgServer{Keeper: keeper} +} + +// RegisterInterchainAccount defines a rpc handler for MsgRegisterInterchainAccount +func (s msgServer) RegisterInterchainAccount(goCtx context.Context, msg *types.MsgRegisterInterchainAccount) (*types.MsgRegisterInterchainAccountResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + portID, err := icatypes.NewControllerPortID(msg.Owner) + if err != nil { + return nil, err + } + + if s.IsMiddlewareEnabled(ctx, portID, msg.ConnectionId) && !s.IsActiveChannelClosed(ctx, msg.ConnectionId, portID) { + return nil, errorsmod.Wrap(icatypes.ErrInvalidChannelFlow, "channel is already active or a handshake is in flight") + } + + s.SetMiddlewareDisabled(ctx, portID, msg.ConnectionId) + + channelID, err := s.registerInterchainAccount(ctx, msg.ConnectionId, portID, msg.Version) + if err != nil { + s.Logger(ctx).Error("error registering interchain account", "error", err.Error()) + return nil, err + } + + s.Logger(ctx).Info("successfully registered interchain account", "channel-id", channelID) + + return &types.MsgRegisterInterchainAccountResponse{ + ChannelId: channelID, + PortId: portID, + }, nil +} + +// SendTx defines a rpc handler for MsgSendTx +func (s msgServer) SendTx(goCtx context.Context, msg *types.MsgSendTx) (*types.MsgSendTxResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + portID, err := icatypes.NewControllerPortID(msg.Owner) + if err != nil { + return nil, err + } + + // the absolute timeout value is calculated using the controller chain block time + the relative timeout value + // this assumes time synchrony to a certain degree between the controller and counterparty host chain + absoluteTimeout := uint64(ctx.BlockTime().UnixNano()) + msg.RelativeTimeout + seq, err := s.sendTx(ctx, msg.ConnectionId, portID, msg.PacketData, absoluteTimeout) + if err != nil { + return nil, err + } + + return &types.MsgSendTxResponse{Sequence: seq}, nil +} diff --git a/modules/apps/27-interchain-accounts/controller/keeper/msg_server_test.go b/modules/apps/27-interchain-accounts/controller/keeper/msg_server_test.go new file mode 100644 index 00000000000..44585b2eea4 --- /dev/null +++ b/modules/apps/27-interchain-accounts/controller/keeper/msg_server_test.go @@ -0,0 +1,200 @@ +package keeper_test + +import ( + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/cosmos/gogoproto/proto" + + "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/controller/keeper" + "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/controller/types" + icatypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + ibctesting "github.com/cosmos/ibc-go/v7/testing" +) + +func (suite *KeeperTestSuite) TestRegisterInterchainAccount_MsgServer() { + var ( + msg *types.MsgRegisterInterchainAccount + expectedChannelID = "channel-0" + ) + + testCases := []struct { + name string + expPass bool + malleate func() + }{ + { + "success", + true, + func() {}, + }, + { + "invalid connection id", + false, + func() { + msg.ConnectionId = "connection-100" + }, + }, + { + "non-empty owner address is valid", + true, + func() { + msg.Owner = "" + }, + }, + { + "empty address invalid", + false, + func() { + msg.Owner = "" + }, + }, + { + "port is already bound for owner but capability is claimed by another module", + false, + func() { + capability := suite.chainA.GetSimApp().IBCKeeper.PortKeeper.BindPort(suite.chainA.GetContext(), TestPortID) + err := suite.chainA.GetSimApp().TransferKeeper.ClaimCapability(suite.chainA.GetContext(), capability, host.PortPath(TestPortID)) + suite.Require().NoError(err) + }, + }, + } + + for _, tc := range testCases { + suite.SetupTest() + + path := NewICAPath(suite.chainA, suite.chainB) + suite.coordinator.SetupConnections(path) + + msg = types.NewMsgRegisterInterchainAccount(ibctesting.FirstConnectionID, ibctesting.TestAccAddress, "") + + tc.malleate() + + ctx := suite.chainA.GetContext() + msgServer := keeper.NewMsgServerImpl(&suite.chainA.GetSimApp().ICAControllerKeeper) + res, err := msgServer.RegisterInterchainAccount(ctx, msg) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().NotNil(res) + suite.Require().Equal(expectedChannelID, res.ChannelId) + + events := ctx.EventManager().Events() + suite.Require().Len(events, 2) + suite.Require().Equal(events[0].Type, channeltypes.EventTypeChannelOpenInit) + suite.Require().Equal(events[1].Type, sdk.EventTypeMessage) + } else { + suite.Require().Error(err) + suite.Require().Nil(res) + } + } +} + +func (suite *KeeperTestSuite) TestSubmitTx() { + var ( + path *ibctesting.Path + msg *types.MsgSendTx + ) + + testCases := []struct { + name string + malleate func() + expPass bool + }{ + { + "success", func() { + }, + true, + }, + { + "failure - owner address is empty", func() { + msg.Owner = "" + }, + false, + }, + { + "failure - active channel does not exist for connection ID", func() { + msg.Owner = TestOwnerAddress + msg.ConnectionId = "connection-100" + }, + false, + }, + { + "failure - active channel does not exist for port ID", func() { + msg.Owner = "invalid-owner" + }, + false, + }, + { + "failure - controller module does not own capability for this channel", func() { + msg.Owner = "invalid-owner" + portID, err := icatypes.NewControllerPortID(msg.Owner) + suite.Require().NoError(err) + + // set the active channel with the incorrect portID in order to reach the capability check + suite.chainA.GetSimApp().ICAControllerKeeper.SetActiveChannelID(suite.chainA.GetContext(), path.EndpointA.ConnectionID, portID, path.EndpointA.ChannelID) + }, + false, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + suite.SetupTest() + + owner := TestOwnerAddress + path = NewICAPath(suite.chainA, suite.chainB) + suite.coordinator.SetupConnections(path) + + err := SetupICAPath(path, owner) + 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 + interchainAccountAddr, found := suite.chainA.GetSimApp().ICAControllerKeeper.GetInterchainAccountAddress(suite.chainA.GetContext(), path.EndpointA.ConnectionID, portID) + suite.Require().True(found) + + // create bank transfer message that will execute on the host chain + icaMsg := &banktypes.MsgSend{ + FromAddress: interchainAccountAddr, + ToAddress: suite.chainB.SenderAccount.GetAddress().String(), + Amount: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100))), + } + + data, err := icatypes.SerializeCosmosTx(suite.chainA.Codec, []proto.Message{icaMsg}) + suite.Require().NoError(err) + + packetData := icatypes.InterchainAccountPacketData{ + Type: icatypes.EXECUTE_TX, + Data: data, + Memo: "memo", + } + + timeoutTimestamp := uint64(suite.chainA.GetContext().BlockTime().Add(time.Minute).UnixNano()) + connectionID := path.EndpointA.ConnectionID + + msg = types.NewMsgSendTx(owner, connectionID, timeoutTimestamp, packetData) + + tc.malleate() // malleate mutates test data + + ctx := suite.chainA.GetContext() + msgServer := keeper.NewMsgServerImpl(&suite.chainA.GetSimApp().ICAControllerKeeper) + res, err := msgServer.SendTx(ctx, msg) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().NotNil(res) + } else { + suite.Require().Error(err) + suite.Require().Nil(res) + } + }) + } +} diff --git a/modules/apps/27-interchain-accounts/controller/keeper/params.go b/modules/apps/27-interchain-accounts/controller/keeper/params.go index 38e82a27c80..8e18cd80095 100644 --- a/modules/apps/27-interchain-accounts/controller/keeper/params.go +++ b/modules/apps/27-interchain-accounts/controller/keeper/params.go @@ -3,7 +3,7 @@ package keeper import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/controller/types" + "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/controller/types" ) // IsControllerEnabled retrieves the controller enabled boolean from the paramstore. diff --git a/modules/apps/27-interchain-accounts/controller/keeper/params_test.go b/modules/apps/27-interchain-accounts/controller/keeper/params_test.go index 4e4d8703e40..7b19883af14 100644 --- a/modules/apps/27-interchain-accounts/controller/keeper/params_test.go +++ b/modules/apps/27-interchain-accounts/controller/keeper/params_test.go @@ -1,6 +1,6 @@ package keeper_test -import "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/controller/types" +import "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/controller/types" func (suite *KeeperTestSuite) TestParams() { expParams := types.DefaultParams() diff --git a/modules/apps/27-interchain-accounts/controller/keeper/relay.go b/modules/apps/27-interchain-accounts/controller/keeper/relay.go index 6d197aadca2..b8a5996a5a0 100644 --- a/modules/apps/27-interchain-accounts/controller/keeper/relay.go +++ b/modules/apps/27-interchain-accounts/controller/keeper/relay.go @@ -1,13 +1,14 @@ package keeper import ( + errorsmod "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - icatypes "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/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" + icatypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" ) // SendTx takes pre-built packet data containing messages to be executed on the host chain from an authentication module and attempts to send the packet. @@ -15,63 +16,40 @@ import ( // If the base application has the capability to send on the provided portID. An appropriate // absolute timeoutTimestamp must be provided. If the packet is timed out, the channel will be closed. // In the case of channel closure, a new channel may be reopened to reconnect to the host chain. -func (k Keeper) SendTx(ctx sdk.Context, chanCap *capabilitytypes.Capability, connectionID, portID string, icaPacketData icatypes.InterchainAccountPacketData, timeoutTimestamp uint64) (uint64, error) { +// +// Deprecated: this is a legacy API that is only intended to function correctly in workflows where an underlying application has been set. +// Prior to to v6.x.x of ibc-go, the controller module was only functional as middleware, with authentication performed +// by the underlying application. For a full summary of the changes in v6.x.x, please see ADR009. +// This API will be removed in later releases. +func (k Keeper) SendTx(ctx sdk.Context, _ *capabilitytypes.Capability, connectionID, portID string, icaPacketData icatypes.InterchainAccountPacketData, timeoutTimestamp uint64) (uint64, error) { + return k.sendTx(ctx, connectionID, portID, icaPacketData, timeoutTimestamp) +} + +func (k Keeper) sendTx(ctx sdk.Context, connectionID, portID string, icaPacketData icatypes.InterchainAccountPacketData, timeoutTimestamp uint64) (uint64, error) { activeChannelID, found := k.GetOpenActiveChannel(ctx, connectionID, portID) if !found { - return 0, sdkerrors.Wrapf(icatypes.ErrActiveChannelNotFound, "failed to retrieve active channel on connection %s for port %s", connectionID, portID) + return 0, errorsmod.Wrapf(icatypes.ErrActiveChannelNotFound, "failed to retrieve active channel on connection %s for port %s", connectionID, portID) } - sourceChannelEnd, found := k.channelKeeper.GetChannel(ctx, portID, activeChannelID) + chanCap, found := k.scopedKeeper.GetCapability(ctx, host.ChannelCapabilityPath(portID, activeChannelID)) if !found { - return 0, sdkerrors.Wrap(channeltypes.ErrChannelNotFound, activeChannelID) + return 0, errorsmod.Wrapf(capabilitytypes.ErrCapabilityNotFound, "failed to find capability: %s", host.ChannelCapabilityPath(portID, activeChannelID)) } - destinationPort := sourceChannelEnd.GetCounterparty().GetPortID() - destinationChannel := sourceChannelEnd.GetCounterparty().GetChannelID() - if uint64(ctx.BlockTime().UnixNano()) >= timeoutTimestamp { return 0, icatypes.ErrInvalidTimeoutTimestamp } - return k.createOutgoingPacket(ctx, portID, activeChannelID, destinationPort, destinationChannel, chanCap, icaPacketData, timeoutTimestamp) -} - -func (k Keeper) createOutgoingPacket( - ctx sdk.Context, - sourcePort, - sourceChannel, - destinationPort, - destinationChannel string, - chanCap *capabilitytypes.Capability, - icaPacketData icatypes.InterchainAccountPacketData, - timeoutTimestamp uint64, -) (uint64, error) { if err := icaPacketData.ValidateBasic(); err != nil { - return 0, sdkerrors.Wrap(err, "invalid interchain account packet data") + return 0, errorsmod.Wrap(err, "invalid interchain account packet data") } - // get the next sequence - sequence, found := k.channelKeeper.GetNextSequenceSend(ctx, sourcePort, sourceChannel) - if !found { - return 0, sdkerrors.Wrapf(channeltypes.ErrSequenceSendNotFound, "failed to retrieve next sequence send for channel %s on port %s", sourceChannel, sourcePort) - } - - packet := channeltypes.NewPacket( - icaPacketData.GetBytes(), - sequence, - sourcePort, - sourceChannel, - destinationPort, - destinationChannel, - clienttypes.ZeroHeight(), - timeoutTimestamp, - ) - - if err := k.ics4Wrapper.SendPacket(ctx, chanCap, packet); err != nil { + sequence, err := k.ics4Wrapper.SendPacket(ctx, chanCap, portID, activeChannelID, clienttypes.ZeroHeight(), timeoutTimestamp, icaPacketData.GetBytes()) + if err != nil { return 0, err } - return packet.Sequence, nil + return sequence, nil } // OnTimeoutPacket removes the active channel associated with the provided packet, the underlying channel end is closed diff --git a/modules/apps/27-interchain-accounts/controller/keeper/relay_test.go b/modules/apps/27-interchain-accounts/controller/keeper/relay_test.go index a980d3f953d..c16af788923 100644 --- a/modules/apps/27-interchain-accounts/controller/keeper/relay_test.go +++ b/modules/apps/27-interchain-accounts/controller/keeper/relay_test.go @@ -3,20 +3,18 @@ package keeper_test import ( sdk "github.com/cosmos/cosmos-sdk/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" - capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" + "github.com/cosmos/gogoproto/proto" - icatypes "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/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" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" - ibctesting "github.com/cosmos/ibc-go/v4/testing" + icatypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + ibctesting "github.com/cosmos/ibc-go/v7/testing" ) func (suite *KeeperTestSuite) TestSendTx() { var ( path *ibctesting.Path packetData icatypes.InterchainAccountPacketData - chanCap *capabilitytypes.Capability timeoutTimestamp uint64 ) @@ -37,7 +35,7 @@ func (suite *KeeperTestSuite) TestSendTx() { Amount: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100))), } - data, err := icatypes.SerializeCosmosTx(suite.chainB.GetSimApp().AppCodec(), []sdk.Msg{msg}) + data, err := icatypes.SerializeCosmosTx(suite.chainB.GetSimApp().AppCodec(), []proto.Message{msg}) suite.Require().NoError(err) packetData = icatypes.InterchainAccountPacketData{ @@ -53,7 +51,7 @@ func (suite *KeeperTestSuite) TestSendTx() { interchainAccountAddr, found := suite.chainA.GetSimApp().ICAControllerKeeper.GetInterchainAccountAddress(suite.chainA.GetContext(), ibctesting.FirstConnectionID, path.EndpointA.ChannelConfig.PortID) suite.Require().True(found) - msgsBankSend := []sdk.Msg{ + msgsBankSend := []proto.Message{ &banktypes.MsgSend{ FromAddress: interchainAccountAddr, ToAddress: suite.chainB.SenderAccount.GetAddress().String(), @@ -93,13 +91,6 @@ func (suite *KeeperTestSuite) TestSendTx() { }, false, }, - { - "channel does not exist", - func() { - suite.chainA.GetSimApp().ICAControllerKeeper.SetActiveChannelID(suite.chainA.GetContext(), ibctesting.FirstConnectionID, path.EndpointA.ChannelConfig.PortID, "channel-100") - }, - false, - }, { "channel in INIT state - optimistic packet sends fail", func() { @@ -114,18 +105,11 @@ func (suite *KeeperTestSuite) TestSendTx() { { "sendPacket fails - channel closed", func() { - err := path.EndpointA.SetChannelClosed() + err := path.EndpointA.SetChannelState(channeltypes.CLOSED) suite.Require().NoError(err) }, false, }, - { - "invalid channel capability provided", - func() { - chanCap = nil - }, - false, - }, { "timeout timestamp is not in the future", func() { @@ -138,7 +122,7 @@ func (suite *KeeperTestSuite) TestSendTx() { Amount: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100))), } - data, err := icatypes.SerializeCosmosTx(suite.chainB.GetSimApp().AppCodec(), []sdk.Msg{msg}) + data, err := icatypes.SerializeCosmosTx(suite.chainB.GetSimApp().AppCodec(), []proto.Message{msg}) suite.Require().NoError(err) packetData = icatypes.InterchainAccountPacketData{ @@ -165,13 +149,10 @@ func (suite *KeeperTestSuite) TestSendTx() { err := SetupICAPath(path, TestOwnerAddress) suite.Require().NoError(err) - var ok bool - chanCap, ok = suite.chainA.GetSimApp().ScopedICAMockKeeper.GetCapability(path.EndpointA.Chain.GetContext(), host.ChannelCapabilityPath(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID)) - suite.Require().True(ok) - tc.malleate() // malleate mutates test data - _, err = suite.chainA.GetSimApp().ICAControllerKeeper.SendTx(suite.chainA.GetContext(), chanCap, ibctesting.FirstConnectionID, path.EndpointA.ChannelConfig.PortID, packetData, timeoutTimestamp) + //nolint: staticcheck // SA1019: ibctesting.FirstConnectionID is deprecated: use path.EndpointA.ConnectionID instead. (staticcheck) + _, err = suite.chainA.GetSimApp().ICAControllerKeeper.SendTx(suite.chainA.GetContext(), nil, ibctesting.FirstConnectionID, path.EndpointA.ChannelConfig.PortID, packetData, timeoutTimestamp) if tc.expPass { suite.Require().NoError(err) diff --git a/modules/apps/27-interchain-accounts/controller/migrations/v6/migrations.go b/modules/apps/27-interchain-accounts/controller/migrations/v6/migrations.go new file mode 100644 index 00000000000..4b7406f044c --- /dev/null +++ b/modules/apps/27-interchain-accounts/controller/migrations/v6/migrations.go @@ -0,0 +1,77 @@ +package v6 + +import ( + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/store/prefix" + storetypes "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" + capabilitykeeper "github.com/cosmos/cosmos-sdk/x/capability/keeper" + capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" + + controllertypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/controller/types" + ibcexported "github.com/cosmos/ibc-go/v7/modules/core/exported" +) + +// MigrateICS27ChannelCapability performs a search on a prefix store using the provided store key and module name. +// It retrieves the associated channel capability index and reassigns ownership to the ICS27 controller submodule. +func MigrateICS27ChannelCapability( + ctx sdk.Context, + cdc codec.BinaryCodec, + capabilityStoreKey storetypes.StoreKey, + capabilityKeeper *capabilitykeeper.Keeper, + module string, // the name of the scoped keeper for the underlying app module +) error { + // construct a prefix store using the x/capability index prefix: index->capability owners + prefixStore := prefix.NewStore(ctx.KVStore(capabilityStoreKey), capabilitytypes.KeyPrefixIndexCapability) + iterator := sdk.KVStorePrefixIterator(prefixStore, nil) + defer sdk.LogDeferred(ctx.Logger(), func() error { return iterator.Close() }) + + for ; iterator.Valid(); iterator.Next() { + // unmarshal the capability index value and set of owners + index := capabilitytypes.IndexFromKey(iterator.Key()) + + var owners capabilitytypes.CapabilityOwners + cdc.MustUnmarshal(iterator.Value(), &owners) + + if !hasIBCOwner(owners.GetOwners()) { + continue + } + + for _, owner := range owners.GetOwners() { + if owner.Module == module { + // remove the owner from the set + owners.Remove(owner) + + // reassign the owner module to icacontroller + owner.Module = controllertypes.SubModuleName + + // add the controller submodule to the set of owners + if err := owners.Set(owner); err != nil { + return err + } + + // set the new owners for the current capability index + capabilityKeeper.SetOwners(ctx, index, owners) + } + } + } + + // initialise the x/capability memstore + capabilityKeeper.InitMemStore(ctx) + + return nil +} + +func hasIBCOwner(owners []capabilitytypes.Owner) bool { + if len(owners) != 2 { + return false + } + + for _, owner := range owners { + if owner.Module == ibcexported.ModuleName { + return true + } + } + + return false +} diff --git a/modules/apps/27-interchain-accounts/controller/migrations/v6/migrations_test.go b/modules/apps/27-interchain-accounts/controller/migrations/v6/migrations_test.go new file mode 100644 index 00000000000..934db7395a3 --- /dev/null +++ b/modules/apps/27-interchain-accounts/controller/migrations/v6/migrations_test.go @@ -0,0 +1,200 @@ +package v6_test + +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" + "github.com/stretchr/testify/suite" + + v6 "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/controller/migrations/v6" + "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/controller/types" + icatypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + ibctesting "github.com/cosmos/ibc-go/v7/testing" + ibcmock "github.com/cosmos/ibc-go/v7/testing/mock" +) + +type MigrationsTestSuite struct { + suite.Suite + + chainA *ibctesting.TestChain + chainB *ibctesting.TestChain + + coordinator *ibctesting.Coordinator + path *ibctesting.Path +} + +func (suite *MigrationsTestSuite) SetupTest() { + suite.coordinator = ibctesting.NewCoordinator(suite.T(), 2) + + suite.chainA = suite.coordinator.GetChain(ibctesting.GetChainID(1)) + suite.chainB = suite.coordinator.GetChain(ibctesting.GetChainID(2)) + + suite.path = ibctesting.NewPath(suite.chainA, suite.chainB) + suite.path.EndpointA.ChannelConfig.PortID = icatypes.HostPortID + suite.path.EndpointB.ChannelConfig.PortID = icatypes.HostPortID + suite.path.EndpointA.ChannelConfig.Order = channeltypes.ORDERED + suite.path.EndpointB.ChannelConfig.Order = channeltypes.ORDERED + suite.path.EndpointA.ChannelConfig.Version = icatypes.NewDefaultMetadataString(ibctesting.FirstConnectionID, ibctesting.FirstConnectionID) + suite.path.EndpointB.ChannelConfig.Version = icatypes.NewDefaultMetadataString(ibctesting.FirstConnectionID, ibctesting.FirstConnectionID) +} + +func (suite *MigrationsTestSuite) SetupPath() error { + if err := suite.RegisterInterchainAccount(suite.path.EndpointA, ibctesting.TestAccAddress); err != nil { + return err + } + + if err := suite.path.EndpointB.ChanOpenTry(); err != nil { + return err + } + + if err := suite.path.EndpointA.ChanOpenAck(); err != nil { + return err + } + + if err := suite.path.EndpointB.ChanOpenConfirm(); err != nil { + return err + } + + return nil +} + +func (suite *MigrationsTestSuite) RegisterInterchainAccount(endpoint *ibctesting.Endpoint, owner string) error { + portID, err := icatypes.NewControllerPortID(owner) + if err != nil { + return err + } + + channelSequence := endpoint.Chain.App.GetIBCKeeper().ChannelKeeper.GetNextChannelSequence(endpoint.Chain.GetContext()) + + if err := endpoint.Chain.GetSimApp().ICAControllerKeeper.RegisterInterchainAccount(endpoint.Chain.GetContext(), endpoint.ConnectionID, owner, endpoint.ChannelConfig.Version); err != nil { + return err + } + + // commit state changes for proof verification + endpoint.Chain.NextBlock() + + // update port/channel ids + endpoint.ChannelID = channeltypes.FormatChannelIdentifier(channelSequence) + endpoint.ChannelConfig.PortID = portID + + return nil +} + +func TestKeeperTestSuite(t *testing.T) { + suite.Run(t, new(MigrationsTestSuite)) +} + +func (suite *MigrationsTestSuite) TestMigrateICS27ChannelCapability() { + suite.SetupTest() + suite.coordinator.SetupConnections(suite.path) + + err := suite.SetupPath() + suite.Require().NoError(err) + + // create additional capabilities to cover edge cases + suite.CreateMockCapabilities() + + // create and claim a new capability with ibc/mock for "channel-1" + // note: suite.SetupPath() now claims the chanel capability using icacontroller for "channel-0" + capName := host.ChannelCapabilityPath(suite.path.EndpointA.ChannelConfig.PortID, channeltypes.FormatChannelIdentifier(1)) + + cap, err := suite.chainA.GetSimApp().ScopedIBCKeeper.NewCapability(suite.chainA.GetContext(), capName) + suite.Require().NoError(err) + + err = suite.chainA.GetSimApp().ScopedICAMockKeeper.ClaimCapability(suite.chainA.GetContext(), cap, capName) + suite.Require().NoError(err) + + // assert the capability is owned by the mock module + cap, found := suite.chainA.GetSimApp().ScopedICAMockKeeper.GetCapability(suite.chainA.GetContext(), capName) + suite.Require().NotNil(cap) + suite.Require().True(found) + + isAuthenticated := suite.chainA.GetSimApp().ScopedICAMockKeeper.AuthenticateCapability(suite.chainA.GetContext(), cap, capName) + suite.Require().True(isAuthenticated) + + cap, found = suite.chainA.GetSimApp().ScopedICAControllerKeeper.GetCapability(suite.chainA.GetContext(), capName) + suite.Require().Nil(cap) + suite.Require().False(found) + + suite.ResetMemStore() // empty the x/capability in-memory store + + err = v6.MigrateICS27ChannelCapability( + suite.chainA.GetContext(), + suite.chainA.Codec, + suite.chainA.GetSimApp().GetKey(capabilitytypes.StoreKey), + suite.chainA.GetSimApp().CapabilityKeeper, + ibcmock.ModuleName+types.SubModuleName, + ) + + suite.Require().NoError(err) + + // assert the capability is now owned by the ICS27 controller submodule + cap, found = suite.chainA.GetSimApp().ScopedICAControllerKeeper.GetCapability(suite.chainA.GetContext(), capName) + suite.Require().NotNil(cap) + suite.Require().True(found) + + isAuthenticated = suite.chainA.GetSimApp().ScopedICAControllerKeeper.AuthenticateCapability(suite.chainA.GetContext(), cap, capName) + suite.Require().True(isAuthenticated) + + cap, found = suite.chainA.GetSimApp().ScopedICAMockKeeper.GetCapability(suite.chainA.GetContext(), capName) + suite.Require().Nil(cap) + suite.Require().False(found) + + // ensure channel capability for "channel-0" is still owned by the controller + capName = host.ChannelCapabilityPath(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID) + cap, found = suite.chainA.GetSimApp().ScopedICAControllerKeeper.GetCapability(suite.chainA.GetContext(), capName) + suite.Require().NotNil(cap) + suite.Require().True(found) + + isAuthenticated = suite.chainA.GetSimApp().ScopedICAControllerKeeper.AuthenticateCapability(suite.chainA.GetContext(), cap, capName) + suite.Require().True(isAuthenticated) + + suite.AssertMockCapabiltiesUnchanged() +} + +// CreateMockCapabilities creates an additional two capabilities used for testing purposes: +// 1. A capability with a single owner +// 2. A capability with two owners, neither of which is "ibc" +func (suite *MigrationsTestSuite) CreateMockCapabilities() { + cap, err := suite.chainA.GetSimApp().ScopedIBCMockKeeper.NewCapability(suite.chainA.GetContext(), "mock_one") + suite.Require().NoError(err) + suite.Require().NotNil(cap) + + cap, err = suite.chainA.GetSimApp().ScopedICAMockKeeper.NewCapability(suite.chainA.GetContext(), "mock_two") + suite.Require().NoError(err) + suite.Require().NotNil(cap) + + err = suite.chainA.GetSimApp().ScopedIBCMockKeeper.ClaimCapability(suite.chainA.GetContext(), cap, "mock_two") + suite.Require().NoError(err) +} + +// AssertMockCapabiltiesUnchanged authenticates the mock capabilities created at the start of the test to ensure they remain unchanged +func (suite *MigrationsTestSuite) AssertMockCapabiltiesUnchanged() { + cap, found := suite.chainA.GetSimApp().ScopedIBCMockKeeper.GetCapability(suite.chainA.GetContext(), "mock_one") + suite.Require().True(found) + suite.Require().NotNil(cap) + + cap, found = suite.chainA.GetSimApp().ScopedIBCMockKeeper.GetCapability(suite.chainA.GetContext(), "mock_two") + suite.Require().True(found) + suite.Require().NotNil(cap) + + isAuthenticated := suite.chainA.GetSimApp().ScopedICAMockKeeper.AuthenticateCapability(suite.chainA.GetContext(), cap, "mock_two") + suite.Require().True(isAuthenticated) +} + +// ResetMemstore removes all existing fwd and rev capability kv pairs and deletes `KeyMemInitialised` from the x/capability memstore. +// This effectively mocks a new chain binary being started. Migration code is run against persisted state only and allows the memstore to be reinitialised. +func (suite *MigrationsTestSuite) ResetMemStore() { + memStore := suite.chainA.GetContext().KVStore(suite.chainA.GetSimApp().GetMemKey(capabilitytypes.MemStoreKey)) + memStore.Delete(capabilitytypes.KeyMemInitialized) + + iterator := memStore.Iterator(nil, nil) + defer sdk.LogDeferred(suite.chainA.GetContext().Logger(), func() error { return iterator.Close() }) + + for ; iterator.Valid(); iterator.Next() { + memStore.Delete(iterator.Key()) + } +} diff --git a/modules/apps/27-interchain-accounts/controller/types/codec.go b/modules/apps/27-interchain-accounts/controller/types/codec.go new file mode 100644 index 00000000000..6b684cf4618 --- /dev/null +++ b/modules/apps/27-interchain-accounts/controller/types/codec.go @@ -0,0 +1,15 @@ +package types + +import ( + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// RegisterInterfaces registers the interchain accounts controller message types using the provided InterfaceRegistry +func RegisterInterfaces(registry codectypes.InterfaceRegistry) { + registry.RegisterImplementations( + (*sdk.Msg)(nil), + &MsgRegisterInterchainAccount{}, + &MsgSendTx{}, + ) +} diff --git a/modules/apps/27-interchain-accounts/controller/types/controller.pb.go b/modules/apps/27-interchain-accounts/controller/types/controller.pb.go index 8aa795abab6..7d8badd96e1 100644 --- a/modules/apps/27-interchain-accounts/controller/types/controller.pb.go +++ b/modules/apps/27-interchain-accounts/controller/types/controller.pb.go @@ -5,8 +5,8 @@ package types import ( fmt "fmt" - _ "github.com/gogo/protobuf/gogoproto" - proto "github.com/gogo/protobuf/proto" + _ "github.com/cosmos/gogoproto/gogoproto" + proto "github.com/cosmos/gogoproto/proto" io "io" math "math" math_bits "math/bits" @@ -93,9 +93,9 @@ var fileDescriptor_177fd0fec5eb3400 = []byte{ 0x24, 0xc7, 0xf8, 0xe0, 0x91, 0x1c, 0xe3, 0x84, 0xc7, 0x72, 0x0c, 0x17, 0x1e, 0xcb, 0x31, 0xdc, 0x78, 0x2c, 0xc7, 0x10, 0x15, 0x90, 0x9e, 0x59, 0x92, 0x51, 0x9a, 0xa4, 0x97, 0x9c, 0x9f, 0xab, 0x9f, 0x9c, 0x5f, 0x9c, 0x9b, 0x5f, 0xac, 0x9f, 0x99, 0x94, 0xac, 0x9b, 0x9e, 0xaf, 0x5f, 0x66, - 0xa2, 0x9f, 0x9b, 0x9f, 0x52, 0x9a, 0x93, 0x5a, 0x0c, 0x0a, 0xa0, 0x62, 0x7d, 0x23, 0x73, 0x5d, + 0xae, 0x9f, 0x9b, 0x9f, 0x52, 0x9a, 0x93, 0x5a, 0x0c, 0x0a, 0xa0, 0x62, 0x7d, 0x23, 0x73, 0x5d, 0x84, 0xb7, 0x74, 0xb1, 0x85, 0x4d, 0x49, 0x65, 0x41, 0x6a, 0x71, 0x12, 0x1b, 0xd8, 0x2b, 0xc6, - 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0x36, 0xd4, 0x5d, 0xad, 0x5b, 0x01, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0xe7, 0xf2, 0x00, 0xac, 0x5b, 0x01, 0x00, 0x00, } func (m *Params) Marshal() (dAtA []byte, err error) { diff --git a/modules/apps/27-interchain-accounts/controller/types/errors.go b/modules/apps/27-interchain-accounts/controller/types/errors.go index 3a0ade00fe1..2f2d5595e21 100644 --- a/modules/apps/27-interchain-accounts/controller/types/errors.go +++ b/modules/apps/27-interchain-accounts/controller/types/errors.go @@ -1,10 +1,10 @@ package types import ( - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + errorsmod "cosmossdk.io/errors" ) // ICA Controller sentinel errors var ( - ErrControllerSubModuleDisabled = sdkerrors.Register(SubModuleName, 2, "controller submodule is disabled") + ErrControllerSubModuleDisabled = errorsmod.Register(SubModuleName, 2, "controller submodule is disabled") ) diff --git a/modules/apps/27-interchain-accounts/controller/types/msgs.go b/modules/apps/27-interchain-accounts/controller/types/msgs.go new file mode 100644 index 00000000000..5837b76a01b --- /dev/null +++ b/modules/apps/27-interchain-accounts/controller/types/msgs.go @@ -0,0 +1,87 @@ +package types + +import ( + "strings" + + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + + ibcerrors "github.com/cosmos/ibc-go/v7/internal/errors" + icatypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" +) + +var _ sdk.Msg = &MsgRegisterInterchainAccount{} + +// NewMsgRegisterInterchainAccount creates a new instance of MsgRegisterInterchainAccount +func NewMsgRegisterInterchainAccount(connectionID, owner, version string) *MsgRegisterInterchainAccount { + return &MsgRegisterInterchainAccount{ + ConnectionId: connectionID, + Owner: owner, + Version: version, + } +} + +// ValidateBasic implements sdk.Msg +func (msg MsgRegisterInterchainAccount) ValidateBasic() error { + if err := host.ConnectionIdentifierValidator(msg.ConnectionId); err != nil { + return errorsmod.Wrap(err, "invalid connection ID") + } + + if strings.TrimSpace(msg.Owner) == "" { + return errorsmod.Wrap(ibcerrors.ErrInvalidAddress, "owner address cannot be empty") + } + + return nil +} + +// GetSigners implements sdk.Msg +func (msg MsgRegisterInterchainAccount) GetSigners() []sdk.AccAddress { + accAddr, err := sdk.AccAddressFromBech32(msg.Owner) + if err != nil { + panic(err) + } + + return []sdk.AccAddress{accAddr} +} + +// NewMsgSendTx creates a new instance of MsgSendTx +func NewMsgSendTx(owner, connectionID string, relativeTimeoutTimestamp uint64, packetData icatypes.InterchainAccountPacketData) *MsgSendTx { + return &MsgSendTx{ + ConnectionId: connectionID, + Owner: owner, + RelativeTimeout: relativeTimeoutTimestamp, + PacketData: packetData, + } +} + +// ValidateBasic implements sdk.Msg +func (msg MsgSendTx) ValidateBasic() error { + if err := host.ConnectionIdentifierValidator(msg.ConnectionId); err != nil { + return errorsmod.Wrap(err, "invalid connection ID") + } + + if strings.TrimSpace(msg.Owner) == "" { + return errorsmod.Wrap(ibcerrors.ErrInvalidAddress, "owner address cannot be empty") + } + + if err := msg.PacketData.ValidateBasic(); err != nil { + return errorsmod.Wrap(err, "invalid interchain account packet data") + } + + if msg.RelativeTimeout == 0 { + return errorsmod.Wrap(ibcerrors.ErrInvalidRequest, "relative timeout cannot be zero") + } + + return nil +} + +// GetSigners implements sdk.Msg +func (msg MsgSendTx) GetSigners() []sdk.AccAddress { + accAddr, err := sdk.AccAddressFromBech32(msg.Owner) + if err != nil { + panic(err) + } + + return []sdk.AccAddress{accAddr} +} diff --git a/modules/apps/27-interchain-accounts/controller/types/msgs_test.go b/modules/apps/27-interchain-accounts/controller/types/msgs_test.go new file mode 100644 index 00000000000..e5afa00c93e --- /dev/null +++ b/modules/apps/27-interchain-accounts/controller/types/msgs_test.go @@ -0,0 +1,196 @@ +package types_test + +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/cosmos/gogoproto/proto" + "github.com/stretchr/testify/require" + + "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/controller/types" + icatypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types" + feetypes "github.com/cosmos/ibc-go/v7/modules/apps/29-fee/types" + ibctesting "github.com/cosmos/ibc-go/v7/testing" + "github.com/cosmos/ibc-go/v7/testing/simapp" +) + +func TestMsgRegisterInterchainAccountValidateBasic(t *testing.T) { + var msg *types.MsgRegisterInterchainAccount + + testCases := []struct { + name string + malleate func() + expPass bool + }{ + { + "success", + func() {}, + true, + }, + { + "success: with empty channel version", + func() { + msg.Version = "" + }, + true, + }, + { + "success: with fee enabled channel version", + func() { + feeMetadata := feetypes.Metadata{ + FeeVersion: feetypes.Version, + AppVersion: icatypes.NewDefaultMetadataString(ibctesting.FirstConnectionID, ibctesting.FirstConnectionID), + } + + bz := feetypes.ModuleCdc.MustMarshalJSON(&feeMetadata) + msg.Version = string(bz) + }, + true, + }, + { + "connection id is invalid", + func() { + msg.ConnectionId = "" + }, + false, + }, + { + "owner address is empty", + func() { + msg.Owner = "" + }, + false, + }, + } + + for i, tc := range testCases { + + msg = types.NewMsgRegisterInterchainAccount( + ibctesting.FirstConnectionID, + ibctesting.TestAccAddress, + icatypes.NewDefaultMetadataString(ibctesting.FirstConnectionID, ibctesting.FirstConnectionID), + ) + + tc.malleate() + + err := msg.ValidateBasic() + if tc.expPass { + require.NoError(t, err, "valid test case %d failed: %s", i, tc.name) + } else { + require.Error(t, err, "invalid test case %d passed: %s", i, tc.name) + } + } +} + +func TestMsgRegisterInterchainAccountGetSigners(t *testing.T) { + expSigner, err := sdk.AccAddressFromBech32(ibctesting.TestAccAddress) + require.NoError(t, err) + + msg := types.NewMsgRegisterInterchainAccount(ibctesting.FirstConnectionID, ibctesting.TestAccAddress, "") + require.Equal(t, []sdk.AccAddress{expSigner}, msg.GetSigners()) +} + +func TestMsgSendTxValidateBasic(t *testing.T) { + var msg *types.MsgSendTx + + testCases := []struct { + name string + malleate func() + expPass bool + }{ + { + "success", + func() {}, + true, + }, + { + "connection id is invalid", + func() { + msg.ConnectionId = "" + }, + false, + }, + { + "owner address is empty", + func() { + msg.Owner = "" + }, + false, + }, + { + "relative timeout is not set", + func() { + msg.RelativeTimeout = 0 + }, + false, + }, + { + "messages array is empty", + func() { + msg.PacketData = icatypes.InterchainAccountPacketData{} + }, + false, + }, + } + + for i, tc := range testCases { + + msgBankSend := &banktypes.MsgSend{ + FromAddress: ibctesting.TestAccAddress, + ToAddress: ibctesting.TestAccAddress, + Amount: ibctesting.TestCoins, + } + + data, err := icatypes.SerializeCosmosTx(simapp.MakeTestEncodingConfig().Marshaler, []proto.Message{msgBankSend}) + require.NoError(t, err) + + packetData := icatypes.InterchainAccountPacketData{ + Type: icatypes.EXECUTE_TX, + Data: data, + } + + msg = types.NewMsgSendTx( + ibctesting.TestAccAddress, + ibctesting.FirstConnectionID, + 100000, + packetData, + ) + + tc.malleate() + + err = msg.ValidateBasic() + if tc.expPass { + require.NoError(t, err, "valid test case %d failed: %s", i, tc.name) + } else { + require.Error(t, err, "invalid test case %d passed: %s", i, tc.name) + } + } +} + +func TestMsgSendTxGetSigners(t *testing.T) { + expSigner, err := sdk.AccAddressFromBech32(ibctesting.TestAccAddress) + require.NoError(t, err) + + msgBankSend := &banktypes.MsgSend{ + FromAddress: ibctesting.TestAccAddress, + ToAddress: ibctesting.TestAccAddress, + Amount: ibctesting.TestCoins, + } + + data, err := icatypes.SerializeCosmosTx(simapp.MakeTestEncodingConfig().Marshaler, []proto.Message{msgBankSend}) + require.NoError(t, err) + + packetData := icatypes.InterchainAccountPacketData{ + Type: icatypes.EXECUTE_TX, + Data: data, + } + + msg := types.NewMsgSendTx( + ibctesting.TestAccAddress, + ibctesting.FirstConnectionID, + 100000, + packetData, + ) + require.Equal(t, []sdk.AccAddress{expSigner}, msg.GetSigners()) +} diff --git a/modules/apps/27-interchain-accounts/controller/types/params.go b/modules/apps/27-interchain-accounts/controller/types/params.go index d7b9cc83874..5baaaa8b1df 100644 --- a/modules/apps/27-interchain-accounts/controller/types/params.go +++ b/modules/apps/27-interchain-accounts/controller/types/params.go @@ -33,7 +33,7 @@ func DefaultParams() Params { // Validate validates all controller submodule parameters func (p Params) Validate() error { - if err := validateEnabled(p.ControllerEnabled); err != nil { + if err := validateEnabledType(p.ControllerEnabled); err != nil { return err } @@ -43,11 +43,11 @@ func (p Params) Validate() error { // ParamSetPairs implements params.ParamSet func (p *Params) ParamSetPairs() paramtypes.ParamSetPairs { return paramtypes.ParamSetPairs{ - paramtypes.NewParamSetPair(KeyControllerEnabled, p.ControllerEnabled, validateEnabled), + paramtypes.NewParamSetPair(KeyControllerEnabled, p.ControllerEnabled, validateEnabledType), } } -func validateEnabled(i interface{}) error { +func validateEnabledType(i interface{}) error { _, ok := i.(bool) if !ok { return fmt.Errorf("invalid parameter type: %T", i) diff --git a/modules/apps/27-interchain-accounts/controller/types/params_test.go b/modules/apps/27-interchain-accounts/controller/types/params_test.go index df3927e22d3..e2f653b741e 100644 --- a/modules/apps/27-interchain-accounts/controller/types/params_test.go +++ b/modules/apps/27-interchain-accounts/controller/types/params_test.go @@ -5,7 +5,7 @@ import ( "github.com/stretchr/testify/require" - "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/controller/types" + "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/controller/types" ) func TestValidateParams(t *testing.T) { 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..c1900bbc179 100644 --- a/modules/apps/27-interchain-accounts/controller/types/query.pb.go +++ b/modules/apps/27-interchain-accounts/controller/types/query.pb.go @@ -6,8 +6,9 @@ package types import ( context "context" fmt "fmt" - grpc1 "github.com/gogo/protobuf/grpc" - proto "github.com/gogo/protobuf/proto" + _ "github.com/cosmos/gogoproto/gogoproto" + grpc1 "github.com/cosmos/gogoproto/grpc" + proto "github.com/cosmos/gogoproto/proto" _ "google.golang.org/genproto/googleapis/api/annotations" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" @@ -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, 0x14, 0x0a, 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, 0xbf, 0x87, 0xe3, 0x55, 0x76, 0x6f, 0xec, + 0xdf, 0x7e, 0xf9, 0x7e, 0x1e, 0x06, 0x27, 0xf3, 0x30, 0xf8, 0x32, 0x0f, 0x83, 0x37, 0x8b, 0xb0, + 0x73, 0xb2, 0x08, 0x3b, 0x9f, 0x16, 0x61, 0xe7, 0xf9, 0x6e, 0x26, 0xed, 0x41, 0x35, 0x21, 0x1c, + 0x0a, 0xca, 0xc1, 0x14, 0x60, 0x6a, 0xf9, 0x51, 0x06, 0x74, 0xba, 0x45, 0x0b, 0x48, 0xab, 0x5c, + 0x98, 0x06, 0x16, 0x6f, 0x8d, 0x96, 0xbc, 0xd1, 0x59, 0x3c, 0x3b, 0x2b, 0x85, 0x99, 0xf4, 0xdc, + 0x4f, 0x7a, 0xf7, 0x47, 0x00, 0x00, 0x00, 0xff, 0xff, 0x0e, 0x23, 0x39, 0x1f, 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..5a0301d480d 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 @@ -20,6 +20,7 @@ import ( "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/grpclog" + "google.golang.org/grpc/metadata" "google.golang.org/grpc/status" ) @@ -30,6 +31,83 @@ var _ status.Status var _ = runtime.String var _ = utilities.NewDoubleArray var _ = descriptor.ForMessage +var _ = metadata.Join + +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 @@ -52,12 +130,37 @@ func local_request_Query_Params_0(ctx context.Context, marshaler runtime.Marshal // RegisterQueryHandlerServer registers the http handlers for service Query to "mux". // UnaryRPC :call QueryServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. -// Note that using this registration option will cause many gRPC library features (such as grpc.SendHeader, etc) to stop working. Consider using RegisterQueryHandlerFromEndpoint instead. +// Note that using this registration option will cause many gRPC library features 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() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + 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) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + 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() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { @@ -65,6 +168,7 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv return } resp, md, err := local_request_Query_Params_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) @@ -116,6 +220,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 +264,13 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie } var ( - 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))) + 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(false))) + + 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(false))) ) var ( + forward_Query_InterchainAccount_0 = runtime.ForwardResponseMessage + forward_Query_Params_0 = runtime.ForwardResponseMessage ) diff --git a/modules/apps/27-interchain-accounts/controller/types/tx.pb.go b/modules/apps/27-interchain-accounts/controller/types/tx.pb.go new file mode 100644 index 00000000000..b15979a713e --- /dev/null +++ b/modules/apps/27-interchain-accounts/controller/types/tx.pb.go @@ -0,0 +1,1212 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: ibc/applications/interchain_accounts/controller/v1/tx.proto + +package types + +import ( + context "context" + fmt "fmt" + _ "github.com/cosmos/gogoproto/gogoproto" + grpc1 "github.com/cosmos/gogoproto/grpc" + proto "github.com/cosmos/gogoproto/proto" + types "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// MsgRegisterInterchainAccount defines the payload for Msg/RegisterAccount +type MsgRegisterInterchainAccount 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"` + Version string `protobuf:"bytes,3,opt,name=version,proto3" json:"version,omitempty"` +} + +func (m *MsgRegisterInterchainAccount) Reset() { *m = MsgRegisterInterchainAccount{} } +func (m *MsgRegisterInterchainAccount) String() string { return proto.CompactTextString(m) } +func (*MsgRegisterInterchainAccount) ProtoMessage() {} +func (*MsgRegisterInterchainAccount) Descriptor() ([]byte, []int) { + return fileDescriptor_7def041328c84a30, []int{0} +} +func (m *MsgRegisterInterchainAccount) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgRegisterInterchainAccount) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgRegisterInterchainAccount.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 *MsgRegisterInterchainAccount) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgRegisterInterchainAccount.Merge(m, src) +} +func (m *MsgRegisterInterchainAccount) XXX_Size() int { + return m.Size() +} +func (m *MsgRegisterInterchainAccount) XXX_DiscardUnknown() { + xxx_messageInfo_MsgRegisterInterchainAccount.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgRegisterInterchainAccount proto.InternalMessageInfo + +// MsgRegisterInterchainAccountResponse defines the response for Msg/RegisterAccount +type MsgRegisterInterchainAccountResponse struct { + ChannelId string `protobuf:"bytes,1,opt,name=channel_id,json=channelId,proto3" json:"channel_id,omitempty" yaml:"channel_id"` + PortId string `protobuf:"bytes,2,opt,name=port_id,json=portId,proto3" json:"port_id,omitempty" yaml:"port_id"` +} + +func (m *MsgRegisterInterchainAccountResponse) Reset() { *m = MsgRegisterInterchainAccountResponse{} } +func (m *MsgRegisterInterchainAccountResponse) String() string { return proto.CompactTextString(m) } +func (*MsgRegisterInterchainAccountResponse) ProtoMessage() {} +func (*MsgRegisterInterchainAccountResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_7def041328c84a30, []int{1} +} +func (m *MsgRegisterInterchainAccountResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgRegisterInterchainAccountResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgRegisterInterchainAccountResponse.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 *MsgRegisterInterchainAccountResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgRegisterInterchainAccountResponse.Merge(m, src) +} +func (m *MsgRegisterInterchainAccountResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgRegisterInterchainAccountResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgRegisterInterchainAccountResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgRegisterInterchainAccountResponse proto.InternalMessageInfo + +func (m *MsgRegisterInterchainAccountResponse) GetChannelId() string { + if m != nil { + return m.ChannelId + } + return "" +} + +func (m *MsgRegisterInterchainAccountResponse) GetPortId() string { + if m != nil { + return m.PortId + } + return "" +} + +// MsgSendTx defines the payload for Msg/SendTx +type MsgSendTx 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"` + PacketData types.InterchainAccountPacketData `protobuf:"bytes,3,opt,name=packet_data,json=packetData,proto3" json:"packet_data" yaml:"packet_data"` + // Relative timeout timestamp provided will be added to the current block time during transaction execution. + // The timeout timestamp must be non-zero. + RelativeTimeout uint64 `protobuf:"varint,4,opt,name=relative_timeout,json=relativeTimeout,proto3" json:"relative_timeout,omitempty" yaml:"relative_timeout"` +} + +func (m *MsgSendTx) Reset() { *m = MsgSendTx{} } +func (m *MsgSendTx) String() string { return proto.CompactTextString(m) } +func (*MsgSendTx) ProtoMessage() {} +func (*MsgSendTx) Descriptor() ([]byte, []int) { + return fileDescriptor_7def041328c84a30, []int{2} +} +func (m *MsgSendTx) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgSendTx) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgSendTx.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 *MsgSendTx) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgSendTx.Merge(m, src) +} +func (m *MsgSendTx) XXX_Size() int { + return m.Size() +} +func (m *MsgSendTx) XXX_DiscardUnknown() { + xxx_messageInfo_MsgSendTx.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgSendTx proto.InternalMessageInfo + +// MsgSendTxResponse defines the response for MsgSendTx +type MsgSendTxResponse struct { + Sequence uint64 `protobuf:"varint,1,opt,name=sequence,proto3" json:"sequence,omitempty"` +} + +func (m *MsgSendTxResponse) Reset() { *m = MsgSendTxResponse{} } +func (m *MsgSendTxResponse) String() string { return proto.CompactTextString(m) } +func (*MsgSendTxResponse) ProtoMessage() {} +func (*MsgSendTxResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_7def041328c84a30, []int{3} +} +func (m *MsgSendTxResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgSendTxResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgSendTxResponse.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 *MsgSendTxResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgSendTxResponse.Merge(m, src) +} +func (m *MsgSendTxResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgSendTxResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgSendTxResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgSendTxResponse proto.InternalMessageInfo + +func (m *MsgSendTxResponse) GetSequence() uint64 { + if m != nil { + return m.Sequence + } + return 0 +} + +func init() { + proto.RegisterType((*MsgRegisterInterchainAccount)(nil), "ibc.applications.interchain_accounts.controller.v1.MsgRegisterInterchainAccount") + proto.RegisterType((*MsgRegisterInterchainAccountResponse)(nil), "ibc.applications.interchain_accounts.controller.v1.MsgRegisterInterchainAccountResponse") + proto.RegisterType((*MsgSendTx)(nil), "ibc.applications.interchain_accounts.controller.v1.MsgSendTx") + proto.RegisterType((*MsgSendTxResponse)(nil), "ibc.applications.interchain_accounts.controller.v1.MsgSendTxResponse") +} + +func init() { + proto.RegisterFile("ibc/applications/interchain_accounts/controller/v1/tx.proto", fileDescriptor_7def041328c84a30) +} + +var fileDescriptor_7def041328c84a30 = []byte{ + // 570 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x94, 0xbf, 0x6f, 0xd3, 0x40, + 0x14, 0xc7, 0xed, 0x34, 0xa4, 0xcd, 0x95, 0x5f, 0xb5, 0x82, 0x30, 0x06, 0xd9, 0x95, 0xc5, 0x50, + 0x09, 0xc5, 0xa7, 0x84, 0x4a, 0x95, 0x8a, 0x3a, 0x10, 0x15, 0xa4, 0x0c, 0x91, 0x22, 0xd3, 0x01, + 0xb1, 0x44, 0x97, 0xf3, 0xc9, 0x39, 0x70, 0xee, 0x8c, 0xef, 0x62, 0xda, 0x91, 0x0d, 0x16, 0xc4, + 0xc6, 0xda, 0xbf, 0x82, 0x7f, 0x81, 0x8e, 0x1d, 0x99, 0xac, 0x2a, 0x59, 0x98, 0xf3, 0x17, 0x20, + 0xdb, 0x89, 0x53, 0xa0, 0x54, 0xe5, 0x47, 0x37, 0xbf, 0x7b, 0xf7, 0x79, 0xef, 0x7b, 0xef, 0x3d, + 0x3f, 0xf0, 0x88, 0xf6, 0x31, 0x44, 0x61, 0x18, 0x50, 0x8c, 0x24, 0xe5, 0x4c, 0x40, 0xca, 0x24, + 0x89, 0xf0, 0x00, 0x51, 0xd6, 0x43, 0x18, 0xf3, 0x11, 0x93, 0x02, 0x62, 0xce, 0x64, 0xc4, 0x83, + 0x80, 0x44, 0x30, 0x6e, 0x40, 0xb9, 0xef, 0x84, 0x11, 0x97, 0x5c, 0x6b, 0xd2, 0x3e, 0x76, 0x4e, + 0xc3, 0xce, 0x19, 0xb0, 0xb3, 0x80, 0x9d, 0xb8, 0x61, 0xd4, 0x7c, 0xee, 0xf3, 0x0c, 0x87, 0xe9, + 0x57, 0x1e, 0xc9, 0xd8, 0xbc, 0x90, 0x8c, 0xb8, 0x01, 0x43, 0x84, 0x5f, 0x11, 0x99, 0x53, 0xf6, + 0x27, 0x15, 0xdc, 0xeb, 0x08, 0xdf, 0x25, 0x3e, 0x15, 0x92, 0x44, 0xed, 0x02, 0x79, 0x9c, 0x13, + 0x5a, 0x0d, 0x5c, 0xe1, 0x6f, 0x18, 0x89, 0x74, 0x75, 0x5d, 0xdd, 0xa8, 0xba, 0xb9, 0xa1, 0xed, + 0x80, 0x6b, 0x98, 0x33, 0x46, 0x70, 0x9a, 0xa9, 0x47, 0x3d, 0xbd, 0x94, 0x7a, 0x5b, 0xfa, 0x34, + 0xb1, 0x6a, 0x07, 0x68, 0x18, 0x6c, 0xdb, 0x3f, 0xb8, 0x6d, 0xf7, 0xea, 0xc2, 0x6e, 0x7b, 0x9a, + 0x0e, 0x96, 0x63, 0x12, 0x09, 0xca, 0x99, 0xbe, 0x94, 0x85, 0x9d, 0x9b, 0xdb, 0x2b, 0xef, 0x0e, + 0x2d, 0xe5, 0xdb, 0xa1, 0xa5, 0xd8, 0xef, 0x55, 0x70, 0xff, 0x3c, 0x65, 0x2e, 0x11, 0x21, 0x67, + 0x82, 0x68, 0x9b, 0x00, 0xe0, 0x01, 0x62, 0x8c, 0x04, 0xa9, 0x90, 0x4c, 0x66, 0xeb, 0xd6, 0x34, + 0xb1, 0xd6, 0x66, 0x42, 0x0a, 0x9f, 0xed, 0x56, 0x67, 0x46, 0xdb, 0xd3, 0x1e, 0x80, 0xe5, 0x90, + 0x47, 0x72, 0xa1, 0x5d, 0x9b, 0x26, 0xd6, 0xf5, 0x1c, 0x99, 0x39, 0x6c, 0xb7, 0x92, 0x7e, 0xb5, + 0x3d, 0xfb, 0x73, 0x09, 0x54, 0x3b, 0xc2, 0x7f, 0x46, 0x98, 0xb7, 0xb7, 0x7f, 0x39, 0x25, 0x79, + 0xab, 0x82, 0xd5, 0xbc, 0x33, 0x3d, 0x0f, 0x49, 0x94, 0xd5, 0x65, 0xb5, 0xb9, 0xeb, 0x5c, 0x68, + 0x3e, 0xe2, 0x86, 0xf3, 0x4b, 0x7d, 0xba, 0x59, 0xb0, 0x5d, 0x24, 0x51, 0xcb, 0x38, 0x4a, 0x2c, + 0x65, 0x9a, 0x58, 0xda, 0xec, 0x79, 0x8b, 0x34, 0xb6, 0x0b, 0xc2, 0xe2, 0x9e, 0xf6, 0x14, 0xdc, + 0x8c, 0x48, 0x80, 0x24, 0x8d, 0x49, 0x4f, 0xd2, 0x21, 0xe1, 0x23, 0xa9, 0x97, 0xd7, 0xd5, 0x8d, + 0x72, 0xeb, 0xee, 0x34, 0xb1, 0x6e, 0xe7, 0xf4, 0xcf, 0x37, 0x6c, 0xf7, 0xc6, 0xfc, 0x68, 0x2f, + 0x3f, 0x39, 0xd5, 0x44, 0x08, 0xd6, 0x8a, 0xba, 0x15, 0x0d, 0x33, 0xc0, 0x8a, 0x20, 0xaf, 0x47, + 0x84, 0x61, 0x92, 0x95, 0xb0, 0xec, 0x16, 0x76, 0xf3, 0xa4, 0x04, 0x96, 0x3a, 0xc2, 0xd7, 0xbe, + 0xa8, 0xe0, 0xce, 0xef, 0x87, 0xb2, 0xeb, 0xfc, 0xf9, 0x6f, 0xe3, 0x9c, 0x37, 0x4c, 0xc6, 0xf3, + 0xff, 0x1d, 0xb1, 0x78, 0xed, 0x07, 0x15, 0x54, 0x66, 0x83, 0xb3, 0xf3, 0x97, 0x49, 0x72, 0xdc, + 0x78, 0xf2, 0x4f, 0xf8, 0x5c, 0x50, 0xeb, 0xe5, 0xd1, 0xd8, 0x54, 0x8f, 0xc7, 0xa6, 0x7a, 0x32, + 0x36, 0xd5, 0x8f, 0x13, 0x53, 0x39, 0x9e, 0x98, 0xca, 0xd7, 0x89, 0xa9, 0xbc, 0xe8, 0xfa, 0x54, + 0x0e, 0x46, 0x7d, 0x07, 0xf3, 0x21, 0xc4, 0x5c, 0x0c, 0xb9, 0x80, 0xb4, 0x8f, 0xeb, 0x3e, 0x87, + 0xf1, 0x16, 0x1c, 0x72, 0x6f, 0x14, 0x10, 0x91, 0xae, 0x18, 0x01, 0x9b, 0x5b, 0xf5, 0x45, 0xea, + 0xfa, 0x59, 0x4b, 0x4e, 0x1e, 0x84, 0x44, 0xf4, 0x2b, 0xd9, 0x96, 0x79, 0xf8, 0x3d, 0x00, 0x00, + 0xff, 0xff, 0xf0, 0x81, 0x7c, 0xb2, 0x24, 0x05, 0x00, 0x00, +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// MsgClient is the client API for Msg service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type MsgClient interface { + // RegisterInterchainAccount defines a rpc handler for MsgRegisterInterchainAccount. + RegisterInterchainAccount(ctx context.Context, in *MsgRegisterInterchainAccount, opts ...grpc.CallOption) (*MsgRegisterInterchainAccountResponse, error) + // SendTx defines a rpc handler for MsgSendTx. + SendTx(ctx context.Context, in *MsgSendTx, opts ...grpc.CallOption) (*MsgSendTxResponse, error) +} + +type msgClient struct { + cc grpc1.ClientConn +} + +func NewMsgClient(cc grpc1.ClientConn) MsgClient { + return &msgClient{cc} +} + +func (c *msgClient) RegisterInterchainAccount(ctx context.Context, in *MsgRegisterInterchainAccount, opts ...grpc.CallOption) (*MsgRegisterInterchainAccountResponse, error) { + out := new(MsgRegisterInterchainAccountResponse) + err := c.cc.Invoke(ctx, "/ibc.applications.interchain_accounts.controller.v1.Msg/RegisterInterchainAccount", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) SendTx(ctx context.Context, in *MsgSendTx, opts ...grpc.CallOption) (*MsgSendTxResponse, error) { + out := new(MsgSendTxResponse) + err := c.cc.Invoke(ctx, "/ibc.applications.interchain_accounts.controller.v1.Msg/SendTx", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// MsgServer is the server API for Msg service. +type MsgServer interface { + // RegisterInterchainAccount defines a rpc handler for MsgRegisterInterchainAccount. + RegisterInterchainAccount(context.Context, *MsgRegisterInterchainAccount) (*MsgRegisterInterchainAccountResponse, error) + // SendTx defines a rpc handler for MsgSendTx. + SendTx(context.Context, *MsgSendTx) (*MsgSendTxResponse, error) +} + +// UnimplementedMsgServer can be embedded to have forward compatible implementations. +type UnimplementedMsgServer struct { +} + +func (*UnimplementedMsgServer) RegisterInterchainAccount(ctx context.Context, req *MsgRegisterInterchainAccount) (*MsgRegisterInterchainAccountResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method RegisterInterchainAccount not implemented") +} +func (*UnimplementedMsgServer) SendTx(ctx context.Context, req *MsgSendTx) (*MsgSendTxResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SendTx not implemented") +} + +func RegisterMsgServer(s grpc1.Server, srv MsgServer) { + s.RegisterService(&_Msg_serviceDesc, srv) +} + +func _Msg_RegisterInterchainAccount_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgRegisterInterchainAccount) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).RegisterInterchainAccount(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ibc.applications.interchain_accounts.controller.v1.Msg/RegisterInterchainAccount", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).RegisterInterchainAccount(ctx, req.(*MsgRegisterInterchainAccount)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_SendTx_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgSendTx) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).SendTx(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ibc.applications.interchain_accounts.controller.v1.Msg/SendTx", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).SendTx(ctx, req.(*MsgSendTx)) + } + return interceptor(ctx, in, info, handler) +} + +var _Msg_serviceDesc = grpc.ServiceDesc{ + ServiceName: "ibc.applications.interchain_accounts.controller.v1.Msg", + HandlerType: (*MsgServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "RegisterInterchainAccount", + Handler: _Msg_RegisterInterchainAccount_Handler, + }, + { + MethodName: "SendTx", + Handler: _Msg_SendTx_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "ibc/applications/interchain_accounts/controller/v1/tx.proto", +} + +func (m *MsgRegisterInterchainAccount) 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 *MsgRegisterInterchainAccount) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgRegisterInterchainAccount) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Version) > 0 { + i -= len(m.Version) + copy(dAtA[i:], m.Version) + i = encodeVarintTx(dAtA, i, uint64(len(m.Version))) + i-- + dAtA[i] = 0x1a + } + if len(m.ConnectionId) > 0 { + i -= len(m.ConnectionId) + copy(dAtA[i:], m.ConnectionId) + i = encodeVarintTx(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 = encodeVarintTx(dAtA, i, uint64(len(m.Owner))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgRegisterInterchainAccountResponse) 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 *MsgRegisterInterchainAccountResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgRegisterInterchainAccountResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.PortId) > 0 { + i -= len(m.PortId) + copy(dAtA[i:], m.PortId) + i = encodeVarintTx(dAtA, i, uint64(len(m.PortId))) + i-- + dAtA[i] = 0x12 + } + if len(m.ChannelId) > 0 { + i -= len(m.ChannelId) + copy(dAtA[i:], m.ChannelId) + i = encodeVarintTx(dAtA, i, uint64(len(m.ChannelId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgSendTx) 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 *MsgSendTx) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgSendTx) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.RelativeTimeout != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.RelativeTimeout)) + i-- + dAtA[i] = 0x20 + } + { + size, err := m.PacketData.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + if len(m.ConnectionId) > 0 { + i -= len(m.ConnectionId) + copy(dAtA[i:], m.ConnectionId) + i = encodeVarintTx(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 = encodeVarintTx(dAtA, i, uint64(len(m.Owner))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgSendTxResponse) 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 *MsgSendTxResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgSendTxResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = 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 +} + +func encodeVarintTx(dAtA []byte, offset int, v uint64) int { + offset -= sovTx(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *MsgRegisterInterchainAccount) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Owner) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.ConnectionId) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.Version) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgRegisterInterchainAccountResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ChannelId) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.PortId) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgSendTx) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Owner) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.ConnectionId) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = m.PacketData.Size() + n += 1 + l + sovTx(uint64(l)) + if m.RelativeTimeout != 0 { + n += 1 + sovTx(uint64(m.RelativeTimeout)) + } + return n +} + +func (m *MsgSendTxResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Sequence != 0 { + n += 1 + sovTx(uint64(m.Sequence)) + } + return n +} + +func sovTx(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozTx(x uint64) (n int) { + return sovTx(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *MsgRegisterInterchainAccount) 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 ErrIntOverflowTx + } + 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: MsgRegisterInterchainAccount: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgRegisterInterchainAccount: 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 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.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 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.ConnectionId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Version", 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.Version = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgRegisterInterchainAccountResponse) 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 ErrIntOverflowTx + } + 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: MsgRegisterInterchainAccountResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgRegisterInterchainAccountResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ChannelId", 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.ChannelId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PortId", 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.PortId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgSendTx) 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 ErrIntOverflowTx + } + 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: MsgSendTx: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgSendTx: 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 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.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 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.ConnectionId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PacketData", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.PacketData.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field RelativeTimeout", wireType) + } + m.RelativeTimeout = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.RelativeTimeout |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgSendTxResponse) 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 ErrIntOverflowTx + } + 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: MsgSendTxResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgSendTxResponse: 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:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipTx(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTx + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTx + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTx + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthTx + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupTx + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthTx + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthTx = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowTx = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupTx = fmt.Errorf("proto: unexpected end of group") +) diff --git a/modules/apps/27-interchain-accounts/doc.go b/modules/apps/27-interchain-accounts/doc.go new file mode 100644 index 00000000000..b31e3a63d3e --- /dev/null +++ b/modules/apps/27-interchain-accounts/doc.go @@ -0,0 +1,8 @@ +/* +Package ica implements the packet data structure, state machine handling logic, +and encoding details for the account management system over an IBC channel +between separate chains. +This implementation is based off the ICS 27 specification +(https://github.com/cosmos/ibc/tree/main/spec/app/ics-027-interchain-accounts) +*/ +package ica diff --git a/modules/apps/27-interchain-accounts/types/genesis.go b/modules/apps/27-interchain-accounts/genesis/types/genesis.go similarity index 88% rename from modules/apps/27-interchain-accounts/types/genesis.go rename to modules/apps/27-interchain-accounts/genesis/types/genesis.go index 67e8391ef66..f69ae13a00a 100644 --- a/modules/apps/27-interchain-accounts/types/genesis.go +++ b/modules/apps/27-interchain-accounts/genesis/types/genesis.go @@ -1,9 +1,10 @@ package types import ( - controllertypes "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/controller/types" - hosttypes "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/host/types" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" + controllertypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/controller/types" + hosttypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/host/types" + icatypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" ) // DefaultGenesis creates and returns the interchain accounts GenesisState @@ -69,7 +70,7 @@ func (gs ControllerGenesisState) Validate() error { return err } - if err := ValidateAccountAddress(acc.AccountAddress); err != nil { + if err := icatypes.ValidateAccountAddress(acc.AccountAddress); err != nil { return err } } @@ -90,7 +91,7 @@ func (gs ControllerGenesisState) Validate() error { // DefaultHostGenesis creates and returns the default interchain accounts HostGenesisState func DefaultHostGenesis() HostGenesisState { return HostGenesisState{ - Port: PortID, + Port: icatypes.HostPortID, Params: hosttypes.DefaultParams(), } } @@ -122,7 +123,7 @@ func (gs HostGenesisState) Validate() error { return err } - if err := ValidateAccountAddress(acc.AccountAddress); err != nil { + if err := icatypes.ValidateAccountAddress(acc.AccountAddress); err != nil { return err } } diff --git a/modules/apps/27-interchain-accounts/types/genesis.pb.go b/modules/apps/27-interchain-accounts/genesis/types/genesis.pb.go similarity index 85% rename from modules/apps/27-interchain-accounts/types/genesis.pb.go rename to modules/apps/27-interchain-accounts/genesis/types/genesis.pb.go index 5dc6b9e4112..8e551ec1d62 100644 --- a/modules/apps/27-interchain-accounts/types/genesis.pb.go +++ b/modules/apps/27-interchain-accounts/genesis/types/genesis.pb.go @@ -1,14 +1,14 @@ // Code generated by protoc-gen-gogo. DO NOT EDIT. -// source: ibc/applications/interchain_accounts/v1/genesis.proto +// source: ibc/applications/interchain_accounts/genesis/v1/genesis.proto package types import ( fmt "fmt" - types "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/controller/types" - types1 "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/host/types" - _ "github.com/gogo/protobuf/gogoproto" - proto "github.com/gogo/protobuf/proto" + _ "github.com/cosmos/gogoproto/gogoproto" + proto "github.com/cosmos/gogoproto/proto" + types "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/controller/types" + types1 "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/host/types" io "io" math "math" math_bits "math/bits" @@ -35,7 +35,7 @@ func (m *GenesisState) Reset() { *m = GenesisState{} } func (m *GenesisState) String() string { return proto.CompactTextString(m) } func (*GenesisState) ProtoMessage() {} func (*GenesisState) Descriptor() ([]byte, []int) { - return fileDescriptor_629b3ced0911516b, []int{0} + return fileDescriptor_d4aa48c8e29a1947, []int{0} } func (m *GenesisState) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -90,7 +90,7 @@ func (m *ControllerGenesisState) Reset() { *m = ControllerGenesisState{} func (m *ControllerGenesisState) String() string { return proto.CompactTextString(m) } func (*ControllerGenesisState) ProtoMessage() {} func (*ControllerGenesisState) Descriptor() ([]byte, []int) { - return fileDescriptor_629b3ced0911516b, []int{1} + return fileDescriptor_d4aa48c8e29a1947, []int{1} } func (m *ControllerGenesisState) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -159,7 +159,7 @@ func (m *HostGenesisState) Reset() { *m = HostGenesisState{} } func (m *HostGenesisState) String() string { return proto.CompactTextString(m) } func (*HostGenesisState) ProtoMessage() {} func (*HostGenesisState) Descriptor() ([]byte, []int) { - return fileDescriptor_629b3ced0911516b, []int{2} + return fileDescriptor_d4aa48c8e29a1947, []int{2} } func (m *HostGenesisState) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -216,18 +216,20 @@ func (m *HostGenesisState) GetParams() types1.Params { return types1.Params{} } -// ActiveChannel contains a connection ID, port ID and associated active channel ID +// ActiveChannel contains a connection ID, port ID and associated active channel ID, as well as a boolean flag to +// indicate if the channel is middleware enabled type ActiveChannel struct { - ConnectionId string `protobuf:"bytes,1,opt,name=connection_id,json=connectionId,proto3" json:"connection_id,omitempty" yaml:"connection_id"` - PortId string `protobuf:"bytes,2,opt,name=port_id,json=portId,proto3" json:"port_id,omitempty" yaml:"port_id"` - ChannelId string `protobuf:"bytes,3,opt,name=channel_id,json=channelId,proto3" json:"channel_id,omitempty" yaml:"channel_id"` + ConnectionId string `protobuf:"bytes,1,opt,name=connection_id,json=connectionId,proto3" json:"connection_id,omitempty" yaml:"connection_id"` + PortId string `protobuf:"bytes,2,opt,name=port_id,json=portId,proto3" json:"port_id,omitempty" yaml:"port_id"` + ChannelId string `protobuf:"bytes,3,opt,name=channel_id,json=channelId,proto3" json:"channel_id,omitempty" yaml:"channel_id"` + IsMiddlewareEnabled bool `protobuf:"varint,4,opt,name=is_middleware_enabled,json=isMiddlewareEnabled,proto3" json:"is_middleware_enabled,omitempty" yaml:"is_middleware_enabled"` } func (m *ActiveChannel) Reset() { *m = ActiveChannel{} } func (m *ActiveChannel) String() string { return proto.CompactTextString(m) } func (*ActiveChannel) ProtoMessage() {} func (*ActiveChannel) Descriptor() ([]byte, []int) { - return fileDescriptor_629b3ced0911516b, []int{3} + return fileDescriptor_d4aa48c8e29a1947, []int{3} } func (m *ActiveChannel) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -277,6 +279,13 @@ func (m *ActiveChannel) GetChannelId() string { return "" } +func (m *ActiveChannel) GetIsMiddlewareEnabled() bool { + if m != nil { + return m.IsMiddlewareEnabled + } + return false +} + // RegisteredInterchainAccount contains a connection ID, port ID and associated interchain account address type RegisteredInterchainAccount struct { ConnectionId string `protobuf:"bytes,1,opt,name=connection_id,json=connectionId,proto3" json:"connection_id,omitempty" yaml:"connection_id"` @@ -288,7 +297,7 @@ func (m *RegisteredInterchainAccount) Reset() { *m = RegisteredInterchai func (m *RegisteredInterchainAccount) String() string { return proto.CompactTextString(m) } func (*RegisteredInterchainAccount) ProtoMessage() {} func (*RegisteredInterchainAccount) Descriptor() ([]byte, []int) { - return fileDescriptor_629b3ced0911516b, []int{4} + return fileDescriptor_d4aa48c8e29a1947, []int{4} } func (m *RegisteredInterchainAccount) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -339,60 +348,63 @@ func (m *RegisteredInterchainAccount) GetAccountAddress() string { } func init() { - proto.RegisterType((*GenesisState)(nil), "ibc.applications.interchain_accounts.v1.GenesisState") - proto.RegisterType((*ControllerGenesisState)(nil), "ibc.applications.interchain_accounts.v1.ControllerGenesisState") - proto.RegisterType((*HostGenesisState)(nil), "ibc.applications.interchain_accounts.v1.HostGenesisState") - proto.RegisterType((*ActiveChannel)(nil), "ibc.applications.interchain_accounts.v1.ActiveChannel") - proto.RegisterType((*RegisteredInterchainAccount)(nil), "ibc.applications.interchain_accounts.v1.RegisteredInterchainAccount") + proto.RegisterType((*GenesisState)(nil), "ibc.applications.interchain_accounts.genesis.v1.GenesisState") + proto.RegisterType((*ControllerGenesisState)(nil), "ibc.applications.interchain_accounts.genesis.v1.ControllerGenesisState") + proto.RegisterType((*HostGenesisState)(nil), "ibc.applications.interchain_accounts.genesis.v1.HostGenesisState") + proto.RegisterType((*ActiveChannel)(nil), "ibc.applications.interchain_accounts.genesis.v1.ActiveChannel") + proto.RegisterType((*RegisteredInterchainAccount)(nil), "ibc.applications.interchain_accounts.genesis.v1.RegisteredInterchainAccount") } func init() { - proto.RegisterFile("ibc/applications/interchain_accounts/v1/genesis.proto", fileDescriptor_629b3ced0911516b) -} - -var fileDescriptor_629b3ced0911516b = []byte{ - // 643 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x95, 0x3f, 0x6f, 0x13, 0x31, - 0x18, 0xc6, 0xe3, 0xa4, 0x14, 0xc5, 0xfd, 0x43, 0x31, 0xa5, 0x3a, 0x82, 0x74, 0x09, 0x5e, 0x1a, - 0x09, 0xf5, 0x4e, 0x2d, 0x85, 0x8a, 0x4a, 0x08, 0xf5, 0x02, 0x82, 0x6c, 0xc8, 0x2c, 0x88, 0xe5, - 0xe4, 0xf8, 0xac, 0xc4, 0x52, 0x72, 0x8e, 0xce, 0x6e, 0xa4, 0x4e, 0xec, 0x4c, 0x6c, 0x88, 0x15, - 0x89, 0x0f, 0xc0, 0x37, 0x60, 0xec, 0x84, 0x3a, 0x32, 0x45, 0xa8, 0xfd, 0x06, 0xf9, 0x04, 0xc8, - 0xbe, 0x53, 0x92, 0x1e, 0x69, 0x75, 0x13, 0x13, 0x53, 0xec, 0x7b, 0xfd, 0x3c, 0xef, 0xef, 0xf5, - 0xeb, 0xd8, 0xf0, 0xb1, 0xe8, 0x30, 0x9f, 0x0e, 0x87, 0x7d, 0xc1, 0xa8, 0x16, 0x32, 0x56, 0xbe, - 0x88, 0x35, 0x4f, 0x58, 0x8f, 0x8a, 0x38, 0xa4, 0x8c, 0xc9, 0xe3, 0x58, 0x2b, 0x7f, 0xb4, 0xeb, - 0x77, 0x79, 0xcc, 0x95, 0x50, 0xde, 0x30, 0x91, 0x5a, 0xa2, 0x6d, 0xd1, 0x61, 0xde, 0xbc, 0xcc, - 0x5b, 0x20, 0xf3, 0x46, 0xbb, 0xb5, 0xcd, 0xae, 0xec, 0x4a, 0xab, 0xf1, 0xcd, 0x28, 0x95, 0xd7, - 0x5a, 0x85, 0xb2, 0x32, 0x19, 0xeb, 0x44, 0xf6, 0xfb, 0x3c, 0x31, 0x00, 0xb3, 0x59, 0x66, 0x72, - 0x50, 0xc8, 0xa4, 0x27, 0x95, 0x36, 0x72, 0xf3, 0x9b, 0x0a, 0xf1, 0x8f, 0x32, 0x5c, 0x7d, 0x95, - 0x96, 0xf3, 0x56, 0x53, 0xcd, 0xd1, 0x57, 0x00, 0x9d, 0x99, 0x7d, 0x98, 0x95, 0x1a, 0x2a, 0x13, - 0x74, 0x40, 0x03, 0x34, 0x57, 0xf6, 0x9e, 0x7b, 0x05, 0x2b, 0xf6, 0x5a, 0x53, 0xa3, 0xf9, 0x1c, - 0xc1, 0xf6, 0xe9, 0xb8, 0x5e, 0x9a, 0x8c, 0xeb, 0xf5, 0x13, 0x3a, 0xe8, 0x1f, 0xe2, 0xab, 0xd2, - 0x61, 0xb2, 0xc5, 0x16, 0x1a, 0xa0, 0x8f, 0x00, 0x22, 0x53, 0x44, 0x0e, 0xaf, 0x6c, 0xf1, 0x9e, - 0x16, 0xc6, 0x7b, 0x2d, 0x95, 0xbe, 0x04, 0xf6, 0x20, 0x03, 0xbb, 0x97, 0x82, 0xfd, 0x9d, 0x02, - 0x93, 0x8d, 0x5e, 0x4e, 0x84, 0xbf, 0x55, 0xe0, 0xd6, 0xe2, 0x42, 0xd1, 0x07, 0x78, 0x8b, 0x32, - 0x2d, 0x46, 0x3c, 0x64, 0x3d, 0x1a, 0xc7, 0xbc, 0xaf, 0x1c, 0xd0, 0xa8, 0x34, 0x57, 0xf6, 0x9e, - 0x14, 0x66, 0x3c, 0xb2, 0xfa, 0x56, 0x2a, 0x0f, 0xdc, 0x0c, 0x70, 0x2b, 0x05, 0xcc, 0x99, 0x63, - 0xb2, 0x4e, 0xe7, 0x97, 0x2b, 0xf4, 0x05, 0xc0, 0x3b, 0x0b, 0x8c, 0x9d, 0xb2, 0xa5, 0x78, 0x51, - 0x98, 0x82, 0xf0, 0xae, 0x50, 0x9a, 0x27, 0x3c, 0x6a, 0x4f, 0x17, 0x1c, 0xa5, 0xf1, 0x00, 0x67, - 0x4c, 0xb5, 0x94, 0x69, 0x81, 0x03, 0x26, 0x48, 0xe4, 0x65, 0x0a, 0x6d, 0xc2, 0x1b, 0x43, 0x99, - 0x68, 0xe5, 0x54, 0x1a, 0x95, 0x66, 0x95, 0xa4, 0x13, 0xf4, 0x0e, 0x2e, 0x0f, 0x69, 0x42, 0x07, - 0xca, 0x59, 0xb2, 0xdd, 0x3c, 0x2c, 0xc6, 0x38, 0xf7, 0x8f, 0x18, 0xed, 0x7a, 0x6f, 0xac, 0x43, - 0xb0, 0x64, 0xc8, 0x48, 0xe6, 0x87, 0x3f, 0x57, 0xe0, 0x46, 0xbe, 0xe3, 0xff, 0x3b, 0x74, 0x5d, - 0x87, 0x10, 0x5c, 0x32, 0x4d, 0x71, 0x2a, 0x0d, 0xd0, 0xac, 0x12, 0x3b, 0x46, 0x24, 0xd7, 0x9f, - 0xfd, 0x62, 0x84, 0xf6, 0xca, 0xb9, 0xaa, 0x33, 0xdf, 0x01, 0x5c, 0xbb, 0xb4, 0x8b, 0xe8, 0x19, - 0x5c, 0x63, 0x32, 0x8e, 0x39, 0x33, 0x8e, 0xa1, 0x88, 0xec, 0xcd, 0x53, 0x0d, 0x9c, 0xc9, 0xb8, - 0xbe, 0x39, 0xbd, 0x34, 0x66, 0x61, 0x4c, 0x56, 0x67, 0xf3, 0x76, 0x84, 0x1e, 0xc2, 0x9b, 0x06, - 0xd6, 0x08, 0xcb, 0x56, 0x88, 0x26, 0xe3, 0xfa, 0x7a, 0x2a, 0xcc, 0x02, 0x98, 0x2c, 0x9b, 0x51, - 0x3b, 0x42, 0xfb, 0x10, 0x66, 0xed, 0x31, 0xeb, 0x6d, 0xad, 0xc1, 0xdd, 0xc9, 0xb8, 0x7e, 0x3b, - 0x4b, 0x34, 0x8d, 0x61, 0x52, 0xcd, 0x26, 0xed, 0x08, 0xff, 0x04, 0xf0, 0xfe, 0x35, 0x7b, 0xfe, - 0x4f, 0x2b, 0x68, 0x99, 0x43, 0x6c, 0xd3, 0x86, 0x34, 0x8a, 0x12, 0xae, 0x54, 0x56, 0x46, 0x6d, - 0xfe, 0x20, 0x5e, 0x5a, 0x60, 0x0f, 0xa2, 0xfd, 0x72, 0x94, 0x7e, 0x08, 0xc2, 0xd3, 0x73, 0x17, - 0x9c, 0x9d, 0xbb, 0xe0, 0xf7, 0xb9, 0x0b, 0x3e, 0x5d, 0xb8, 0xa5, 0xb3, 0x0b, 0xb7, 0xf4, 0xeb, - 0xc2, 0x2d, 0xbd, 0x7f, 0xd9, 0x15, 0xba, 0x77, 0xdc, 0xf1, 0x98, 0x1c, 0xf8, 0x4c, 0xaa, 0x81, - 0x54, 0xbe, 0xe8, 0xb0, 0x9d, 0xae, 0xf4, 0x47, 0xfb, 0xfe, 0x40, 0x46, 0xc7, 0x7d, 0xae, 0xcc, - 0xe3, 0xa3, 0xfc, 0xbd, 0x83, 0x9d, 0x59, 0xf3, 0x77, 0xa6, 0xef, 0x8e, 0x3e, 0x19, 0x72, 0xd5, - 0x59, 0xb6, 0x2f, 0xce, 0xa3, 0x3f, 0x01, 0x00, 0x00, 0xff, 0xff, 0xcf, 0xd8, 0xa4, 0x04, 0x67, - 0x07, 0x00, 0x00, + proto.RegisterFile("ibc/applications/interchain_accounts/genesis/v1/genesis.proto", fileDescriptor_d4aa48c8e29a1947) +} + +var fileDescriptor_d4aa48c8e29a1947 = []byte{ + // 692 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x95, 0x4d, 0x6b, 0xdb, 0x3e, + 0x1c, 0xc7, 0xe3, 0xa4, 0xff, 0xfe, 0x17, 0xf5, 0x61, 0x9d, 0xfa, 0x80, 0x97, 0x0d, 0x3b, 0xf3, + 0x65, 0x81, 0x51, 0x9b, 0x76, 0x85, 0x42, 0xa1, 0x83, 0x38, 0x8c, 0x2e, 0xb0, 0xc2, 0xf0, 0x76, + 0x18, 0xbb, 0x18, 0x45, 0x16, 0x8e, 0xc0, 0xb1, 0x82, 0xa5, 0x66, 0xf4, 0x15, 0xf4, 0x3a, 0xf6, + 0x0e, 0x76, 0x1b, 0x7b, 0x01, 0x7b, 0x0d, 0x3d, 0x8d, 0x1e, 0x77, 0x0a, 0xa3, 0x7d, 0x07, 0x79, + 0x05, 0x43, 0xb2, 0x9a, 0xa4, 0x69, 0x3a, 0x92, 0xcb, 0x4e, 0x3b, 0x59, 0x0f, 0xbf, 0xef, 0xf7, + 0xf7, 0x91, 0x7e, 0xb2, 0x04, 0x0e, 0x69, 0x0b, 0x7b, 0xa8, 0xdb, 0x4d, 0x28, 0x46, 0x82, 0xb2, + 0x94, 0x7b, 0x34, 0x15, 0x24, 0xc3, 0x6d, 0x44, 0xd3, 0x10, 0x61, 0xcc, 0x4e, 0x52, 0xc1, 0xbd, + 0x98, 0xa4, 0x84, 0x53, 0xee, 0xf5, 0x76, 0xae, 0x9b, 0x6e, 0x37, 0x63, 0x82, 0x41, 0x8f, 0xb6, + 0xb0, 0x3b, 0x2e, 0x77, 0xa7, 0xc8, 0xdd, 0x6b, 0x4d, 0x6f, 0xa7, 0xb2, 0x11, 0xb3, 0x98, 0x29, + 0xad, 0x27, 0x5b, 0xb9, 0x4d, 0xa5, 0x31, 0x13, 0x05, 0x66, 0xa9, 0xc8, 0x58, 0x92, 0x90, 0x4c, + 0x82, 0x8c, 0x7a, 0xda, 0x64, 0x7f, 0x26, 0x93, 0x36, 0xe3, 0x42, 0xca, 0xe5, 0x37, 0x17, 0x3a, + 0x17, 0x45, 0xb0, 0x7c, 0x94, 0x23, 0xbe, 0x15, 0x48, 0x10, 0xf8, 0xcd, 0x00, 0xe6, 0xc8, 0x3e, + 0xd4, 0xf8, 0x21, 0x97, 0x93, 0xa6, 0x51, 0x35, 0x6a, 0x4b, 0xbb, 0x47, 0xee, 0x9c, 0x2b, 0x77, + 0x1b, 0x43, 0xc3, 0xf1, 0x5c, 0xfe, 0xd3, 0xf3, 0xbe, 0x5d, 0x18, 0xf4, 0x6d, 0xfb, 0x14, 0x75, + 0x92, 0x03, 0xe7, 0xae, 0xb4, 0x4e, 0xb0, 0x85, 0xa7, 0x1a, 0xc0, 0xcf, 0x06, 0x80, 0x72, 0x31, + 0x13, 0x98, 0x45, 0x85, 0x59, 0x9f, 0x1b, 0xf3, 0x15, 0xe3, 0xe2, 0x06, 0xe0, 0x13, 0x0d, 0xf8, + 0x30, 0x07, 0xbc, 0x9d, 0xca, 0x09, 0xd6, 0xda, 0x13, 0x22, 0xe7, 0x7b, 0x09, 0x6c, 0x4d, 0x5f, + 0x30, 0x3c, 0x33, 0xc0, 0x7d, 0x84, 0x05, 0xed, 0x91, 0x10, 0xb7, 0x51, 0x9a, 0x92, 0x84, 0x9b, + 0x46, 0xb5, 0x54, 0x5b, 0xda, 0x7d, 0x31, 0x37, 0x6c, 0x5d, 0xf9, 0x34, 0x72, 0x1b, 0xdf, 0xd2, + 0xa4, 0x5b, 0x39, 0xe9, 0x44, 0x12, 0x27, 0x58, 0x45, 0xe3, 0xe1, 0x1c, 0x7e, 0x31, 0xc0, 0xfa, + 0x94, 0x04, 0x66, 0x51, 0xd1, 0xbc, 0x9e, 0x9b, 0x26, 0x20, 0x31, 0xe5, 0x82, 0x64, 0x24, 0x6a, + 0x0e, 0x03, 0xeb, 0x79, 0x9c, 0xef, 0x68, 0xb6, 0x4a, 0xce, 0x36, 0xc5, 0xc9, 0x09, 0x20, 0x9d, + 0x94, 0x71, 0xb8, 0x01, 0xfe, 0xeb, 0xb2, 0x4c, 0x70, 0xb3, 0x54, 0x2d, 0xd5, 0xca, 0x41, 0xde, + 0x81, 0xef, 0xc1, 0x62, 0x17, 0x65, 0xa8, 0xc3, 0xcd, 0x05, 0x55, 0xe6, 0x83, 0xd9, 0x58, 0xc7, + 0x7e, 0x99, 0xde, 0x8e, 0xfb, 0x46, 0x39, 0xf8, 0x0b, 0x92, 0x2c, 0xd0, 0x7e, 0xce, 0xd7, 0x12, + 0x58, 0x9b, 0x3c, 0x02, 0xff, 0x4a, 0x36, 0x57, 0xc9, 0x20, 0x58, 0x90, 0x55, 0x32, 0x4b, 0x55, + 0xa3, 0x56, 0x0e, 0x54, 0x1b, 0x06, 0x13, 0x05, 0xdb, 0x9b, 0x8d, 0x54, 0x5d, 0x52, 0x77, 0x95, + 0xea, 0xac, 0x08, 0x56, 0x6e, 0xec, 0x26, 0x3c, 0x04, 0x2b, 0x98, 0xa5, 0x29, 0xc1, 0xd2, 0x31, + 0xa4, 0x91, 0xba, 0xab, 0xca, 0xbe, 0x39, 0xe8, 0xdb, 0x1b, 0xc3, 0xeb, 0x65, 0x34, 0xed, 0x04, + 0xcb, 0xa3, 0x7e, 0x33, 0x82, 0xcf, 0xc0, 0xff, 0x12, 0x56, 0x0a, 0x8b, 0x4a, 0x08, 0x07, 0x7d, + 0x7b, 0x35, 0x17, 0xea, 0x09, 0x27, 0x58, 0x94, 0xad, 0x66, 0x04, 0xf7, 0x00, 0xd0, 0x65, 0x92, + 0xf1, 0x6a, 0xad, 0xfe, 0xe6, 0xa0, 0x6f, 0x3f, 0xd0, 0x89, 0x86, 0x73, 0x4e, 0x50, 0xd6, 0x9d, + 0x66, 0x04, 0xdf, 0x81, 0x4d, 0xca, 0xc3, 0x0e, 0x8d, 0xa2, 0x84, 0x7c, 0x44, 0x19, 0x09, 0x49, + 0x8a, 0x5a, 0x09, 0x89, 0xd4, 0xb6, 0xdc, 0xf3, 0xab, 0x83, 0xbe, 0xfd, 0x58, 0x6f, 0xf7, 0xb4, + 0x30, 0x27, 0x58, 0xa7, 0xfc, 0x78, 0x38, 0xfc, 0x52, 0x8f, 0xfe, 0x30, 0xc0, 0xa3, 0x3f, 0x54, + 0xf2, 0xaf, 0xee, 0x4b, 0x43, 0xfe, 0x2a, 0x2a, 0x6d, 0x88, 0xa2, 0x28, 0x23, 0x9c, 0xeb, 0xcd, + 0xa9, 0x8c, 0x1f, 0xf3, 0x1b, 0x01, 0xea, 0x98, 0xab, 0x91, 0x7a, 0x3e, 0xe0, 0xc7, 0xe7, 0x97, + 0x96, 0x71, 0x71, 0x69, 0x19, 0xbf, 0x2e, 0x2d, 0xe3, 0xd3, 0x95, 0x55, 0xb8, 0xb8, 0xb2, 0x0a, + 0x3f, 0xaf, 0xac, 0xc2, 0x87, 0xe3, 0x98, 0x8a, 0xf6, 0x49, 0xcb, 0xc5, 0xac, 0xe3, 0x61, 0xc6, + 0x3b, 0x8c, 0xcb, 0x27, 0x78, 0x3b, 0x66, 0x5e, 0x6f, 0xdf, 0xeb, 0xb0, 0xe8, 0x24, 0x21, 0x5c, + 0x3e, 0x82, 0xdc, 0xdb, 0xdd, 0xdf, 0x1e, 0x1d, 0xa9, 0xed, 0x5b, 0x4f, 0xb9, 0x38, 0xed, 0x12, + 0xde, 0x5a, 0x54, 0x2f, 0xe0, 0xf3, 0xdf, 0x01, 0x00, 0x00, 0xff, 0xff, 0x33, 0x7f, 0xe2, 0xc0, + 0x07, 0x08, 0x00, 0x00, } func (m *GenesisState) Marshal() (dAtA []byte, err error) { @@ -596,6 +608,16 @@ func (m *ActiveChannel) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.IsMiddlewareEnabled { + i-- + if m.IsMiddlewareEnabled { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x20 + } if len(m.ChannelId) > 0 { i -= len(m.ChannelId) copy(dAtA[i:], m.ChannelId) @@ -762,6 +784,9 @@ func (m *ActiveChannel) Size() (n int) { if l > 0 { n += 1 + l + sovGenesis(uint64(l)) } + if m.IsMiddlewareEnabled { + n += 2 + } return n } @@ -1399,6 +1424,26 @@ func (m *ActiveChannel) Unmarshal(dAtA []byte) error { } m.ChannelId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field IsMiddlewareEnabled", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.IsMiddlewareEnabled = bool(v != 0) default: iNdEx = preIndex skippy, err := skipGenesis(dAtA[iNdEx:]) diff --git a/modules/apps/27-interchain-accounts/types/genesis_test.go b/modules/apps/27-interchain-accounts/genesis/types/genesis_test.go similarity index 50% rename from modules/apps/27-interchain-accounts/types/genesis_test.go rename to modules/apps/27-interchain-accounts/genesis/types/genesis_test.go index be86e8595a4..d26ebff53a9 100644 --- a/modules/apps/27-interchain-accounts/types/genesis_test.go +++ b/modules/apps/27-interchain-accounts/genesis/types/genesis_test.go @@ -1,14 +1,29 @@ package types_test import ( - controllertypes "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/controller/types" - hosttypes "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/host/types" - "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/types" - ibctesting "github.com/cosmos/ibc-go/v4/testing" + "github.com/stretchr/testify/suite" + + controllertypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/controller/types" + genesistypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/genesis/types" + hosttypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/host/types" + icatypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types" + ibctesting "github.com/cosmos/ibc-go/v7/testing" +) + +var ( + // TestOwnerAddress defines a reusable bech32 address for testing purposes + TestOwnerAddress = "cosmos17dtl0mjt3t77kpuhg2edqzjpszulwhgzuj9ljs" + + // TestPortID defines a reusable port identifier for testing purposes + TestPortID, _ = icatypes.NewControllerPortID(TestOwnerAddress) ) -func (suite *TypesTestSuite) TestValidateGenesisState() { - var genesisState types.GenesisState +type GenesisTypesTestSuite struct { + suite.Suite +} + +func (suite *GenesisTypesTestSuite) TestValidateGenesisState() { + var genesisState genesistypes.GenesisState testCases := []struct { name string @@ -23,21 +38,21 @@ func (suite *TypesTestSuite) TestValidateGenesisState() { { "failed to validate - empty value", func() { - genesisState = types.GenesisState{} + genesisState = genesistypes.GenesisState{} }, false, }, { "failed to validate - invalid controller genesis", func() { - genesisState = *types.NewGenesisState(types.ControllerGenesisState{Ports: []string{"invalid|port"}}, types.DefaultHostGenesis()) + genesisState = *genesistypes.NewGenesisState(genesistypes.ControllerGenesisState{Ports: []string{"invalid|port"}}, genesistypes.DefaultHostGenesis()) }, false, }, { "failed to validate - invalid host genesis", func() { - genesisState = *types.NewGenesisState(types.DefaultControllerGenesis(), types.HostGenesisState{}) + genesisState = *genesistypes.NewGenesisState(genesistypes.DefaultControllerGenesis(), genesistypes.HostGenesisState{}) }, false, }, @@ -45,7 +60,7 @@ func (suite *TypesTestSuite) TestValidateGenesisState() { for _, tc := range testCases { suite.Run(tc.name, func() { - genesisState = *types.DefaultGenesis() + genesisState = *genesistypes.DefaultGenesis() tc.malleate() // malleate mutates test data @@ -60,8 +75,8 @@ func (suite *TypesTestSuite) TestValidateGenesisState() { } } -func (suite *TypesTestSuite) TestValidateControllerGenesisState() { - var genesisState types.ControllerGenesisState +func (suite *GenesisTypesTestSuite) TestValidateControllerGenesisState() { + var genesisState genesistypes.ControllerGenesisState testCases := []struct { name string @@ -76,91 +91,91 @@ func (suite *TypesTestSuite) TestValidateControllerGenesisState() { { "failed to validate active channel - invalid port identifier", func() { - activeChannels := []types.ActiveChannel{ + activeChannels := []genesistypes.ActiveChannel{ { PortId: "invalid|port", ChannelId: ibctesting.FirstChannelID, }, } - genesisState = types.NewControllerGenesisState(activeChannels, []types.RegisteredInterchainAccount{}, []string{}, controllertypes.DefaultParams()) + genesisState = genesistypes.NewControllerGenesisState(activeChannels, []genesistypes.RegisteredInterchainAccount{}, []string{}, controllertypes.DefaultParams()) }, false, }, { "failed to validate active channel - invalid channel identifier", func() { - activeChannels := []types.ActiveChannel{ + activeChannels := []genesistypes.ActiveChannel{ { PortId: TestPortID, ChannelId: "invalid|channel", }, } - genesisState = types.NewControllerGenesisState(activeChannels, []types.RegisteredInterchainAccount{}, []string{}, controllertypes.DefaultParams()) + genesisState = genesistypes.NewControllerGenesisState(activeChannels, []genesistypes.RegisteredInterchainAccount{}, []string{}, controllertypes.DefaultParams()) }, false, }, { "failed to validate registered account - invalid port identifier", func() { - activeChannels := []types.ActiveChannel{ + activeChannels := []genesistypes.ActiveChannel{ { PortId: TestPortID, ChannelId: ibctesting.FirstChannelID, }, } - registeredAccounts := []types.RegisteredInterchainAccount{ + registeredAccounts := []genesistypes.RegisteredInterchainAccount{ { PortId: "invalid|port", AccountAddress: TestOwnerAddress, }, } - genesisState = types.NewControllerGenesisState(activeChannels, registeredAccounts, []string{}, controllertypes.DefaultParams()) + genesisState = genesistypes.NewControllerGenesisState(activeChannels, registeredAccounts, []string{}, controllertypes.DefaultParams()) }, false, }, { "failed to validate registered account - invalid owner address", func() { - activeChannels := []types.ActiveChannel{ + activeChannels := []genesistypes.ActiveChannel{ { PortId: TestPortID, ChannelId: ibctesting.FirstChannelID, }, } - registeredAccounts := []types.RegisteredInterchainAccount{ + registeredAccounts := []genesistypes.RegisteredInterchainAccount{ { PortId: TestPortID, AccountAddress: "", }, } - genesisState = types.NewControllerGenesisState(activeChannels, registeredAccounts, []string{}, controllertypes.DefaultParams()) + genesisState = genesistypes.NewControllerGenesisState(activeChannels, registeredAccounts, []string{}, controllertypes.DefaultParams()) }, false, }, { "failed to validate controller ports - invalid port identifier", func() { - activeChannels := []types.ActiveChannel{ + activeChannels := []genesistypes.ActiveChannel{ { PortId: TestPortID, ChannelId: ibctesting.FirstChannelID, }, } - registeredAccounts := []types.RegisteredInterchainAccount{ + registeredAccounts := []genesistypes.RegisteredInterchainAccount{ { PortId: TestPortID, AccountAddress: TestOwnerAddress, }, } - genesisState = types.NewControllerGenesisState(activeChannels, registeredAccounts, []string{"invalid|port"}, controllertypes.DefaultParams()) + genesisState = genesistypes.NewControllerGenesisState(activeChannels, registeredAccounts, []string{"invalid|port"}, controllertypes.DefaultParams()) }, false, }, @@ -168,7 +183,7 @@ func (suite *TypesTestSuite) TestValidateControllerGenesisState() { for _, tc := range testCases { suite.Run(tc.name, func() { - genesisState = types.DefaultControllerGenesis() + genesisState = genesistypes.DefaultControllerGenesis() tc.malleate() // malleate mutates test data @@ -183,8 +198,8 @@ func (suite *TypesTestSuite) TestValidateControllerGenesisState() { } } -func (suite *TypesTestSuite) TestValidateHostGenesisState() { - var genesisState types.HostGenesisState +func (suite *GenesisTypesTestSuite) TestValidateHostGenesisState() { + var genesisState genesistypes.HostGenesisState testCases := []struct { name string @@ -199,91 +214,91 @@ func (suite *TypesTestSuite) TestValidateHostGenesisState() { { "failed to validate active channel - invalid port identifier", func() { - activeChannels := []types.ActiveChannel{ + activeChannels := []genesistypes.ActiveChannel{ { PortId: "invalid|port", ChannelId: ibctesting.FirstChannelID, }, } - genesisState = types.NewHostGenesisState(activeChannels, []types.RegisteredInterchainAccount{}, types.PortID, hosttypes.DefaultParams()) + genesisState = genesistypes.NewHostGenesisState(activeChannels, []genesistypes.RegisteredInterchainAccount{}, icatypes.HostPortID, hosttypes.DefaultParams()) }, false, }, { "failed to validate active channel - invalid channel identifier", func() { - activeChannels := []types.ActiveChannel{ + activeChannels := []genesistypes.ActiveChannel{ { PortId: TestPortID, ChannelId: "invalid|channel", }, } - genesisState = types.NewHostGenesisState(activeChannels, []types.RegisteredInterchainAccount{}, types.PortID, hosttypes.DefaultParams()) + genesisState = genesistypes.NewHostGenesisState(activeChannels, []genesistypes.RegisteredInterchainAccount{}, icatypes.HostPortID, hosttypes.DefaultParams()) }, false, }, { "failed to validate registered account - invalid port identifier", func() { - activeChannels := []types.ActiveChannel{ + activeChannels := []genesistypes.ActiveChannel{ { PortId: TestPortID, ChannelId: ibctesting.FirstChannelID, }, } - registeredAccounts := []types.RegisteredInterchainAccount{ + registeredAccounts := []genesistypes.RegisteredInterchainAccount{ { PortId: "invalid|port", AccountAddress: TestOwnerAddress, }, } - genesisState = types.NewHostGenesisState(activeChannels, registeredAccounts, types.PortID, hosttypes.DefaultParams()) + genesisState = genesistypes.NewHostGenesisState(activeChannels, registeredAccounts, icatypes.HostPortID, hosttypes.DefaultParams()) }, false, }, { "failed to validate registered account - invalid owner address", func() { - activeChannels := []types.ActiveChannel{ + activeChannels := []genesistypes.ActiveChannel{ { PortId: TestPortID, ChannelId: ibctesting.FirstChannelID, }, } - registeredAccounts := []types.RegisteredInterchainAccount{ + registeredAccounts := []genesistypes.RegisteredInterchainAccount{ { PortId: TestPortID, AccountAddress: "", }, } - genesisState = types.NewHostGenesisState(activeChannels, registeredAccounts, types.PortID, hosttypes.DefaultParams()) + genesisState = genesistypes.NewHostGenesisState(activeChannels, registeredAccounts, icatypes.HostPortID, hosttypes.DefaultParams()) }, false, }, { "failed to validate controller ports - invalid port identifier", func() { - activeChannels := []types.ActiveChannel{ + activeChannels := []genesistypes.ActiveChannel{ { PortId: TestPortID, ChannelId: ibctesting.FirstChannelID, }, } - registeredAccounts := []types.RegisteredInterchainAccount{ + registeredAccounts := []genesistypes.RegisteredInterchainAccount{ { PortId: TestPortID, AccountAddress: TestOwnerAddress, }, } - genesisState = types.NewHostGenesisState(activeChannels, registeredAccounts, "invalid|port", hosttypes.DefaultParams()) + genesisState = genesistypes.NewHostGenesisState(activeChannels, registeredAccounts, "invalid|port", hosttypes.DefaultParams()) }, false, }, @@ -291,7 +306,7 @@ func (suite *TypesTestSuite) TestValidateHostGenesisState() { for _, tc := range testCases { suite.Run(tc.name, func() { - genesisState = types.DefaultHostGenesis() + genesisState = genesistypes.DefaultHostGenesis() tc.malleate() // malleate mutates test data diff --git a/modules/apps/27-interchain-accounts/host/client/cli/cli.go b/modules/apps/27-interchain-accounts/host/client/cli/cli.go index 2ae88a48b70..23d2dc2e83a 100644 --- a/modules/apps/27-interchain-accounts/host/client/cli/cli.go +++ b/modules/apps/27-interchain-accounts/host/client/cli/cli.go @@ -1,6 +1,7 @@ package cli import ( + "github.com/cosmos/cosmos-sdk/client" "github.com/spf13/cobra" ) @@ -8,7 +9,7 @@ import ( func GetQueryCmd() *cobra.Command { queryCmd := &cobra.Command{ Use: "host", - Short: "interchain-accounts host subcommands", + Short: "IBC interchain accounts host query subcommands", DisableFlagParsing: true, SuggestionsMinimumDistance: 2, } @@ -20,3 +21,20 @@ func GetQueryCmd() *cobra.Command { return queryCmd } + +// NewTxCmd creates and returns the tx command +func NewTxCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "host", + Short: "IBC interchain accounts host transaction subcommands", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + cmd.AddCommand( + generatePacketDataCmd(), + ) + + return cmd +} diff --git a/modules/apps/27-interchain-accounts/host/client/cli/query.go b/modules/apps/27-interchain-accounts/host/client/cli/query.go index a5a6637ce9a..95ff6a77fc8 100644 --- a/modules/apps/27-interchain-accounts/host/client/cli/query.go +++ b/modules/apps/27-interchain-accounts/host/client/cli/query.go @@ -12,10 +12,10 @@ import ( "github.com/spf13/cobra" abci "github.com/tendermint/tendermint/abci/types" - "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" + "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/host/types" + icatypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" ) // GetCmdParams returns the command handler for the host submodule parameter querying. @@ -61,7 +61,7 @@ func GetCmdPacketEvents() *cobra.Command { return err } - channelID, portID := args[0], icatypes.PortID + channelID, portID := args[0], icatypes.HostPortID if err := host.ChannelIdentifierValidator(channelID); err != nil { return err } diff --git a/modules/apps/27-interchain-accounts/host/client/cli/tx.go b/modules/apps/27-interchain-accounts/host/client/cli/tx.go new file mode 100644 index 00000000000..8d74f1ce66c --- /dev/null +++ b/modules/apps/27-interchain-accounts/host/client/cli/tx.go @@ -0,0 +1,148 @@ +package cli + +import ( + "encoding/json" + "fmt" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/version" + "github.com/cosmos/gogoproto/proto" + "github.com/spf13/cobra" + + icatypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types" +) + +const ( + memoFlag string = "memo" +) + +func generatePacketDataCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "generate-packet-data [message]", + Short: "Generates ICA packet data.", + Long: `generate-packet-data accepts a message string and serializes it +into packet data which is outputted to stdout. It can be used in conjunction with send-tx" +which submits pre-built packet data containing messages to be executed on the host chain. +`, + Example: fmt.Sprintf(`%s tx interchain-accounts host generate-packet-data '{ + "@type":"/cosmos.bank.v1beta1.MsgSend", + "from_address":"cosmos15ccshhmp0gsx29qpqq6g4zmltnnvgmyu9ueuadh9y2nc5zj0szls5gtddz", + "to_address":"cosmos10h9stc5v6ntgeygf5xf945njqq5h32r53uquvw", + "amount": [ + { + "denom": "stake", + "amount": "1000" + } + ] +}' --memo memo + + +%s tx interchain-accounts host generate-packet-data '[{ + "@type":"/cosmos.bank.v1beta1.MsgSend", + "from_address":"cosmos15ccshhmp0gsx29qpqq6g4zmltnnvgmyu9ueuadh9y2nc5zj0szls5gtddz", + "to_address":"cosmos10h9stc5v6ntgeygf5xf945njqq5h32r53uquvw", + "amount": [ + { + "denom": "stake", + "amount": "1000" + } + ] +}, +{ + "@type": "/cosmos.staking.v1beta1.MsgDelegate", + "delegator_address": "cosmos15ccshhmp0gsx29qpqq6g4zmltnnvgmyu9ueuadh9y2nc5zj0szls5gtddz", + "validator_address": "cosmosvaloper1qnk2n4nlkpw9xfqntladh74w6ujtulwnmxnh3k", + "amount": { + "denom": "stake", + "amount": "1000" + } +}]'`, version.AppName, version.AppName), + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + cdc := codec.NewProtoCodec(clientCtx.InterfaceRegistry) + + memo, err := cmd.Flags().GetString(memoFlag) + if err != nil { + return err + } + + packetDataBytes, err := generatePacketData(cdc, []byte(args[0]), memo) + if err != nil { + return err + } + + cmd.Println(string(packetDataBytes)) + + return nil + }, + } + + cmd.Flags().String(memoFlag, "", "an optional memo to be included in the interchain account packet data") + return cmd +} + +// generatePacketData takes in message bytes and a memo and serializes the message into an +// instance of InterchainAccountPacketData which is returned as bytes. +func generatePacketData(cdc *codec.ProtoCodec, msgBytes []byte, memo string) ([]byte, error) { + protoMessages, err := convertBytesIntoProtoMessages(cdc, msgBytes) + if err != nil { + return nil, err + } + + return generateIcaPacketDataFromProtoMessages(cdc, protoMessages, memo) +} + +// convertBytesIntoProtoMessages returns a list of proto messages from bytes. The bytes can be in the form of a single +// message, or a json array of messages. +func convertBytesIntoProtoMessages(cdc *codec.ProtoCodec, msgBytes []byte) ([]proto.Message, error) { + var rawMessages []json.RawMessage + if err := json.Unmarshal(msgBytes, &rawMessages); err != nil { + // if we fail to unmarshal a list of messages, we assume we are just dealing with a single message. + // in this case we return a list of a single item. + var msg sdk.Msg + if err := cdc.UnmarshalInterfaceJSON(msgBytes, &msg); err != nil { + return nil, err + } + + return []proto.Message{msg}, nil + } + + sdkMessages := make([]proto.Message, len(rawMessages)) + for i, anyJSON := range rawMessages { + var msg sdk.Msg + if err := cdc.UnmarshalInterfaceJSON(anyJSON, &msg); err != nil { + return nil, err + } + + sdkMessages[i] = msg + } + + return sdkMessages, nil +} + +// generateIcaPacketDataFromProtoMessages generates ica packet data as bytes from a given set of proto encoded sdk messages and a memo. +func generateIcaPacketDataFromProtoMessages(cdc *codec.ProtoCodec, sdkMessages []proto.Message, memo string) ([]byte, error) { + icaPacketDataBytes, err := icatypes.SerializeCosmosTx(cdc, sdkMessages) + if err != nil { + return nil, err + } + + icaPacketData := icatypes.InterchainAccountPacketData{ + Type: icatypes.EXECUTE_TX, + Data: icaPacketDataBytes, + Memo: memo, + } + + if err := icaPacketData.ValidateBasic(); err != nil { + return nil, err + } + + return cdc.MarshalJSON(&icaPacketData) +} diff --git a/modules/apps/27-interchain-accounts/host/client/cli/tx_test.go b/modules/apps/27-interchain-accounts/host/client/cli/tx_test.go new file mode 100644 index 00000000000..b8a617e224e --- /dev/null +++ b/modules/apps/27-interchain-accounts/host/client/cli/tx_test.go @@ -0,0 +1,155 @@ +package cli + +import ( + "fmt" + "testing" + + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/stretchr/testify/require" + + icatypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types" +) + +const msgDelegateMessage = `{ + "@type": "/cosmos.staking.v1beta1.MsgDelegate", + "delegator_address": "cosmos15ccshhmp0gsx29qpqq6g4zmltnnvgmyu9ueuadh9y2nc5zj0szls5gtddz", + "validator_address": "cosmosvaloper1qnk2n4nlkpw9xfqntladh74w6ujtulwnmxnh3k", + "amount": { + "denom": "stake", + "amount": "1000" + } +}` + +const bankSendMessage = `{ + "@type":"/cosmos.bank.v1beta1.MsgSend", + "from_address":"cosmos15ccshhmp0gsx29qpqq6g4zmltnnvgmyu9ueuadh9y2nc5zj0szls5gtddz", + "to_address":"cosmos10h9stc5v6ntgeygf5xf945njqq5h32r53uquvw", + "amount": [ + { + "denom": "stake", + "amount": "1000" + } + ] +}` + +var multiMsg = fmt.Sprintf("[ %s, %s ]", msgDelegateMessage, bankSendMessage) + +func TestGeneratePacketData(t *testing.T) { + tests := []struct { + name string + memo string + expectedPass bool + message string + registerInterfaceFn func(registry codectypes.InterfaceRegistry) + assertionFn func(t *testing.T, msgs []sdk.Msg) + }{ + { + name: "packet data generation succeeds (MsgDelegate & MsgSend)", + memo: "", + expectedPass: true, + message: multiMsg, + registerInterfaceFn: func(registry codectypes.InterfaceRegistry) { + stakingtypes.RegisterInterfaces(registry) + banktypes.RegisterInterfaces(registry) + }, + assertionFn: func(t *testing.T, msgs []sdk.Msg) { + assertMsgDelegate(t, msgs[0]) + assertMsgBankSend(t, msgs[1]) + }, + }, + { + name: "packet data generation succeeds (MsgDelegate)", + memo: "non-empty-memo", + expectedPass: true, + message: msgDelegateMessage, + registerInterfaceFn: stakingtypes.RegisterInterfaces, + assertionFn: func(t *testing.T, msgs []sdk.Msg) { + assertMsgDelegate(t, msgs[0]) + }, + }, + { + name: "packet data generation succeeds (MsgSend)", + memo: "non-empty-memo", + expectedPass: true, + message: bankSendMessage, + registerInterfaceFn: banktypes.RegisterInterfaces, + assertionFn: func(t *testing.T, msgs []sdk.Msg) { + assertMsgBankSend(t, msgs[0]) + }, + }, + { + name: "empty memo is valid", + memo: "", + expectedPass: true, + message: msgDelegateMessage, + registerInterfaceFn: stakingtypes.RegisterInterfaces, + assertionFn: nil, + }, + { + name: "invalid message string", + expectedPass: false, + message: "", + }, + } + + for _, tc := range tests { + tc := tc + ir := codectypes.NewInterfaceRegistry() + if tc.registerInterfaceFn != nil { + tc.registerInterfaceFn(ir) + } + + cdc := codec.NewProtoCodec(ir) + + t.Run(tc.name, func(t *testing.T) { + bz, err := generatePacketData(cdc, []byte(tc.message), tc.memo) + + if tc.expectedPass { + require.NoError(t, err) + require.NotNil(t, bz) + + packetData := icatypes.InterchainAccountPacketData{} + err = cdc.UnmarshalJSON(bz, &packetData) + require.NoError(t, err) + + require.Equal(t, icatypes.EXECUTE_TX, packetData.Type) + require.Equal(t, tc.memo, packetData.Memo) + + data := packetData.Data + messages, err := icatypes.DeserializeCosmosTx(cdc, data) + + require.NoError(t, err) + require.NotNil(t, messages) + + if tc.assertionFn != nil { + tc.assertionFn(t, messages) + } + } else { + require.Error(t, err) + require.Nil(t, bz) + } + }) + } +} + +func assertMsgBankSend(t *testing.T, msg sdk.Msg) { + bankSendMsg, ok := msg.(*banktypes.MsgSend) + require.True(t, ok) + require.Equal(t, "cosmos15ccshhmp0gsx29qpqq6g4zmltnnvgmyu9ueuadh9y2nc5zj0szls5gtddz", bankSendMsg.FromAddress) + require.Equal(t, "cosmos10h9stc5v6ntgeygf5xf945njqq5h32r53uquvw", bankSendMsg.ToAddress) + require.Equal(t, "stake", bankSendMsg.Amount.GetDenomByIndex(0)) + require.Equal(t, uint64(1000), bankSendMsg.Amount[0].Amount.Uint64()) +} + +func assertMsgDelegate(t *testing.T, msg sdk.Msg) { + msgDelegate, ok := msg.(*stakingtypes.MsgDelegate) + require.True(t, ok) + require.Equal(t, "cosmos15ccshhmp0gsx29qpqq6g4zmltnnvgmyu9ueuadh9y2nc5zj0szls5gtddz", msgDelegate.DelegatorAddress) + require.Equal(t, "cosmosvaloper1qnk2n4nlkpw9xfqntladh74w6ujtulwnmxnh3k", msgDelegate.ValidatorAddress) + require.Equal(t, "stake", msgDelegate.Amount.Denom) + require.Equal(t, uint64(1000), msgDelegate.Amount.Amount.Uint64()) +} diff --git a/modules/apps/27-interchain-accounts/host/ibc_module.go b/modules/apps/27-interchain-accounts/host/ibc_module.go index 7cf0deca969..1620ce87bc5 100644 --- a/modules/apps/27-interchain-accounts/host/ibc_module.go +++ b/modules/apps/27-interchain-accounts/host/ibc_module.go @@ -1,15 +1,18 @@ package host import ( + "fmt" + + errorsmod "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/host/keeper" - "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" - ibcexported "github.com/cosmos/ibc-go/v4/modules/core/exported" + ibcerrors "github.com/cosmos/ibc-go/v7/internal/errors" + "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/host/keeper" + "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/host/types" + icatypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + ibcexported "github.com/cosmos/ibc-go/v7/modules/core/exported" ) // IBCModule implements the ICS26 interface for interchain accounts host chains @@ -35,7 +38,7 @@ func (im IBCModule) OnChanOpenInit( counterparty channeltypes.Counterparty, version string, ) (string, error) { - return "", sdkerrors.Wrap(icatypes.ErrInvalidChannelFlow, "channel handshake must be initiated by controller chain") + return "", errorsmod.Wrap(icatypes.ErrInvalidChannelFlow, "channel handshake must be initiated by controller chain") } // OnChanOpenTry implements the IBCModule interface @@ -64,7 +67,7 @@ func (im IBCModule) OnChanOpenAck( counterpartyChannelID string, counterpartyVersion string, ) error { - return sdkerrors.Wrap(icatypes.ErrInvalidChannelFlow, "channel handshake must be initiated by controller chain") + return errorsmod.Wrap(icatypes.ErrInvalidChannelFlow, "channel handshake must be initiated by controller chain") } // OnChanOpenAck implements the IBCModule interface @@ -87,7 +90,7 @@ func (im IBCModule) OnChanCloseInit( channelID string, ) error { // Disallow user-initiated channel closing for interchain account channels - return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "user cannot close channel") + return errorsmod.Wrap(ibcerrors.ErrInvalidRequest, "user cannot close channel") } // OnChanCloseConfirm implements the IBCModule interface @@ -105,7 +108,9 @@ func (im IBCModule) OnRecvPacket( packet channeltypes.Packet, _ sdk.AccAddress, ) ibcexported.Acknowledgement { + logger := im.keeper.Logger(ctx) if !im.keeper.IsHostEnabled(ctx) { + logger.Info("host submodule is disabled") return channeltypes.NewErrorAcknowledgement(types.ErrHostSubModuleDisabled) } @@ -113,6 +118,9 @@ func (im IBCModule) OnRecvPacket( ack := channeltypes.NewResultAcknowledgement(txResponse) if err != nil { ack = channeltypes.NewErrorAcknowledgement(err) + logger.Error(fmt.Sprintf("%s sequence %d", err.Error(), packet.Sequence)) + } else { + logger.Info("successfully handled packet sequence: %d", packet.Sequence) } // Emit an event indicating a successful or failed acknowledgement. @@ -129,7 +137,7 @@ func (im IBCModule) OnAcknowledgementPacket( acknowledgement []byte, relayer sdk.AccAddress, ) error { - return sdkerrors.Wrap(icatypes.ErrInvalidChannelFlow, "cannot receive acknowledgement on a host channel end, a host chain does not send a packet over the channel") + return errorsmod.Wrap(icatypes.ErrInvalidChannelFlow, "cannot receive acknowledgement on a host channel end, a host chain does not send a packet over the channel") } // OnTimeoutPacket implements the IBCModule interface @@ -138,5 +146,5 @@ func (im IBCModule) OnTimeoutPacket( packet channeltypes.Packet, relayer sdk.AccAddress, ) error { - return sdkerrors.Wrap(icatypes.ErrInvalidChannelFlow, "cannot cause a packet timeout on a host channel end, a host chain does not send a packet over the channel") + return errorsmod.Wrap(icatypes.ErrInvalidChannelFlow, "cannot cause a packet timeout on a host channel end, a host chain does not send a packet over the channel") } 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 b3e19a00ad2..ff3718ad9be 100644 --- a/modules/apps/27-interchain-accounts/host/ibc_module_test.go +++ b/modules/apps/27-interchain-accounts/host/ibc_module_test.go @@ -4,36 +4,31 @@ import ( "fmt" "testing" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - "github.com/gogo/protobuf/proto" + "github.com/cosmos/gogoproto/proto" "github.com/stretchr/testify/suite" - "github.com/tendermint/tendermint/crypto" - - "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" - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" - "github.com/cosmos/ibc-go/v4/modules/core/exported" - ibctesting "github.com/cosmos/ibc-go/v4/testing" + + "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/host/types" + icatypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types" + feetypes "github.com/cosmos/ibc-go/v7/modules/apps/29-fee/types" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + "github.com/cosmos/ibc-go/v7/modules/core/exported" + ibctesting "github.com/cosmos/ibc-go/v7/testing" ) 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 = "cosmos17dtl0mjt3t77kpuhg2edqzjpszulwhgzuj9ljs" - // TestPortID defines a resuable port identifier for testing purposes + // TestPortID defines a reusable port identifier for testing purposes TestPortID, _ = icatypes.NewControllerPortID(TestOwnerAddress) - // TestVersion defines a resuable interchainaccounts version string for testing purposes + // TestVersion defines a reusable interchainaccounts version string for testing purposes TestVersion = string(icatypes.ModuleCdc.MustMarshalJSON(&icatypes.Metadata{ Version: icatypes.Version, ControllerConnectionId: ibctesting.FirstConnectionID, @@ -65,8 +60,8 @@ func (suite *InterchainAccountsTestSuite) SetupTest() { func NewICAPath(chainA, chainB *ibctesting.TestChain) *ibctesting.Path { path := ibctesting.NewPath(chainA, chainB) - path.EndpointA.ChannelConfig.PortID = icatypes.PortID - path.EndpointB.ChannelConfig.PortID = icatypes.PortID + path.EndpointA.ChannelConfig.PortID = icatypes.HostPortID + path.EndpointB.ChannelConfig.PortID = icatypes.HostPortID path.EndpointA.ChannelConfig.Order = channeltypes.ORDERED path.EndpointB.ChannelConfig.Order = channeltypes.ORDERED path.EndpointA.ChannelConfig.Version = TestVersion @@ -83,7 +78,7 @@ func RegisterInterchainAccount(endpoint *ibctesting.Endpoint, owner string) erro channelSequence := endpoint.Chain.App.GetIBCKeeper().ChannelKeeper.GetNextChannelSequence(endpoint.Chain.GetContext()) - if err := endpoint.Chain.GetSimApp().ICAControllerKeeper.RegisterInterchainAccount(endpoint.Chain.GetContext(), endpoint.ConnectionID, owner, TestVersion); err != nil { + if err := endpoint.Chain.GetSimApp().ICAControllerKeeper.RegisterInterchainAccount(endpoint.Chain.GetContext(), endpoint.ConnectionID, owner, endpoint.ChannelConfig.Version); err != nil { return err } @@ -147,6 +142,17 @@ func (suite *InterchainAccountsTestSuite) TestOnChanOpenTry() { { "success", func() {}, true, }, + { + "account address generation is block dependent", func() { + icaHostAccount := icatypes.GenerateAddress(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{})) @@ -213,6 +219,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) @@ -241,7 +251,8 @@ func (suite *InterchainAccountsTestSuite) TestChanOpenAck() { // commit state changes so proof can be created suite.chainA.NextBlock() - path.EndpointB.UpdateClient() + err = path.EndpointB.UpdateClient() + suite.Require().NoError(err) // query proof from ChainA channelKey := host.ChannelKey(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) @@ -436,7 +447,7 @@ func (suite *InterchainAccountsTestSuite) TestOnRecvPacket() { ToAddress: suite.chainB.SenderAccount.GetAddress().String(), Amount: amount, } - data, err := icatypes.SerializeCosmosTx(suite.chainA.Codec, []sdk.Msg{msg}) + data, err := icatypes.SerializeCosmosTx(suite.chainA.Codec, []proto.Message{msg}) suite.Require().NoError(err) icaPacketData := icatypes.InterchainAccountPacketData{ @@ -446,16 +457,11 @@ func (suite *InterchainAccountsTestSuite) TestOnRecvPacket() { packetData = icaPacketData.GetBytes() // build expected ack - msgResponseBz, err := proto.Marshal(&banktypes.MsgSendResponse{}) + protoAny, err := codectypes.NewAnyWithValue(&banktypes.MsgSendResponse{}) suite.Require().NoError(err) - msgData := &sdk.MsgData{ - MsgType: sdk.MsgTypeURL(msg), - Data: msgResponseBz, - } - expectedTxResponse, err := proto.Marshal(&sdk.TxMsgData{ - Data: []*sdk.MsgData{msgData}, + MsgResponses: []*codectypes.Any{protoAny}, }) suite.Require().NoError(err) @@ -531,7 +537,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) @@ -584,7 +590,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) @@ -610,28 +616,47 @@ 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) + + // use a fee enabled version to cover unwrapping channel version code paths + feeMetadata := feetypes.Metadata{ + FeeVersion: feetypes.Version, + AppVersion: TestVersion, + } + + feeICAVersion := string(feetypes.ModuleCdc.MustMarshalJSON(&feeMetadata)) + + path.EndpointA.ChannelConfig.Version = feeICAVersion + path.EndpointB.ChannelConfig.Version = feeICAVersion + 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(), Amount: tokenAmt, } - data, err := icatypes.SerializeCosmosTx(suite.chainA.GetSimApp().AppCodec(), []sdk.Msg{msg}) + data, err := icatypes.SerializeCosmosTx(suite.chainA.GetSimApp().AppCodec(), []proto.Message{msg}) suite.Require().NoError(err) icaPacketData := icatypes.InterchainAccountPacketData{ @@ -642,12 +667,11 @@ func (suite *InterchainAccountsTestSuite) TestControlAccountAfterChannelClose() params := types.NewParams(true, []string{sdk.MsgTypeURL(msg)}) suite.chainB.GetSimApp().ICAHostKeeper.SetParams(suite.chainB.GetContext(), params) - chanCap, ok := suite.chainA.GetSimApp().ScopedICAMockKeeper.GetCapability(path.EndpointA.Chain.GetContext(), host.ChannelCapabilityPath(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID)) - suite.Require().True(ok) - - _, err = suite.chainA.GetSimApp().ICAControllerKeeper.SendTx(suite.chainA.GetContext(), chanCap, ibctesting.FirstConnectionID, path.EndpointA.ChannelConfig.PortID, icaPacketData, ^uint64(0)) + //nolint: staticcheck // SA1019: ibctesting.FirstConnectionID is deprecated: use path.EndpointA.ConnectionID instead. (staticcheck) + _, err = suite.chainA.GetSimApp().ICAControllerKeeper.SendTx(suite.chainA.GetContext(), nil, ibctesting.FirstConnectionID, path.EndpointA.ChannelConfig.PortID, icaPacketData, ^uint64(0)) + suite.Require().NoError(err) + err = path.EndpointB.UpdateClient() suite.Require().NoError(err) - path.EndpointB.UpdateClient() // relay the packet packetRelay := channeltypes.NewPacket(icaPacketData.GetBytes(), 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, clienttypes.ZeroHeight(), ^uint64(0)) @@ -658,13 +682,12 @@ 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() + err = path.EndpointA.SetChannelState(channeltypes.CLOSED) suite.Require().NoError(err) - err = path.EndpointB.SetChannelClosed() + err = path.EndpointB.SetChannelState(channeltypes.CLOSED) suite.Require().NoError(err) // open a new channel on the same port @@ -672,20 +695,23 @@ func (suite *InterchainAccountsTestSuite) TestControlAccountAfterChannelClose() path.EndpointB.ChannelID = "" suite.coordinator.CreateChannels(path) - // try to control the interchain account again - chanCap, ok = suite.chainA.GetSimApp().ScopedICAMockKeeper.GetCapability(path.EndpointA.Chain.GetContext(), host.ChannelCapabilityPath(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID)) - suite.Require().True(ok) - - _, err = suite.chainA.GetSimApp().ICAControllerKeeper.SendTx(suite.chainA.GetContext(), chanCap, ibctesting.FirstConnectionID, path.EndpointA.ChannelConfig.PortID, icaPacketData, ^uint64(0)) + //nolint: staticcheck // SA1019: ibctesting.FirstConnectionID is deprecated: use path.EndpointA.ConnectionID instead. (staticcheck) + _, err = suite.chainA.GetSimApp().ICAControllerKeeper.SendTx(suite.chainA.GetContext(), nil, ibctesting.FirstConnectionID, path.EndpointA.ChannelConfig.PortID, icaPacketData, ^uint64(0)) + suite.Require().NoError(err) + err = path.EndpointB.UpdateClient() suite.Require().NoError(err) - path.EndpointB.UpdateClient() // relay the packet packetRelay = channeltypes.NewPacket(icaPacketData.GetBytes(), 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, clienttypes.ZeroHeight(), ^uint64(0)) 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) +} + +// 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 a4c8b511dda..3b02d3c7cae 100644 --- a/modules/apps/27-interchain-accounts/host/keeper/account.go +++ b/modules/apps/27-interchain-accounts/host/keeper/account.go @@ -1,18 +1,21 @@ package keeper import ( + errorsmod "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - icatypes "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/types" + icatypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types" ) -// 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) -func (k Keeper) RegisterInterchainAccount(ctx sdk.Context, connectionID, controllerPortID string, accAddress sdk.AccAddress) { +// 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.GenerateAddress(ctx, connectionID, controllerPortID) + if acc := k.accountKeeper.GetAccount(ctx, accAddress); acc != nil { - return + return nil, errorsmod.Wrapf(icatypes.ErrAccountAlreadyExist, "existing account for newly generated interchain account address %s", accAddress) } interchainAccount := icatypes.NewInterchainAccount( @@ -24,4 +27,6 @@ func (k Keeper) RegisterInterchainAccount(ctx sdk.Context, connectionID, control 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 038ed003748..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/cosmos/cosmos-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/events.go b/modules/apps/27-interchain-accounts/host/keeper/events.go index ead5d783864..6c320ace6c0 100644 --- a/modules/apps/27-interchain-accounts/host/keeper/events.go +++ b/modules/apps/27-interchain-accounts/host/keeper/events.go @@ -5,8 +5,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" - icatypes "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/types" - "github.com/cosmos/ibc-go/v4/modules/core/exported" + icatypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types" + "github.com/cosmos/ibc-go/v7/modules/core/exported" ) // EmitAcknowledgementEvent emits an event signalling a successful or failed acknowledgement and including the error diff --git a/modules/apps/27-interchain-accounts/host/keeper/genesis.go b/modules/apps/27-interchain-accounts/host/keeper/genesis.go index c3dff759f4d..6575bac1713 100644 --- a/modules/apps/27-interchain-accounts/host/keeper/genesis.go +++ b/modules/apps/27-interchain-accounts/host/keeper/genesis.go @@ -5,12 +5,13 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" - icatypes "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/types" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" + genesistypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/genesis/types" + icatypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" ) // InitGenesis initializes the interchain accounts host application state from a provided genesis state -func InitGenesis(ctx sdk.Context, keeper Keeper, state icatypes.HostGenesisState) { +func InitGenesis(ctx sdk.Context, keeper Keeper, state genesistypes.HostGenesisState) { if !keeper.IsBound(ctx, state.Port) { cap := keeper.BindPort(ctx, state.Port) if err := keeper.ClaimCapability(ctx, cap, host.PortPath(state.Port)); err != nil { @@ -30,11 +31,11 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, state icatypes.HostGenesisState } // ExportGenesis returns the interchain accounts host exported genesis -func ExportGenesis(ctx sdk.Context, keeper Keeper) icatypes.HostGenesisState { - return icatypes.NewHostGenesisState( +func ExportGenesis(ctx sdk.Context, keeper Keeper) genesistypes.HostGenesisState { + return genesistypes.NewHostGenesisState( keeper.GetAllActiveChannels(ctx), keeper.GetAllInterchainAccounts(ctx), - icatypes.PortID, + icatypes.HostPortID, keeper.GetParams(ctx), ) } 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..7851b730527 100644 --- a/modules/apps/27-interchain-accounts/host/keeper/genesis_test.go +++ b/modules/apps/27-interchain-accounts/host/keeper/genesis_test.go @@ -1,31 +1,34 @@ package keeper_test import ( - "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/host/keeper" - "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" - ibctesting "github.com/cosmos/ibc-go/v4/testing" + genesistypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/genesis/types" + "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/host/keeper" + "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/host/types" + icatypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types" + ibctesting "github.com/cosmos/ibc-go/v7/testing" ) func (suite *KeeperTestSuite) TestInitGenesis() { suite.SetupTest() - genesisState := icatypes.HostGenesisState{ - ActiveChannels: []icatypes.ActiveChannel{ + interchainAccAddr := icatypes.GenerateAddress(suite.chainB.GetContext(), ibctesting.FirstConnectionID, TestPortID) + + genesisState := genesistypes.HostGenesisState{ + ActiveChannels: []genesistypes.ActiveChannel{ { ConnectionId: ibctesting.FirstConnectionID, PortId: TestPortID, ChannelId: ibctesting.FirstChannelID, }, }, - InterchainAccounts: []icatypes.RegisteredInterchainAccount{ + InterchainAccounts: []genesistypes.RegisteredInterchainAccount{ { ConnectionId: ibctesting.FirstConnectionID, PortId: TestPortID, - AccountAddress: TestAccAddress.String(), + AccountAddress: interchainAccAddr.String(), }, }, - Port: icatypes.PortID, + Port: icatypes.HostPortID, } keeper.InitGenesis(suite.chainA.GetContext(), suite.chainA.GetSimApp().ICAHostKeeper, genesisState) @@ -36,7 +39,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,15 +55,18 @@ 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()) + suite.Require().Equal(icatypes.HostPortID, genesisState.GetPort()) expParams := types.DefaultParams() suite.Require().Equal(expParams, genesisState.GetParams()) diff --git a/modules/apps/27-interchain-accounts/host/keeper/grpc_query.go b/modules/apps/27-interchain-accounts/host/keeper/grpc_query.go index 65afdece331..8677c6b5754 100644 --- a/modules/apps/27-interchain-accounts/host/keeper/grpc_query.go +++ b/modules/apps/27-interchain-accounts/host/keeper/grpc_query.go @@ -5,7 +5,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/host/types" + "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/host/types" ) var _ types.QueryServer = Keeper{} diff --git a/modules/apps/27-interchain-accounts/host/keeper/grpc_query_test.go b/modules/apps/27-interchain-accounts/host/keeper/grpc_query_test.go index 44aae4b4e1f..c8698194a1e 100644 --- a/modules/apps/27-interchain-accounts/host/keeper/grpc_query_test.go +++ b/modules/apps/27-interchain-accounts/host/keeper/grpc_query_test.go @@ -3,7 +3,7 @@ package keeper_test import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/host/types" + "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/host/types" ) func (suite *KeeperTestSuite) TestQueryParams() { diff --git a/modules/apps/27-interchain-accounts/host/keeper/handshake.go b/modules/apps/27-interchain-accounts/host/keeper/handshake.go index 15a18fcea77..8f3f4a0ccff 100644 --- a/modules/apps/27-interchain-accounts/host/keeper/handshake.go +++ b/modules/apps/27-interchain-accounts/host/keeper/handshake.go @@ -2,15 +2,14 @@ package keeper import ( "fmt" - "strings" + errorsmod "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/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" + icatypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" ) // OnChanOpenTry performs basic validation of the ICA channel @@ -28,20 +27,16 @@ func (k Keeper) OnChanOpenTry( counterpartyVersion string, ) (string, error) { if order != channeltypes.ORDERED { - return "", sdkerrors.Wrapf(channeltypes.ErrInvalidChannelOrdering, "expected %s channel, got %s", channeltypes.ORDERED, order) + return "", errorsmod.Wrapf(channeltypes.ErrInvalidChannelOrdering, "expected %s channel, got %s", channeltypes.ORDERED, order) } - if portID != icatypes.PortID { - 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) + if portID != icatypes.HostPortID { + return "", errorsmod.Wrapf(icatypes.ErrInvalidHostPort, "expected %s, got %s", icatypes.HostPortID, 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") + return "", errorsmod.Wrapf(icatypes.ErrUnknownDataType, "cannot unmarshal ICS-27 interchain accounts metadata") } if err := icatypes.ValidateHostMetadata(ctx, k.channelKeeper, connectionHops, metadata); err != nil { @@ -56,24 +51,46 @@ func (k Keeper) OnChanOpenTry( } if channel.State == channeltypes.OPEN { - return "", sdkerrors.Wrapf(icatypes.ErrActiveChannelAlreadySet, "existing active channel %s for portID %s is already OPEN", activeChannelID, portID) + return "", errorsmod.Wrapf(icatypes.ErrActiveChannelAlreadySet, "existing active channel %s for portID %s is already OPEN", activeChannelID, portID) + } + + appVersion, found := k.GetAppVersion(ctx, portID, activeChannelID) + if !found { + panic(fmt.Sprintf("active channel mapping set for %s, but channel does not exist in channel store", activeChannelID)) } - if !icatypes.IsPreviousMetadataEqual(channel.Version, metadata) { - return "", sdkerrors.Wrap(icatypes.ErrInvalidVersion, "previous active channel metadata does not match provided version") + if !icatypes.IsPreviousMetadataEqual(appVersion, metadata) { + return "", errorsmod.Wrap(icatypes.ErrInvalidVersion, "previous active channel metadata does not match provided version") } } // On the host chain the capability may only be claimed during the OnChanOpenTry // The capability being claimed in OpenInit is for a controller chain (the port is different) if err := k.ClaimCapability(ctx, chanCap, host.ChannelCapabilityPath(portID, channelID)); err != nil { - return "", sdkerrors.Wrapf(err, "failed to claim capability for channel %s on port %s", channelID, portID) + return "", errorsmod.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 + k.Logger(ctx).Info("reopening existing interchain account", "address", interchainAccAddr) + accAddress = sdk.MustAccAddressFromBech32(interchainAccAddr) + if _, ok := k.accountKeeper.GetAccount(ctx, accAddress).(*icatypes.InterchainAccount); !ok { + return "", errorsmod.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 + } + k.Logger(ctx).Info("successfully created new interchain account", "host-connection-id", metadata.HostConnectionId, "port-id", counterparty.PortId, "address", accAddress) + } metadata.Address = accAddress.String() versionBytes, err := icatypes.ModuleCdc.MarshalJSON(&metadata) @@ -92,7 +109,7 @@ func (k Keeper) OnChanOpenConfirm( ) error { channel, found := k.channelKeeper.GetChannel(ctx, portID, channelID) if !found { - return sdkerrors.Wrapf(channeltypes.ErrChannelNotFound, "failed to retrieve channel %s on port %s", channelID, portID) + return errorsmod.Wrapf(channeltypes.ErrChannelNotFound, "failed to retrieve channel %s on port %s", channelID, portID) } // It is assumed the controller chain will not allow multiple active channels to be created for the same connectionID/portID 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 9784c0ba412..fdba6518538 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/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/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" + hosttypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/host/types" + icatypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + ibctesting "github.com/cosmos/ibc-go/v7/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.SetChannelState(channeltypes.CLOSED) + suite.Require().NoError(err) + + err = path.EndpointB.SetChannelState(channeltypes.CLOSED) + 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.GenerateAddress(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, }, { @@ -69,14 +167,7 @@ func (suite *KeeperTestSuite) TestOnChanOpenTry() { { "invalid port ID", func() { - path.EndpointB.ChannelConfig.PortID = "invalid-port-id" - }, - false, - }, - { - "invalid counterparty port ID", - func() { - channel.Counterparty.PortId = "invalid-port-id" + path.EndpointB.ChannelConfig.PortID = "invalid-port-id" //nolint:goconst }, false, }, @@ -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.go b/modules/apps/27-interchain-accounts/host/keeper/keeper.go index 7125bb52f20..fe8d3310adf 100644 --- a/modules/apps/27-interchain-accounts/host/keeper/keeper.go +++ b/modules/apps/27-interchain-accounts/host/keeper/keeper.go @@ -4,40 +4,43 @@ import ( "fmt" "strings" - baseapp "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/codec" + storetypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" - capabilitykeeper "github.com/cosmos/cosmos-sdk/x/capability/keeper" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" "github.com/tendermint/tendermint/libs/log" - "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" + genesistypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/genesis/types" + "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/host/types" + icatypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + porttypes "github.com/cosmos/ibc-go/v7/modules/core/05-port/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + "github.com/cosmos/ibc-go/v7/modules/core/exported" ) // Keeper defines the IBC interchain accounts host keeper type Keeper struct { - storeKey sdk.StoreKey + storeKey storetypes.StoreKey cdc codec.BinaryCodec paramSpace paramtypes.Subspace + ics4Wrapper porttypes.ICS4Wrapper channelKeeper icatypes.ChannelKeeper portKeeper icatypes.PortKeeper accountKeeper icatypes.AccountKeeper - scopedKeeper capabilitykeeper.ScopedKeeper + scopedKeeper exported.ScopedKeeper - msgRouter *baseapp.MsgServiceRouter + msgRouter icatypes.MessageRouter } // NewKeeper creates a new interchain accounts host Keeper instance func NewKeeper( - cdc codec.BinaryCodec, key sdk.StoreKey, paramSpace paramtypes.Subspace, - channelKeeper icatypes.ChannelKeeper, portKeeper icatypes.PortKeeper, - accountKeeper icatypes.AccountKeeper, scopedKeeper capabilitykeeper.ScopedKeeper, msgRouter *baseapp.MsgServiceRouter, + cdc codec.BinaryCodec, key storetypes.StoreKey, paramSpace paramtypes.Subspace, + ics4Wrapper porttypes.ICS4Wrapper, channelKeeper icatypes.ChannelKeeper, portKeeper icatypes.PortKeeper, + accountKeeper icatypes.AccountKeeper, scopedKeeper exported.ScopedKeeper, msgRouter icatypes.MessageRouter, ) Keeper { // ensure ibc interchain accounts module account is set if addr := accountKeeper.GetModuleAddress(icatypes.ModuleName); addr == nil { @@ -53,6 +56,7 @@ func NewKeeper( storeKey: key, cdc: cdc, paramSpace: paramSpace, + ics4Wrapper: ics4Wrapper, channelKeeper: channelKeeper, portKeeper: portKeeper, accountKeeper: accountKeeper, @@ -63,7 +67,7 @@ func NewKeeper( // Logger returns the application logger, scoped to the associated module func (k Keeper) Logger(ctx sdk.Context) log.Logger { - return ctx.Logger().With("module", fmt.Sprintf("x/%s-%s", host.ModuleName, icatypes.ModuleName)) + return ctx.Logger().With("module", fmt.Sprintf("x/%s-%s", exported.ModuleName, icatypes.ModuleName)) } // BindPort stores the provided portID and binds to it, returning the associated capability @@ -90,6 +94,11 @@ func (k Keeper) ClaimCapability(ctx sdk.Context, cap *capabilitytypes.Capability return k.scopedKeeper.ClaimCapability(ctx, cap, name) } +// GetAppVersion calls the ICS4Wrapper GetAppVersion function. +func (k Keeper) GetAppVersion(ctx sdk.Context, portID, channelID string) (string, bool) { + return k.ics4Wrapper.GetAppVersion(ctx, portID, channelID) +} + // GetActiveChannelID retrieves the active channelID from the store keyed by the provided connectionID and portID func (k Keeper) GetActiveChannelID(ctx sdk.Context, connectionID, portID string) (string, bool) { store := ctx.KVStore(k.storeKey) @@ -119,16 +128,16 @@ func (k Keeper) GetOpenActiveChannel(ctx sdk.Context, connectionID, portID strin } // GetAllActiveChannels returns a list of all active interchain accounts host channels and their associated connection and port identifiers -func (k Keeper) GetAllActiveChannels(ctx sdk.Context) []icatypes.ActiveChannel { +func (k Keeper) GetAllActiveChannels(ctx sdk.Context) []genesistypes.ActiveChannel { store := ctx.KVStore(k.storeKey) iterator := sdk.KVStorePrefixIterator(store, []byte(icatypes.ActiveChannelKeyPrefix)) - defer iterator.Close() + defer sdk.LogDeferred(ctx.Logger(), func() error { return iterator.Close() }) - var activeChannels []icatypes.ActiveChannel + var activeChannels []genesistypes.ActiveChannel for ; iterator.Valid(); iterator.Next() { keySplit := strings.Split(string(iterator.Key()), "/") - ch := icatypes.ActiveChannel{ + ch := genesistypes.ActiveChannel{ ConnectionId: keySplit[2], PortId: keySplit[1], ChannelId: string(iterator.Value()), @@ -165,15 +174,15 @@ func (k Keeper) GetInterchainAccountAddress(ctx sdk.Context, connectionID, portI } // GetAllInterchainAccounts returns a list of all registered interchain account addresses and their associated connection and controller port identifiers -func (k Keeper) GetAllInterchainAccounts(ctx sdk.Context) []icatypes.RegisteredInterchainAccount { +func (k Keeper) GetAllInterchainAccounts(ctx sdk.Context) []genesistypes.RegisteredInterchainAccount { store := ctx.KVStore(k.storeKey) iterator := sdk.KVStorePrefixIterator(store, []byte(icatypes.OwnerKeyPrefix)) - var interchainAccounts []icatypes.RegisteredInterchainAccount + var interchainAccounts []genesistypes.RegisteredInterchainAccount for ; iterator.Valid(); iterator.Next() { keySplit := strings.Split(string(iterator.Key()), "/") - acc := icatypes.RegisteredInterchainAccount{ + acc := genesistypes.RegisteredInterchainAccount{ ConnectionId: keySplit[2], PortId: keySplit[1], AccountAddress: string(iterator.Value()), 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 7953f0f5663..c250e046d67 100644 --- a/modules/apps/27-interchain-accounts/host/keeper/keeper_test.go +++ b/modules/apps/27-interchain-accounts/host/keeper/keeper_test.go @@ -3,29 +3,22 @@ package keeper_test import ( "testing" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/suite" - "github.com/tendermint/tendermint/crypto" - 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" - ibctesting "github.com/cosmos/ibc-go/v4/testing" + genesistypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/genesis/types" + icatypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + ibctesting "github.com/cosmos/ibc-go/v7/testing" ) 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 = "cosmos17dtl0mjt3t77kpuhg2edqzjpszulwhgzuj9ljs" - // TestPortID defines a resuable port identifier for testing purposes + // TestPortID defines a reusable port identifier for testing purposes TestPortID, _ = icatypes.NewControllerPortID(TestOwnerAddress) - // TestVersion defines a resuable interchainaccounts version string for testing purposes + // TestVersion defines a reusable interchainaccounts version string for testing purposes TestVersion = string(icatypes.ModuleCdc.MustMarshalJSON(&icatypes.Metadata{ Version: icatypes.Version, ControllerConnectionId: ibctesting.FirstConnectionID, @@ -55,8 +48,8 @@ func (suite *KeeperTestSuite) SetupTest() { func NewICAPath(chainA, chainB *ibctesting.TestChain) *ibctesting.Path { path := ibctesting.NewPath(chainA, chainB) - path.EndpointA.ChannelConfig.PortID = icatypes.PortID - path.EndpointB.ChannelConfig.PortID = icatypes.PortID + path.EndpointA.ChannelConfig.PortID = icatypes.HostPortID + path.EndpointB.ChannelConfig.PortID = icatypes.HostPortID path.EndpointA.ChannelConfig.Order = channeltypes.ORDERED path.EndpointB.ChannelConfig.Order = channeltypes.ORDERED path.EndpointA.ChannelConfig.Version = TestVersion @@ -136,11 +129,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) @@ -149,8 +141,8 @@ func (suite *KeeperTestSuite) TestGetInterchainAccountAddress() { func (suite *KeeperTestSuite) TestGetAllActiveChannels() { var ( - expectedChannelID string = "test-channel" - expectedPortID string = "test-port" + expectedChannelID = "test-channel" + expectedPortID = "test-port" ) suite.SetupTest() @@ -163,7 +155,7 @@ func (suite *KeeperTestSuite) TestGetAllActiveChannels() { suite.chainB.GetSimApp().ICAHostKeeper.SetActiveChannelID(suite.chainB.GetContext(), ibctesting.FirstConnectionID, expectedPortID, expectedChannelID) - expectedChannels := []icatypes.ActiveChannel{ + expectedChannels := []genesistypes.ActiveChannel{ { ConnectionId: ibctesting.FirstConnectionID, PortId: path.EndpointA.ChannelConfig.PortID, @@ -183,8 +175,8 @@ func (suite *KeeperTestSuite) TestGetAllActiveChannels() { func (suite *KeeperTestSuite) TestGetAllInterchainAccounts() { var ( - expectedAccAddr string = "test-acc-addr" - expectedPortID string = "test-port" + expectedAccAddr = "test-acc-addr" + expectedPortID = "test-port" ) suite.SetupTest() @@ -195,13 +187,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{ + expectedAccounts := []genesistypes.RegisteredInterchainAccount{ { ConnectionId: ibctesting.FirstConnectionID, PortId: TestPortID, - AccountAddress: TestAccAddress.String(), + AccountAddress: interchainAccAddr, }, { ConnectionId: ibctesting.FirstConnectionID, @@ -230,8 +225,8 @@ func (suite *KeeperTestSuite) TestIsActiveChannel() { func (suite *KeeperTestSuite) TestSetInterchainAccountAddress() { var ( - expectedAccAddr string = "test-acc-addr" - expectedPortID string = "test-port" + expectedAccAddr = "test-acc-addr" + expectedPortID = "test-port" ) suite.chainB.GetSimApp().ICAHostKeeper.SetInterchainAccountAddress(suite.chainB.GetContext(), ibctesting.FirstConnectionID, expectedPortID, expectedAccAddr) diff --git a/modules/apps/27-interchain-accounts/host/keeper/params.go b/modules/apps/27-interchain-accounts/host/keeper/params.go index e56e72342c8..6c95d737541 100644 --- a/modules/apps/27-interchain-accounts/host/keeper/params.go +++ b/modules/apps/27-interchain-accounts/host/keeper/params.go @@ -3,7 +3,7 @@ package keeper import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/host/types" + "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/host/types" ) // IsHostEnabled retrieves the host enabled boolean from the paramstore. diff --git a/modules/apps/27-interchain-accounts/host/keeper/params_test.go b/modules/apps/27-interchain-accounts/host/keeper/params_test.go index c769ade3fdb..a8d056ce007 100644 --- a/modules/apps/27-interchain-accounts/host/keeper/params_test.go +++ b/modules/apps/27-interchain-accounts/host/keeper/params_test.go @@ -1,6 +1,6 @@ package keeper_test -import "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/host/types" +import "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/host/types" func (suite *KeeperTestSuite) TestParams() { expParams := types.DefaultParams() diff --git a/modules/apps/27-interchain-accounts/host/keeper/relay.go b/modules/apps/27-interchain-accounts/host/keeper/relay.go index e658c5bd3f5..4e620291a6b 100644 --- a/modules/apps/27-interchain-accounts/host/keeper/relay.go +++ b/modules/apps/27-interchain-accounts/host/keeper/relay.go @@ -1,13 +1,15 @@ package keeper import ( + errorsmod "cosmossdk.io/errors" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/gogo/protobuf/proto" + "github.com/cosmos/gogoproto/proto" - "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" + ibcerrors "github.com/cosmos/ibc-go/v7/internal/errors" + "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/host/types" + icatypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" ) // OnRecvPacket handles a given interchain accounts packet on a destination host chain. @@ -17,21 +19,20 @@ func (k Keeper) OnRecvPacket(ctx sdk.Context, packet channeltypes.Packet) ([]byt if err := icatypes.ModuleCdc.UnmarshalJSON(packet.GetData(), &data); err != nil { // UnmarshalJSON errors are indeterminate and therefore are not wrapped and included in failed acks - return nil, sdkerrors.Wrapf(icatypes.ErrUnknownDataType, "cannot unmarshal ICS-27 interchain account packet data") + return nil, errorsmod.Wrapf(icatypes.ErrUnknownDataType, "cannot unmarshal ICS-27 interchain account packet data") } switch data.Type { case icatypes.EXECUTE_TX: msgs, err := icatypes.DeserializeCosmosTx(k.cdc, data.Data) if err != nil { - return nil, err + return nil, errorsmod.Wrapf(err, "failed to deserialize interchain account transaction") } txResponse, err := k.executeTx(ctx, packet.SourcePort, packet.DestinationPort, packet.DestinationChannel, msgs) if err != nil { - return nil, err + return nil, errorsmod.Wrapf(err, "failed to execute interchain account transaction") } - return txResponse, nil default: return nil, icatypes.ErrUnknownDataType @@ -53,7 +54,7 @@ func (k Keeper) executeTx(ctx sdk.Context, sourcePort, destPort, destChannel str } txMsgData := &sdk.TxMsgData{ - Data: make([]*sdk.MsgData, len(msgs)), + MsgResponses: make([]*codectypes.Any, len(msgs)), } // CacheContext returns a new context with the multi-store branched into a cached storage object @@ -64,25 +65,19 @@ func (k Keeper) executeTx(ctx sdk.Context, sourcePort, destPort, destChannel str return nil, err } - msgResponse, err := k.executeMsg(cacheCtx, msg) + protoAny, err := k.executeMsg(cacheCtx, msg) if err != nil { return nil, err } - txMsgData.Data[i] = &sdk.MsgData{ - MsgType: sdk.MsgTypeURL(msg), - Data: msgResponse, - } - + txMsgData.MsgResponses[i] = protoAny } - // NOTE: The context returned by CacheContext() creates a new EventManager, so events must be correctly propagated back to the current context - ctx.EventManager().EmitEvents(cacheCtx.EventManager().Events()) writeCache() txResponse, err := proto.Marshal(txMsgData) if err != nil { - return nil, sdkerrors.Wrap(err, "failed to marshal tx data") + return nil, errorsmod.Wrap(err, "failed to marshal tx data") } return txResponse, nil @@ -93,18 +88,18 @@ func (k Keeper) executeTx(ctx sdk.Context, sourcePort, destPort, destChannel str func (k Keeper) authenticateTx(ctx sdk.Context, msgs []sdk.Msg, connectionID, portID string) error { interchainAccountAddr, found := k.GetInterchainAccountAddress(ctx, connectionID, portID) if !found { - return sdkerrors.Wrapf(icatypes.ErrInterchainAccountNotFound, "failed to retrieve interchain account on port %s", portID) + return errorsmod.Wrapf(icatypes.ErrInterchainAccountNotFound, "failed to retrieve interchain account on port %s", portID) } allowMsgs := k.GetAllowMessages(ctx) for _, msg := range msgs { if !types.ContainsMsgType(allowMsgs, msg) { - return sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "message type not allowed: %s", sdk.MsgTypeURL(msg)) + return errorsmod.Wrapf(ibcerrors.ErrUnauthorized, "message type not allowed: %s", sdk.MsgTypeURL(msg)) } for _, signer := range msg.GetSigners() { if interchainAccountAddr != signer.String() { - return sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "unexpected signer address: expected %s, got %s", interchainAccountAddr, signer.String()) + return errorsmod.Wrapf(ibcerrors.ErrUnauthorized, "unexpected signer address: expected %s, got %s", interchainAccountAddr, signer.String()) } } } @@ -114,7 +109,7 @@ func (k Keeper) authenticateTx(ctx sdk.Context, msgs []sdk.Msg, connectionID, po // Attempts to get the message handler from the router and if found will then execute the message. // If the message execution is successful, the proto marshaled message response will be returned. -func (k Keeper) executeMsg(ctx sdk.Context, msg sdk.Msg) ([]byte, error) { +func (k Keeper) executeMsg(ctx sdk.Context, msg sdk.Msg) (*codectypes.Any, error) { handler := k.msgRouter.Handler(msg) if handler == nil { return nil, icatypes.ErrInvalidRoute @@ -128,5 +123,11 @@ func (k Keeper) executeMsg(ctx sdk.Context, msg sdk.Msg) ([]byte, error) { // NOTE: The sdk msg handler creates a new EventManager, so events must be correctly propagated back to the current context ctx.EventManager().EmitEvents(res.GetEvents()) - return res.Data, nil + // Each individual sdk.Result has exactly one Msg response. We aggregate here. + msgResponse := res.MsgResponses[0] + if msgResponse == nil { + return nil, errorsmod.Wrapf(ibcerrors.ErrLogic, "got nil Msg response for msg %s", sdk.MsgTypeURL(msg)) + } + + return msgResponse, nil } diff --git a/modules/apps/27-interchain-accounts/host/keeper/relay_test.go b/modules/apps/27-interchain-accounts/host/keeper/relay_test.go index c7ebd9428f5..6e07e58715f 100644 --- a/modules/apps/27-interchain-accounts/host/keeper/relay_test.go +++ b/modules/apps/27-interchain-accounts/host/keeper/relay_test.go @@ -1,21 +1,21 @@ package keeper_test import ( - "time" - codectypes "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" disttypes "github.com/cosmos/cosmos-sdk/x/distribution/types" - govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + govv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - - "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" - 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" + "github.com/cosmos/gogoproto/proto" + + "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/host/types" + icatypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types" + transfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + ibctesting "github.com/cosmos/ibc-go/v7/testing" ) func (suite *KeeperTestSuite) TestOnRecvPacket() { @@ -41,7 +41,10 @@ func (suite *KeeperTestSuite) TestOnRecvPacket() { Description: "tokens for all!", } - proposal, err := govtypes.NewProposal(testProposal, govtypes.DefaultStartingProposalID, time.Now(), time.Now().Add(time.Hour)) + proposalMsg, err := govv1.NewLegacyContent(testProposal, interchainAccountAddr) + suite.Require().NoError(err) + + proposal, err := govv1.NewProposal([]sdk.Msg{proposalMsg}, govtypes.DefaultStartingProposalID, suite.chainA.GetContext().BlockTime(), suite.chainA.GetContext().BlockTime(), "test proposal", "title", "Description", sdk.AccAddress(interchainAccountAddr)) suite.Require().NoError(err) suite.chainB.GetSimApp().GovKeeper.SetProposal(suite.chainB.GetContext(), proposal) @@ -53,7 +56,7 @@ func (suite *KeeperTestSuite) TestOnRecvPacket() { Option: govtypes.OptionYes, } - data, err := icatypes.SerializeCosmosTx(suite.chainA.GetSimApp().AppCodec(), []sdk.Msg{msg}) + data, err := icatypes.SerializeCosmosTx(suite.chainA.GetSimApp().AppCodec(), []proto.Message{msg}) suite.Require().NoError(err) icaPacketData := icatypes.InterchainAccountPacketData{ @@ -80,7 +83,7 @@ func (suite *KeeperTestSuite) TestOnRecvPacket() { Amount: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100))), } - data, err := icatypes.SerializeCosmosTx(suite.chainA.GetSimApp().AppCodec(), []sdk.Msg{msg}) + data, err := icatypes.SerializeCosmosTx(suite.chainA.GetSimApp().AppCodec(), []proto.Message{msg}) suite.Require().NoError(err) icaPacketData := icatypes.InterchainAccountPacketData{ @@ -108,7 +111,7 @@ func (suite *KeeperTestSuite) TestOnRecvPacket() { Amount: sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(5000)), } - data, err := icatypes.SerializeCosmosTx(suite.chainA.GetSimApp().AppCodec(), []sdk.Msg{msg}) + data, err := icatypes.SerializeCosmosTx(suite.chainA.GetSimApp().AppCodec(), []proto.Message{msg}) suite.Require().NoError(err) icaPacketData := icatypes.InterchainAccountPacketData{ @@ -142,7 +145,7 @@ func (suite *KeeperTestSuite) TestOnRecvPacket() { Amount: sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(5000)), } - data, err := icatypes.SerializeCosmosTx(suite.chainA.GetSimApp().AppCodec(), []sdk.Msg{msgDelegate, msgUndelegate}) + data, err := icatypes.SerializeCosmosTx(suite.chainA.GetSimApp().AppCodec(), []proto.Message{msgDelegate, msgUndelegate}) suite.Require().NoError(err) icaPacketData := icatypes.InterchainAccountPacketData{ @@ -168,16 +171,16 @@ func (suite *KeeperTestSuite) TestOnRecvPacket() { Description: "tokens for all!", } - any, err := codectypes.NewAnyWithValue(testProposal) + protoAny, err := codectypes.NewAnyWithValue(testProposal) suite.Require().NoError(err) msg := &govtypes.MsgSubmitProposal{ - Content: any, + Content: protoAny, InitialDeposit: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(5000))), Proposer: interchainAccountAddr, } - data, err := icatypes.SerializeCosmosTx(suite.chainA.GetSimApp().AppCodec(), []sdk.Msg{msg}) + data, err := icatypes.SerializeCosmosTx(suite.chainA.GetSimApp().AppCodec(), []proto.Message{msg}) suite.Require().NoError(err) icaPacketData := icatypes.InterchainAccountPacketData{ @@ -204,7 +207,10 @@ func (suite *KeeperTestSuite) TestOnRecvPacket() { Description: "tokens for all!", } - proposal, err := govtypes.NewProposal(testProposal, govtypes.DefaultStartingProposalID, time.Now(), time.Now().Add(time.Hour)) + proposalMsg, err := govv1.NewLegacyContent(testProposal, interchainAccountAddr) + suite.Require().NoError(err) + + proposal, err := govv1.NewProposal([]sdk.Msg{proposalMsg}, govtypes.DefaultStartingProposalID, suite.chainA.GetContext().BlockTime(), suite.chainA.GetContext().BlockTime(), "test proposal", "title", "description", sdk.AccAddress(interchainAccountAddr)) suite.Require().NoError(err) suite.chainB.GetSimApp().GovKeeper.SetProposal(suite.chainB.GetContext(), proposal) @@ -216,7 +222,7 @@ func (suite *KeeperTestSuite) TestOnRecvPacket() { Option: govtypes.OptionYes, } - data, err := icatypes.SerializeCosmosTx(suite.chainA.GetSimApp().AppCodec(), []sdk.Msg{msg}) + data, err := icatypes.SerializeCosmosTx(suite.chainA.GetSimApp().AppCodec(), []proto.Message{msg}) suite.Require().NoError(err) icaPacketData := icatypes.InterchainAccountPacketData{ @@ -242,7 +248,7 @@ func (suite *KeeperTestSuite) TestOnRecvPacket() { Depositor: interchainAccountAddr, } - data, err := icatypes.SerializeCosmosTx(suite.chainA.GetSimApp().AppCodec(), []sdk.Msg{msg}) + data, err := icatypes.SerializeCosmosTx(suite.chainA.GetSimApp().AppCodec(), []proto.Message{msg}) suite.Require().NoError(err) icaPacketData := icatypes.InterchainAccountPacketData{ @@ -268,7 +274,7 @@ func (suite *KeeperTestSuite) TestOnRecvPacket() { WithdrawAddress: suite.chainB.SenderAccount.GetAddress().String(), } - data, err := icatypes.SerializeCosmosTx(suite.chainA.GetSimApp().AppCodec(), []sdk.Msg{msg}) + data, err := icatypes.SerializeCosmosTx(suite.chainA.GetSimApp().AppCodec(), []proto.Message{msg}) suite.Require().NoError(err) icaPacketData := icatypes.InterchainAccountPacketData{ @@ -303,11 +309,11 @@ func (suite *KeeperTestSuite) TestOnRecvPacket() { Token: sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100)), Sender: interchainAccountAddr, Receiver: suite.chainA.SenderAccount.GetAddress().String(), - TimeoutHeight: clienttypes.NewHeight(0, 100), + TimeoutHeight: clienttypes.NewHeight(1, 100), TimeoutTimestamp: uint64(0), } - data, err := icatypes.SerializeCosmosTx(suite.chainA.GetSimApp().AppCodec(), []sdk.Msg{msg}) + data, err := icatypes.SerializeCosmosTx(suite.chainA.GetSimApp().AppCodec(), []proto.Message{msg}) suite.Require().NoError(err) icaPacketData := icatypes.InterchainAccountPacketData{ @@ -322,6 +328,26 @@ func (suite *KeeperTestSuite) TestOnRecvPacket() { }, true, }, + { + "unregistered sdk.Msg", + func() { + msg := &banktypes.MsgSendResponse{} + + data, err := icatypes.SerializeCosmosTx(suite.chainA.GetSimApp().AppCodec(), []proto.Message{msg}) + suite.Require().NoError(err) + + icaPacketData := icatypes.InterchainAccountPacketData{ + Type: icatypes.EXECUTE_TX, + Data: data, + } + + packetData = icaPacketData.GetBytes() + + params := types.NewParams(true, []string{"/" + proto.MessageName(msg)}) + suite.chainB.GetSimApp().ICAHostKeeper.SetParams(suite.chainB.GetContext(), params) + }, + false, + }, { "cannot unmarshal interchain account packet data", func() { @@ -346,7 +372,7 @@ func (suite *KeeperTestSuite) TestOnRecvPacket() { { "invalid packet type - UNSPECIFIED", func() { - data, err := icatypes.SerializeCosmosTx(suite.chainA.GetSimApp().AppCodec(), []sdk.Msg{&banktypes.MsgSend{}}) + data, err := icatypes.SerializeCosmosTx(suite.chainA.GetSimApp().AppCodec(), []proto.Message{&banktypes.MsgSend{}}) suite.Require().NoError(err) icaPacketData := icatypes.InterchainAccountPacketData{ @@ -363,7 +389,7 @@ func (suite *KeeperTestSuite) TestOnRecvPacket() { func() { path.EndpointA.ChannelConfig.PortID = "invalid-port-id" - data, err := icatypes.SerializeCosmosTx(suite.chainA.GetSimApp().AppCodec(), []sdk.Msg{&banktypes.MsgSend{}}) + data, err := icatypes.SerializeCosmosTx(suite.chainA.GetSimApp().AppCodec(), []proto.Message{&banktypes.MsgSend{}}) suite.Require().NoError(err) icaPacketData := icatypes.InterchainAccountPacketData{ @@ -384,7 +410,7 @@ func (suite *KeeperTestSuite) TestOnRecvPacket() { Amount: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100))), } - data, err := icatypes.SerializeCosmosTx(suite.chainA.GetSimApp().AppCodec(), []sdk.Msg{msg}) + data, err := icatypes.SerializeCosmosTx(suite.chainA.GetSimApp().AppCodec(), []proto.Message{msg}) suite.Require().NoError(err) icaPacketData := icatypes.InterchainAccountPacketData{ @@ -405,7 +431,7 @@ func (suite *KeeperTestSuite) TestOnRecvPacket() { Amount: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100))), } - data, err := icatypes.SerializeCosmosTx(suite.chainA.GetSimApp().AppCodec(), []sdk.Msg{msg}) + data, err := icatypes.SerializeCosmosTx(suite.chainA.GetSimApp().AppCodec(), []proto.Message{msg}) suite.Require().NoError(err) icaPacketData := icatypes.InterchainAccountPacketData{ @@ -459,7 +485,7 @@ func (suite *KeeperTestSuite) TestOnRecvPacket() { path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, - clienttypes.NewHeight(0, 100), + clienttypes.NewHeight(1, 100), 0, ) diff --git a/modules/apps/27-interchain-accounts/host/types/errors.go b/modules/apps/27-interchain-accounts/host/types/errors.go index b16b4093e5e..adba00f6596 100644 --- a/modules/apps/27-interchain-accounts/host/types/errors.go +++ b/modules/apps/27-interchain-accounts/host/types/errors.go @@ -1,10 +1,10 @@ package types import ( - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + errorsmod "cosmossdk.io/errors" ) // ICA Host sentinel errors var ( - ErrHostSubModuleDisabled = sdkerrors.Register(SubModuleName, 2, "host submodule is disabled") + ErrHostSubModuleDisabled = errorsmod.Register(SubModuleName, 2, "host submodule is disabled") ) diff --git a/modules/apps/27-interchain-accounts/host/types/host.pb.go b/modules/apps/27-interchain-accounts/host/types/host.pb.go index db7356b7e9a..4b2cc0c6218 100644 --- a/modules/apps/27-interchain-accounts/host/types/host.pb.go +++ b/modules/apps/27-interchain-accounts/host/types/host.pb.go @@ -5,8 +5,8 @@ package types import ( fmt "fmt" - _ "github.com/gogo/protobuf/gogoproto" - proto "github.com/gogo/protobuf/proto" + _ "github.com/cosmos/gogoproto/gogoproto" + proto "github.com/cosmos/gogoproto/proto" io "io" math "math" math_bits "math/bits" @@ -104,9 +104,9 @@ var fileDescriptor_48e202774f13d08e = []byte{ 0xe4, 0x18, 0x2f, 0x3c, 0x92, 0x63, 0x7c, 0xf0, 0x48, 0x8e, 0x71, 0xc2, 0x63, 0x39, 0x86, 0x0b, 0x8f, 0xe5, 0x18, 0x6e, 0x3c, 0x96, 0x63, 0x88, 0xf2, 0x4a, 0xcf, 0x2c, 0xc9, 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xce, 0x2f, 0xce, 0xcd, 0x2f, 0xd6, 0xcf, 0x4c, 0x4a, 0xd6, 0x4d, - 0xcf, 0xd7, 0x2f, 0x33, 0xd1, 0xcf, 0xcd, 0x4f, 0x29, 0xcd, 0x49, 0x2d, 0x06, 0x85, 0x5f, 0xb1, + 0xcf, 0xd7, 0x2f, 0x33, 0xd7, 0xcf, 0xcd, 0x4f, 0x29, 0xcd, 0x49, 0x2d, 0x06, 0x85, 0x5f, 0xb1, 0xbe, 0x91, 0xb9, 0x2e, 0x22, 0x04, 0x74, 0x51, 0x83, 0xae, 0xa4, 0xb2, 0x20, 0xb5, 0x38, 0x89, - 0x0d, 0xec, 0x6b, 0x63, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0xd5, 0xb2, 0xee, 0x31, 0x74, 0x01, + 0x0d, 0xec, 0x6b, 0x63, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0xac, 0x86, 0x51, 0x17, 0x74, 0x01, 0x00, 0x00, } diff --git a/modules/apps/27-interchain-accounts/host/types/keys.go b/modules/apps/27-interchain-accounts/host/types/keys.go index 7f1a04facb5..7e0ca350d0e 100644 --- a/modules/apps/27-interchain-accounts/host/types/keys.go +++ b/modules/apps/27-interchain-accounts/host/types/keys.go @@ -10,12 +10,15 @@ const ( // StoreKey is the store key string for the interchain accounts host module StoreKey = SubModuleName + + // AllowAllHostMsgs holds the string key that allows all message types on interchain accounts host module + AllowAllHostMsgs = "*" ) // ContainsMsgType returns true if the sdk.Msg TypeURL is present in allowMsgs, otherwise false func ContainsMsgType(allowMsgs []string, msg sdk.Msg) bool { // check that wildcard * option for allowing all message types is the only string in the array, if so, return true - if len(allowMsgs) == 1 && allowMsgs[0] == "*" { + if len(allowMsgs) == 1 && allowMsgs[0] == AllowAllHostMsgs { return true } diff --git a/modules/apps/27-interchain-accounts/host/types/params.go b/modules/apps/27-interchain-accounts/host/types/params.go index 480de05c286..af28c413c9e 100644 --- a/modules/apps/27-interchain-accounts/host/types/params.go +++ b/modules/apps/27-interchain-accounts/host/types/params.go @@ -34,12 +34,12 @@ func NewParams(enableHost bool, allowMsgs []string) Params { // DefaultParams is the default parameter configuration for the host submodule func DefaultParams() Params { - return NewParams(DefaultHostEnabled, nil) + return NewParams(DefaultHostEnabled, []string{AllowAllHostMsgs}) } // Validate validates all host submodule parameters func (p Params) Validate() error { - if err := validateEnabled(p.HostEnabled); err != nil { + if err := validateEnabledType(p.HostEnabled); err != nil { return err } @@ -53,12 +53,12 @@ func (p Params) Validate() error { // ParamSetPairs implements params.ParamSet func (p *Params) ParamSetPairs() paramtypes.ParamSetPairs { return paramtypes.ParamSetPairs{ - paramtypes.NewParamSetPair(KeyHostEnabled, p.HostEnabled, validateEnabled), + paramtypes.NewParamSetPair(KeyHostEnabled, p.HostEnabled, validateEnabledType), paramtypes.NewParamSetPair(KeyAllowMessages, p.AllowMessages, validateAllowlist), } } -func validateEnabled(i interface{}) error { +func validateEnabledType(i interface{}) error { _, ok := i.(bool) if !ok { return fmt.Errorf("invalid parameter type: %T", i) diff --git a/modules/apps/27-interchain-accounts/host/types/params_test.go b/modules/apps/27-interchain-accounts/host/types/params_test.go index 39ed24251f7..e83b82e7447 100644 --- a/modules/apps/27-interchain-accounts/host/types/params_test.go +++ b/modules/apps/27-interchain-accounts/host/types/params_test.go @@ -5,7 +5,7 @@ import ( "github.com/stretchr/testify/require" - "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/host/types" + "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/host/types" ) func TestValidateParams(t *testing.T) { diff --git a/modules/apps/27-interchain-accounts/host/types/query.pb.go b/modules/apps/27-interchain-accounts/host/types/query.pb.go index 313d0f10b6f..e1019e8c1f6 100644 --- a/modules/apps/27-interchain-accounts/host/types/query.pb.go +++ b/modules/apps/27-interchain-accounts/host/types/query.pb.go @@ -6,8 +6,8 @@ package types import ( context "context" fmt "fmt" - grpc1 "github.com/gogo/protobuf/grpc" - proto "github.com/gogo/protobuf/proto" + grpc1 "github.com/cosmos/gogoproto/grpc" + proto "github.com/cosmos/gogoproto/proto" _ "google.golang.org/genproto/googleapis/api/annotations" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" @@ -139,9 +139,9 @@ var fileDescriptor_e6b7e23fc90c353a = []byte{ 0xc5, 0xc8, 0x80, 0x37, 0x67, 0xfd, 0xfb, 0x9c, 0x75, 0x9f, 0xf3, 0xd9, 0x73, 0x41, 0xd1, 0xba, 0xa0, 0xe8, 0xbd, 0xa0, 0xe8, 0xa1, 0xa4, 0x9d, 0x75, 0x49, 0x3b, 0x6f, 0x25, 0xed, 0xdc, 0x5e, 0xc4, 0xda, 0xcf, 0xb3, 0x88, 0x49, 0x48, 0xb8, 0x04, 0x97, 0x80, 0xab, 0xc4, 0xc3, 0x18, 0x78, - 0x3e, 0xe1, 0x09, 0xcc, 0xb2, 0x85, 0x72, 0x75, 0xcc, 0x78, 0x3a, 0xfc, 0x4a, 0x1a, 0x6e, 0x26, - 0xf9, 0x95, 0x55, 0x2e, 0xea, 0x86, 0x7f, 0x3b, 0xfe, 0x0c, 0x00, 0x00, 0xff, 0xff, 0x58, 0x8e, - 0x7b, 0xe6, 0x78, 0x02, 0x00, 0x00, + 0x3e, 0xe5, 0x09, 0xcc, 0xb2, 0x85, 0x72, 0x75, 0xcc, 0x78, 0x3a, 0xfc, 0x4a, 0x1a, 0x6e, 0x26, + 0xf9, 0x95, 0x55, 0x2e, 0xea, 0x86, 0x7f, 0x3b, 0xfe, 0x0c, 0x00, 0x00, 0xff, 0xff, 0x21, 0xba, + 0xc4, 0xc0, 0x78, 0x02, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. diff --git a/modules/apps/27-interchain-accounts/host/types/query.pb.gw.go b/modules/apps/27-interchain-accounts/host/types/query.pb.gw.go index fbf8503339f..abfb56ce79f 100644 --- a/modules/apps/27-interchain-accounts/host/types/query.pb.gw.go +++ b/modules/apps/27-interchain-accounts/host/types/query.pb.gw.go @@ -20,6 +20,7 @@ import ( "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/grpclog" + "google.golang.org/grpc/metadata" "google.golang.org/grpc/status" ) @@ -30,6 +31,7 @@ var _ status.Status var _ = runtime.String var _ = utilities.NewDoubleArray var _ = descriptor.ForMessage +var _ = metadata.Join 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 @@ -52,12 +54,14 @@ func local_request_Query_Params_0(ctx context.Context, marshaler runtime.Marshal // RegisterQueryHandlerServer registers the http handlers for service Query to "mux". // UnaryRPC :call QueryServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. -// Note that using this registration option will cause many gRPC library features (such as grpc.SendHeader, etc) to stop working. Consider using RegisterQueryHandlerFromEndpoint instead. +// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterQueryHandlerFromEndpoint instead. func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, server QueryServer) error { 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() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { @@ -65,6 +69,7 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv return } resp, md, err := local_request_Query_Params_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) @@ -140,7 +145,7 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie } var ( - 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", "host", "v1", "params"}, "", 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", "host", "v1", "params"}, "", runtime.AssumeColonVerbOpt(false))) ) var ( diff --git a/modules/apps/27-interchain-accounts/module.go b/modules/apps/27-interchain-accounts/module.go index cfcf9804a66..e845df02e35 100644 --- a/modules/apps/27-interchain-accounts/module.go +++ b/modules/apps/27-interchain-accounts/module.go @@ -10,25 +10,28 @@ import ( codectypes "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" - "github.com/gorilla/mux" + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" "github.com/grpc-ecosystem/grpc-gateway/runtime" "github.com/spf13/cobra" abci "github.com/tendermint/tendermint/abci/types" - "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/client/cli" - controllerkeeper "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/controller/keeper" - controllertypes "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/controller/types" - "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/host" - hostkeeper "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/host/keeper" - hosttypes "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/host/types" - "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/types" - porttypes "github.com/cosmos/ibc-go/v4/modules/core/05-port/types" - ibchost "github.com/cosmos/ibc-go/v4/modules/core/24-host" + "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/client/cli" + controllerkeeper "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/controller/keeper" + controllertypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/controller/types" + genesistypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/genesis/types" + "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/host" + hostkeeper "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/host/keeper" + hosttypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/host/types" + "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/simulation" + "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types" + porttypes "github.com/cosmos/ibc-go/v7/modules/core/05-port/types" + ibchost "github.com/cosmos/ibc-go/v7/modules/core/24-host" ) var ( - _ module.AppModule = AppModule{} - _ module.AppModuleBasic = AppModuleBasic{} + _ module.AppModule = AppModule{} + _ module.AppModuleBasic = AppModuleBasic{} + _ module.AppModuleSimulation = AppModule{} _ porttypes.IBCModule = host.IBCModule{} ) @@ -46,18 +49,19 @@ func (AppModuleBasic) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) {} // RegisterInterfaces registers module concrete types into protobuf Any func (AppModuleBasic) RegisterInterfaces(registry codectypes.InterfaceRegistry) { + controllertypes.RegisterInterfaces(registry) types.RegisterInterfaces(registry) } // DefaultGenesis returns default genesis state as raw bytes for the IBC // interchain accounts module func (AppModuleBasic) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage { - return cdc.MustMarshalJSON(types.DefaultGenesis()) + return cdc.MustMarshalJSON(genesistypes.DefaultGenesis()) } // ValidateGenesis performs genesis state validation for the IBC interchain acounts module func (AppModuleBasic) ValidateGenesis(cdc codec.JSONCodec, config client.TxEncodingConfig, bz json.RawMessage) error { - var gs types.GenesisState + var gs genesistypes.GenesisState if err := cdc.UnmarshalJSON(bz, &gs); err != nil { return fmt.Errorf("failed to unmarshal %s genesis state: %w", types.ModuleName, err) } @@ -65,19 +69,22 @@ func (AppModuleBasic) ValidateGenesis(cdc codec.JSONCodec, config client.TxEncod return gs.Validate() } -// RegisterRESTRoutes implements AppModuleBasic interface -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 func (AppModuleBasic) GetTxCmd() *cobra.Command { - return nil + return cli.NewTxCmd() } // GetQueryCmd implements AppModuleBasic interface @@ -110,8 +117,8 @@ func (am AppModule) InitModule(ctx sdk.Context, controllerParams controllertypes if am.hostKeeper != nil { am.hostKeeper.SetParams(ctx, hostParams) - cap := am.hostKeeper.BindPort(ctx, types.PortID) - if err := am.hostKeeper.ClaimCapability(ctx, cap, ibchost.PortPath(types.PortID)); err != nil { + cap := am.hostKeeper.BindPort(ctx, types.HostPortID) + if err := am.hostKeeper.ClaimCapability(ctx, cap, ibchost.PortPath(types.HostPortID)); err != nil { panic(fmt.Sprintf("could not claim port capability: %v", err)) } } @@ -121,36 +128,27 @@ func (am AppModule) InitModule(ctx sdk.Context, controllerParams controllertypes func (AppModule) RegisterInvariants(ir sdk.InvariantRegistry) { } -// Route implements the AppModule interface -func (AppModule) Route() sdk.Route { - return sdk.NewRoute(types.RouterKey, nil) -} - -// NewHandler implements the AppModule interface -func (AppModule) NewHandler() sdk.Handler { - return nil -} - -// QuerierRoute implements the AppModule interface -func (AppModule) QuerierRoute() string { - return types.QuerierRoute -} - -// LegacyQuerierHandler implements the AppModule interface -func (am AppModule) LegacyQuerierHandler(legacyQuerierCdc *codec.LegacyAmino) sdk.Querier { - return nil -} - // 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.RegisterMsgServer(cfg.MsgServer(), controllerkeeper.NewMsgServerImpl(am.controllerKeeper)) + controllertypes.RegisterQueryServer(cfg.QueryServer(), am.controllerKeeper) + } + + if am.hostKeeper != nil { + hosttypes.RegisterQueryServer(cfg.QueryServer(), am.hostKeeper) + } + + m := controllerkeeper.NewMigrator(am.controllerKeeper) + if err := cfg.RegisterMigration(types.ModuleName, 1, m.AssertChannelCapabilityMigrations); err != nil { + panic(fmt.Sprintf("failed to migrate interchainaccounts app from version 1 to 2: %v", err)) + } } // InitGenesis performs genesis initialization for the interchain accounts module. // It returns no validator updates. func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, data json.RawMessage) []abci.ValidatorUpdate { - var genesisState types.GenesisState + var genesisState genesistypes.GenesisState cdc.MustUnmarshalJSON(data, &genesisState) if am.controllerKeeper != nil { @@ -167,8 +165,8 @@ func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, data json. // ExportGenesis returns the exported genesis state as raw bytes for the interchain accounts module func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.RawMessage { var ( - controllerGenesisState = types.DefaultControllerGenesis() - hostGenesisState = types.DefaultHostGenesis() + controllerGenesisState = genesistypes.DefaultControllerGenesis() + hostGenesisState = genesistypes.DefaultHostGenesis() ) if am.controllerKeeper != nil { @@ -179,13 +177,13 @@ func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.Raw hostGenesisState = hostkeeper.ExportGenesis(ctx, *am.hostKeeper) } - gs := types.NewGenesisState(controllerGenesisState, hostGenesisState) + gs := genesistypes.NewGenesisState(controllerGenesisState, hostGenesisState) return cdc.MustMarshalJSON(gs) } // 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 abci.RequestBeginBlock) { @@ -195,3 +193,20 @@ func (am AppModule) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) { func (am AppModule) EndBlock(ctx sdk.Context, req abci.RequestEndBlock) []abci.ValidatorUpdate { return []abci.ValidatorUpdate{} } + +// AppModuleSimulation functions + +// GenerateGenesisState creates a randomized GenState of the ics27 module. +func (AppModule) GenerateGenesisState(simState *module.SimulationState) { + simulation.RandomizedGenState(simState) +} + +// WeightedOperations is unimplemented. +func (am AppModule) WeightedOperations(_ module.SimulationState) []simtypes.WeightedOperation { + return nil +} + +// RegisterStoreDecoder registers a decoder for interchain accounts module's types +func (am AppModule) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) { + sdr[types.StoreKey] = simulation.NewDecodeStore() +} diff --git a/modules/apps/27-interchain-accounts/module_test.go b/modules/apps/27-interchain-accounts/module_test.go index be51b9d71c7..a1215a60ddb 100644 --- a/modules/apps/27-interchain-accounts/module_test.go +++ b/modules/apps/27-interchain-accounts/module_test.go @@ -3,17 +3,18 @@ package ica_test import ( "testing" + simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" "github.com/stretchr/testify/suite" "github.com/tendermint/tendermint/libs/log" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" dbm "github.com/tendermint/tm-db" - ica "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts" - controllertypes "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/controller/types" - hosttypes "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/host/types" - "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/types" - ibctesting "github.com/cosmos/ibc-go/v4/testing" - "github.com/cosmos/ibc-go/v4/testing/simapp" + ica "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts" + controllertypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/controller/types" + hosttypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/host/types" + "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types" + ibctesting "github.com/cosmos/ibc-go/v7/testing" + "github.com/cosmos/ibc-go/v7/testing/simapp" ) type InterchainAccountsTestSuite struct { @@ -32,7 +33,7 @@ func (suite *InterchainAccountsTestSuite) SetupTest() { func (suite *InterchainAccountsTestSuite) TestInitModule() { // setup and basic testing - app := simapp.NewSimApp(log.NewNopLogger(), dbm.NewMemDB(), nil, true, map[int64]bool{}, simapp.DefaultNodeHome, 5, simapp.MakeTestEncodingConfig(), simapp.EmptyAppOptions{}) + app := simapp.NewSimApp(log.NewNopLogger(), dbm.NewMemDB(), nil, true, map[int64]bool{}, simapp.DefaultNodeHome, 5, simapp.MakeTestEncodingConfig(), simtestutil.EmptyAppOptions{}) appModule, ok := app.GetModuleManager().Modules[types.ModuleName].(ica.AppModule) suite.Require().True(ok) @@ -59,7 +60,7 @@ func (suite *InterchainAccountsTestSuite) TestInitModule() { expAllowMessages := []string{"sdk.Msg"} hostParams.HostEnabled = true hostParams.AllowMessages = expAllowMessages - suite.Require().False(app.IBCKeeper.PortKeeper.IsBound(ctx, types.PortID)) + suite.Require().False(app.IBCKeeper.PortKeeper.IsBound(ctx, types.HostPortID)) testCases := []struct { name string @@ -98,7 +99,7 @@ func (suite *InterchainAccountsTestSuite) TestInitModule() { suite.SetupTest() // reset // reset app state - app = simapp.NewSimApp(log.NewNopLogger(), dbm.NewMemDB(), nil, true, map[int64]bool{}, simapp.DefaultNodeHome, 5, simapp.MakeTestEncodingConfig(), simapp.EmptyAppOptions{}) + app = simapp.NewSimApp(log.NewNopLogger(), dbm.NewMemDB(), nil, true, map[int64]bool{}, simapp.DefaultNodeHome, 5, simapp.MakeTestEncodingConfig(), simtestutil.EmptyAppOptions{}) header := tmproto.Header{ ChainID: "testchain", Height: 1, @@ -123,7 +124,7 @@ func (suite *InterchainAccountsTestSuite) TestInitModule() { suite.Require().True(hostParams.HostEnabled) suite.Require().Equal(expAllowMessages, hostParams.AllowMessages) - suite.Require().True(app.IBCKeeper.PortKeeper.IsBound(ctx, types.PortID)) + suite.Require().True(app.IBCKeeper.PortKeeper.IsBound(ctx, types.HostPortID)) } }) } diff --git a/modules/apps/27-interchain-accounts/simulation/decoder.go b/modules/apps/27-interchain-accounts/simulation/decoder.go new file mode 100644 index 00000000000..b0ceaaf1de3 --- /dev/null +++ b/modules/apps/27-interchain-accounts/simulation/decoder.go @@ -0,0 +1,30 @@ +package simulation + +import ( + "bytes" + "fmt" + + "github.com/cosmos/cosmos-sdk/types/kv" + + "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types" +) + +// NewDecodeStore returns a decoder function closure that unmarshals the KVPair's +// Value to the corresponding DenomTrace type. +func NewDecodeStore() func(kvA, kvB kv.Pair) string { + return func(kvA, kvB kv.Pair) string { + switch { + case bytes.Equal(kvA.Key[:len(types.PortKeyPrefix)], []byte(types.PortKeyPrefix)): + return fmt.Sprintf("Port A: %s\nPort B: %s", string(kvA.Value), string(kvB.Value)) + case bytes.Equal(kvA.Key[:len(types.OwnerKeyPrefix)], []byte(types.OwnerKeyPrefix)): + return fmt.Sprintf("Owner A: %s\nOwner B: %s", string(kvA.Value), string(kvB.Value)) + case bytes.Equal(kvA.Key[:len(types.ActiveChannelKeyPrefix)], []byte(types.ActiveChannelKeyPrefix)): + return fmt.Sprintf("ActiveChannel A: %s\nActiveChannel B: %s", string(kvA.Value), string(kvB.Value)) + case bytes.Equal(kvA.Key[:len(types.IsMiddlewareEnabledPrefix)], []byte(types.IsMiddlewareEnabledPrefix)): + return fmt.Sprintf("IsMiddlewareEnabled A: %s\nIsMiddlewareEnabled B: %s", string(kvA.Value), string(kvB.Value)) + + default: + panic(fmt.Sprintf("invalid %s key prefix %s", types.ModuleName, kvA.Key)) + } + } +} diff --git a/modules/apps/27-interchain-accounts/simulation/decoder_test.go b/modules/apps/27-interchain-accounts/simulation/decoder_test.go new file mode 100644 index 00000000000..81e899b620e --- /dev/null +++ b/modules/apps/27-interchain-accounts/simulation/decoder_test.go @@ -0,0 +1,64 @@ +package simulation_test + +import ( + "fmt" + "testing" + + "github.com/cosmos/cosmos-sdk/types/kv" + "github.com/stretchr/testify/require" + + "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/simulation" + "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types" + ibctesting "github.com/cosmos/ibc-go/v7/testing" +) + +func TestDecodeStore(t *testing.T) { + var ( + owner = "owner" + channelID = ibctesting.FirstChannelID + ) + + dec := simulation.NewDecodeStore() + + kvPairs := kv.Pairs{ + Pairs: []kv.Pair{ + { + Key: []byte(types.PortKeyPrefix), + Value: []byte(types.HostPortID), + }, + { + Key: []byte(types.OwnerKeyPrefix), + Value: []byte("owner"), + }, + { + Key: []byte(types.ActiveChannelKeyPrefix), + Value: []byte("channel-0"), + }, + { + Key: []byte(types.IsMiddlewareEnabledPrefix), + Value: []byte("false"), + }, + }, + } + tests := []struct { + name string + expectedLog string + }{ + {"PortID", fmt.Sprintf("Port A: %s\nPort B: %s", types.HostPortID, types.HostPortID)}, + {"Owner", fmt.Sprintf("Owner A: %s\nOwner B: %s", owner, owner)}, + {"ActiveChannel", fmt.Sprintf("ActiveChannel A: %s\nActiveChannel B: %s", channelID, channelID)}, + {"IsMiddlewareEnabled", fmt.Sprintf("IsMiddlewareEnabled A: %s\nIsMiddlewareEnabled B: %s", "false", "false")}, + {"other", ""}, + } + + for i, tt := range tests { + i, tt := i, tt + t.Run(tt.name, func(t *testing.T) { + if i == len(tests)-1 { + require.Panics(t, func() { dec(kvPairs.Pairs[i], kvPairs.Pairs[i]) }, tt.name) + } else { + require.Equal(t, tt.expectedLog, dec(kvPairs.Pairs[i], kvPairs.Pairs[i]), tt.name) + } + }) + } +} diff --git a/modules/apps/27-interchain-accounts/simulation/genesis.go b/modules/apps/27-interchain-accounts/simulation/genesis.go new file mode 100644 index 00000000000..c90ecaf7003 --- /dev/null +++ b/modules/apps/27-interchain-accounts/simulation/genesis.go @@ -0,0 +1,70 @@ +package simulation + +import ( + "encoding/json" + "fmt" + "math/rand" + + "github.com/cosmos/cosmos-sdk/types/module" + + controllertypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/controller/types" + genesistypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/genesis/types" + hosttypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/host/types" + "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types" +) + +// RandomEnabled randomized controller or host enabled param with 75% prob of being true. +func RandomEnabled(r *rand.Rand) bool { + return r.Int63n(101) <= 75 +} + +// RandomizedGenState generates a random GenesisState for ics27. +// Only the params are non nil +func RandomizedGenState(simState *module.SimulationState) { + var controllerEnabled bool + simState.AppParams.GetOrGenerate( + simState.Cdc, string(controllertypes.KeyControllerEnabled), &controllerEnabled, simState.Rand, + func(r *rand.Rand) { controllerEnabled = RandomEnabled(r) }, + ) + + controllerParams := controllertypes.Params{ + ControllerEnabled: controllerEnabled, + } + + controllerGenesisState := genesistypes.ControllerGenesisState{ + ActiveChannels: nil, + InterchainAccounts: nil, + Ports: []string{}, + Params: controllerParams, + } + + var hostEnabled bool + simState.AppParams.GetOrGenerate( + simState.Cdc, string(hosttypes.KeyHostEnabled), &hostEnabled, simState.Rand, + func(r *rand.Rand) { hostEnabled = RandomEnabled(r) }, + ) + + hostParams := hosttypes.Params{ + HostEnabled: hostEnabled, + AllowMessages: []string{"*"}, // allow all messages + } + + hostGenesisState := genesistypes.HostGenesisState{ + ActiveChannels: nil, + InterchainAccounts: nil, + Port: types.HostPortID, + Params: hostParams, + } + + icaGenesis := genesistypes.GenesisState{ + ControllerGenesisState: controllerGenesisState, + HostGenesisState: hostGenesisState, + } + + bz, err := json.MarshalIndent(&icaGenesis, "", " ") + if err != nil { + panic(err) + } + fmt.Printf("Selected randomly generated %s parameters:\n%s\n", types.ModuleName, bz) + simState.GenState[types.ModuleName] = simState.Cdc.MustMarshalJSON(&icaGenesis) +} diff --git a/modules/apps/27-interchain-accounts/simulation/genesis_test.go b/modules/apps/27-interchain-accounts/simulation/genesis_test.go new file mode 100644 index 00000000000..28b927643b9 --- /dev/null +++ b/modules/apps/27-interchain-accounts/simulation/genesis_test.go @@ -0,0 +1,56 @@ +package simulation_test + +import ( + "encoding/json" + "math/rand" + "testing" + + "cosmossdk.io/math" + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + "github.com/cosmos/cosmos-sdk/types/module" + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + "github.com/stretchr/testify/require" + + genesistypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/genesis/types" + "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/simulation" + "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types" +) + +// TestRandomizedGenState tests the normal scenario of applying RandomizedGenState. +// Abonormal scenarios are not tested here. +func TestRandomizedGenState(t *testing.T) { + interfaceRegistry := codectypes.NewInterfaceRegistry() + cryptocodec.RegisterInterfaces(interfaceRegistry) + cdc := codec.NewProtoCodec(interfaceRegistry) + + s := rand.NewSource(1) // 1 is the seed + r := rand.New(s) + + simState := module.SimulationState{ + AppParams: make(simtypes.AppParams), + Cdc: cdc, + Rand: r, + NumBonded: 3, + Accounts: simtypes.RandomAccounts(r, 3), + InitialStake: math.NewInt(1000), + GenState: make(map[string]json.RawMessage), + } + + simulation.RandomizedGenState(&simState) + + var icaGenesis genesistypes.GenesisState + simState.Cdc.MustUnmarshalJSON(simState.GenState[types.ModuleName], &icaGenesis) + + require.True(t, icaGenesis.ControllerGenesisState.Params.ControllerEnabled) + require.Empty(t, icaGenesis.ControllerGenesisState.ActiveChannels) + require.Empty(t, icaGenesis.ControllerGenesisState.InterchainAccounts) + require.Empty(t, icaGenesis.ControllerGenesisState.Ports) + + require.True(t, icaGenesis.HostGenesisState.Params.HostEnabled) + require.Equal(t, []string{"*"}, icaGenesis.HostGenesisState.Params.AllowMessages) + require.Equal(t, types.HostPortID, icaGenesis.HostGenesisState.Port) + require.Empty(t, icaGenesis.ControllerGenesisState.ActiveChannels) + require.Empty(t, icaGenesis.ControllerGenesisState.InterchainAccounts) +} diff --git a/modules/apps/27-interchain-accounts/types/account.go b/modules/apps/27-interchain-accounts/types/account.go index 38d7c4c5fe5..472b509818c 100644 --- a/modules/apps/27-interchain-accounts/types/account.go +++ b/modules/apps/27-interchain-accounts/types/account.go @@ -5,10 +5,10 @@ import ( "regexp" "strings" + errorsmod "cosmossdk.io/errors" crypto "github.com/cosmos/cosmos-sdk/crypto/types" sdk "github.com/cosmos/cosmos-sdk/types" sdkaddress "github.com/cosmos/cosmos-sdk/types/address" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" yaml "gopkg.in/yaml.v2" ) @@ -39,17 +39,24 @@ type interchainAccountPretty struct { AccountOwner string `json:"account_owner" yaml:"account_owner"` } -// 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 -func GenerateAddress(moduleAccAddr sdk.AccAddress, connectionID, portID string) sdk.AccAddress { - return sdk.AccAddress(sdkaddress.Derive(moduleAccAddr, []byte(connectionID+portID))) +// GenerateAddress 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 GenerateAddress(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 { if !isValidAddr(addr) || len(addr) > DefaultMaxAddrLength { - return sdkerrors.Wrapf( + return errorsmod.Wrapf( ErrInvalidAccountAddress, "address must contain strictly alphanumeric characters, not exceeding %d characters in length", DefaultMaxAddrLength, @@ -69,18 +76,18 @@ func NewInterchainAccount(ba *authtypes.BaseAccount, accountOwner string) *Inter // SetPubKey implements the authtypes.AccountI interface func (ia InterchainAccount) SetPubKey(pubKey crypto.PubKey) error { - return sdkerrors.Wrap(ErrUnsupported, "cannot set public key for interchain account") + return errorsmod.Wrap(ErrUnsupported, "cannot set public key for interchain account") } // SetSequence implements the authtypes.AccountI interface func (ia InterchainAccount) SetSequence(seq uint64) error { - return sdkerrors.Wrap(ErrUnsupported, "cannot set sequence number for interchain account") + return errorsmod.Wrap(ErrUnsupported, "cannot set sequence number for interchain account") } // Validate implements basic validation of the InterchainAccount func (ia InterchainAccount) Validate() error { if strings.TrimSpace(ia.AccountOwner) == "" { - return sdkerrors.Wrap(ErrInvalidAccountAddress, "AccountOwner cannot be empty") + return errorsmod.Wrap(ErrInvalidAccountAddress, "AccountOwner cannot be empty") } return ia.BaseAccount.Validate() diff --git a/modules/apps/27-interchain-accounts/types/account.pb.go b/modules/apps/27-interchain-accounts/types/account.pb.go index 4978a25f4a7..67273dfd591 100644 --- a/modules/apps/27-interchain-accounts/types/account.pb.go +++ b/modules/apps/27-interchain-accounts/types/account.pb.go @@ -5,10 +5,10 @@ package types import ( fmt "fmt" + _ "github.com/cosmos/cosmos-proto" types "github.com/cosmos/cosmos-sdk/x/auth/types" - _ "github.com/gogo/protobuf/gogoproto" - proto "github.com/gogo/protobuf/proto" - _ "github.com/regen-network/cosmos-proto" + _ "github.com/cosmos/gogoproto/gogoproto" + proto "github.com/cosmos/gogoproto/proto" io "io" math "math" math_bits "math/bits" @@ -72,7 +72,7 @@ func init() { } var fileDescriptor_5561bd92625bf7da = []byte{ - // 341 bytes of a gzipped FileDescriptorProto + // 344 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x32, 0xcd, 0x4c, 0x4a, 0xd6, 0x4f, 0x2c, 0x28, 0xc8, 0xc9, 0x4c, 0x4e, 0x2c, 0xc9, 0xcc, 0xcf, 0x2b, 0xd6, 0xcf, 0xcc, 0x2b, 0x49, 0x2d, 0x4a, 0xce, 0x48, 0xcc, 0xcc, 0x8b, 0x4f, 0x4c, 0x4e, 0xce, 0x2f, 0xcd, 0x2b, 0x29, @@ -80,21 +80,21 @@ var fileDescriptor_5561bd92625bf7da = []byte{ 0x92, 0xf5, 0x90, 0xb5, 0xe9, 0x61, 0xd1, 0xa6, 0x57, 0x66, 0x28, 0x25, 0x99, 0x9c, 0x5f, 0x9c, 0x9b, 0x5f, 0x1c, 0x0f, 0xd6, 0xa6, 0x0f, 0xe1, 0x40, 0xcc, 0x90, 0x12, 0x49, 0xcf, 0x4f, 0xcf, 0x87, 0x88, 0x83, 0x58, 0x50, 0x51, 0x39, 0x88, 0x1a, 0xfd, 0xc4, 0xd2, 0x92, 0x0c, 0xfd, 0x32, - 0xc3, 0xa4, 0xd4, 0x92, 0x44, 0x43, 0x30, 0x07, 0x22, 0xaf, 0x74, 0x85, 0x91, 0x4b, 0xd0, 0x13, + 0xc3, 0xa4, 0xd4, 0x92, 0x44, 0x43, 0x30, 0x07, 0x22, 0xaf, 0xf4, 0x87, 0x91, 0x4b, 0xd0, 0x13, 0x6e, 0x97, 0x23, 0xc4, 0x2a, 0xa1, 0x04, 0x2e, 0x9e, 0xa4, 0xc4, 0xe2, 0x54, 0x98, 0xd5, 0x12, 0x8c, 0x0a, 0x8c, 0x1a, 0xdc, 0x46, 0x0a, 0x7a, 0x50, 0x0b, 0xc1, 0xfa, 0xa1, 0x86, 0xe9, 0x39, 0x25, 0x16, 0xa7, 0x42, 0xf5, 0x39, 0x49, 0x5f, 0xb8, 0x27, 0xcf, 0xf8, 0xe9, 0x9e, 0xbc, 0x70, 0x65, 0x62, 0x6e, 0x8e, 0x95, 0x12, 0xb2, 0x19, 0x4a, 0x41, 0xdc, 0x49, 0x08, 0x95, 0x42, 0xb6, 0x5c, 0xbc, 0x50, 0x89, 0xf8, 0xfc, 0xf2, 0xbc, 0xd4, 0x22, 0x09, 0x26, 0x05, 0x46, 0x0d, 0x4e, 0x27, 0x89, 0x4f, 0xf7, 0xe4, 0x45, 0x20, 0x9a, 0x51, 0xa4, 0x95, 0x82, 0x78, 0xa0, 0x7c, 0x7f, - 0x10, 0xd7, 0x4a, 0xae, 0x63, 0x81, 0x3c, 0xc3, 0x8c, 0x05, 0xf2, 0x0c, 0x97, 0xb6, 0xe8, 0x0a, - 0x61, 0xb8, 0xdf, 0xd3, 0x29, 0xfe, 0xc4, 0x23, 0x39, 0xc6, 0x0b, 0x8f, 0xe4, 0x18, 0x1f, 0x3c, - 0x92, 0x63, 0x9c, 0xf0, 0x58, 0x8e, 0xe1, 0xc2, 0x63, 0x39, 0x86, 0x1b, 0x8f, 0xe5, 0x18, 0xa2, - 0x5c, 0xd3, 0x33, 0x4b, 0x32, 0x4a, 0x93, 0xf4, 0x92, 0xf3, 0x73, 0xa1, 0xe1, 0xa7, 0x9f, 0x99, - 0x94, 0xac, 0x9b, 0x9e, 0xaf, 0x5f, 0x66, 0xa2, 0x9f, 0x9b, 0x9f, 0x52, 0x9a, 0x93, 0x5a, 0x0c, - 0x8a, 0xc1, 0x62, 0x7d, 0x23, 0x73, 0x5d, 0x44, 0x2c, 0xe8, 0xc2, 0x23, 0xaf, 0xa4, 0xb2, 0x20, - 0xb5, 0x38, 0x89, 0x0d, 0x1c, 0x7c, 0xc6, 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0x4e, 0xd7, 0x23, - 0x15, 0xf1, 0x01, 0x00, 0x00, + 0x10, 0xd7, 0xca, 0xad, 0x63, 0x81, 0x3c, 0xc3, 0x8c, 0x05, 0xf2, 0x0c, 0xa7, 0xb6, 0xe8, 0x5a, + 0x11, 0x19, 0x78, 0x7a, 0x18, 0xfe, 0xf4, 0x74, 0x8a, 0x3f, 0xf1, 0x48, 0x8e, 0xf1, 0xc2, 0x23, + 0x39, 0xc6, 0x07, 0x8f, 0xe4, 0x18, 0x27, 0x3c, 0x96, 0x63, 0xb8, 0xf0, 0x58, 0x8e, 0xe1, 0xc6, + 0x63, 0x39, 0x86, 0x28, 0xd7, 0xf4, 0xcc, 0x92, 0x8c, 0xd2, 0x24, 0xbd, 0xe4, 0xfc, 0x5c, 0x68, + 0x38, 0xeb, 0x67, 0x26, 0x25, 0xeb, 0xa6, 0xe7, 0xeb, 0x97, 0x99, 0xeb, 0xe7, 0xe6, 0xa7, 0x94, + 0xe6, 0xa4, 0x16, 0x83, 0x62, 0xba, 0x58, 0xdf, 0xc8, 0x5c, 0x17, 0x61, 0xa1, 0x2e, 0x3c, 0x92, + 0x4b, 0x2a, 0x0b, 0x52, 0x8b, 0x93, 0xd8, 0xc0, 0xc1, 0x6c, 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, + 0xae, 0x11, 0x4f, 0x20, 0x19, 0x02, 0x00, 0x00, } func (m *InterchainAccount) Marshal() (dAtA []byte, err error) { diff --git a/modules/apps/27-interchain-accounts/types/account_test.go b/modules/apps/27-interchain-accounts/types/account_test.go index 19684cb8c80..8acef7e33a9 100644 --- a/modules/apps/27-interchain-accounts/types/account_test.go +++ b/modules/apps/27-interchain-accounts/types/account_test.go @@ -10,15 +10,15 @@ import ( authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" "github.com/stretchr/testify/suite" - "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/types" - ibctesting "github.com/cosmos/ibc-go/v4/testing" + "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types" + ibctesting "github.com/cosmos/ibc-go/v7/testing" ) var ( // TestOwnerAddress defines a reusable bech32 address for testing purposes TestOwnerAddress = "cosmos17dtl0mjt3t77kpuhg2edqzjpszulwhgzuj9ljs" - // TestPortID defines a resuable port identifier for testing purposes + // TestPortID defines a reusable port identifier for testing purposes TestPortID, _ = types.NewControllerPortID(TestOwnerAddress) ) @@ -43,7 +43,7 @@ func TestTypesTestSuite(t *testing.T) { } func (suite *TypesTestSuite) TestGenerateAddress() { - addr := types.GenerateAddress([]byte{}, "test-connection-id", "test-port-id") + addr := types.GenerateAddress(suite.chainA.GetContext(), "test-connection-id", "test-port-id") accAddr, err := sdk.AccAddressFromBech32(addr.String()) suite.Require().NoError(err, "TestGenerateAddress failed") diff --git a/modules/apps/27-interchain-accounts/types/codec.go b/modules/apps/27-interchain-accounts/types/codec.go index 033c264f9e7..b0ba7b8902b 100644 --- a/modules/apps/27-interchain-accounts/types/codec.go +++ b/modules/apps/27-interchain-accounts/types/codec.go @@ -1,11 +1,12 @@ package types import ( + errorsmod "cosmossdk.io/errors" "github.com/cosmos/cosmos-sdk/codec" codectypes "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/cosmos/gogoproto/proto" ) // ModuleCdc references the global interchain accounts module codec. Note, the codec @@ -15,8 +16,8 @@ import ( // defined at the application level. var ModuleCdc = codec.NewProtoCodec(codectypes.NewInterfaceRegistry()) -// RegisterInterfaces registers the concrete InterchainAccount implementation against the associated -// x/auth AccountI and GenesisAccount interfaces +// RegisterInterfaces registers the interchain accounts controller types and the concrete InterchainAccount implementation +// against the associated x/auth AccountI and GenesisAccount interfaces. func RegisterInterfaces(registry codectypes.InterfaceRegistry) { registry.RegisterImplementations((*authtypes.AccountI)(nil), &InterchainAccount{}) registry.RegisterImplementations((*authtypes.GenesisAccount)(nil), &InterchainAccount{}) @@ -25,10 +26,10 @@ func RegisterInterfaces(registry codectypes.InterfaceRegistry) { // SerializeCosmosTx serializes a slice of sdk.Msg's using the CosmosTx type. The sdk.Msg's are // packed into Any's and inserted into the Messages field of a CosmosTx. The proto marshaled CosmosTx // bytes are returned. Only the ProtoCodec is supported for serializing messages. -func SerializeCosmosTx(cdc codec.BinaryCodec, msgs []sdk.Msg) (bz []byte, err error) { +func SerializeCosmosTx(cdc codec.BinaryCodec, msgs []proto.Message) (bz []byte, err error) { // only ProtoCodec is supported if _, ok := cdc.(*codec.ProtoCodec); !ok { - return nil, sdkerrors.Wrap(ErrInvalidCodec, "only ProtoCodec is supported for receiving messages on the host chain") + return nil, errorsmod.Wrap(ErrInvalidCodec, "only ProtoCodec is supported for receiving messages on the host chain") } msgAnys := make([]*codectypes.Any, len(msgs)) @@ -58,7 +59,7 @@ func SerializeCosmosTx(cdc codec.BinaryCodec, msgs []sdk.Msg) (bz []byte, err er func DeserializeCosmosTx(cdc codec.BinaryCodec, data []byte) ([]sdk.Msg, error) { // only ProtoCodec is supported if _, ok := cdc.(*codec.ProtoCodec); !ok { - return nil, sdkerrors.Wrap(ErrInvalidCodec, "only ProtoCodec is supported for receiving messages on the host chain") + return nil, errorsmod.Wrap(ErrInvalidCodec, "only ProtoCodec is supported for receiving messages on the host chain") } var cosmosTx CosmosTx @@ -68,10 +69,10 @@ func DeserializeCosmosTx(cdc codec.BinaryCodec, data []byte) ([]sdk.Msg, error) msgs := make([]sdk.Msg, len(cosmosTx.Messages)) - for i, any := range cosmosTx.Messages { + for i, protoAny := range cosmosTx.Messages { var msg sdk.Msg - err := cdc.UnpackAny(any, &msg) + err := cdc.UnpackAny(protoAny, &msg) if err != nil { return nil, err } diff --git a/modules/apps/27-interchain-accounts/types/codec_test.go b/modules/apps/27-interchain-accounts/types/codec_test.go index 51fe1128fba..1df2dd4d66a 100644 --- a/modules/apps/27-interchain-accounts/types/codec_test.go +++ b/modules/apps/27-interchain-accounts/types/codec_test.go @@ -4,19 +4,13 @@ import ( "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" - govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" + "github.com/cosmos/gogoproto/proto" - "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/types" - "github.com/cosmos/ibc-go/v4/testing/simapp" + "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types" + "github.com/cosmos/ibc-go/v7/testing/simapp" ) -// caseRawBytes defines a helper struct, used for testing codec operations -type caseRawBytes struct { - name string - bz []byte - expPass bool -} - // mockSdkMsg defines a mock struct, used for testing codec error scenarios type mockSdkMsg struct{} @@ -46,12 +40,12 @@ func (mockSdkMsg) GetSigners() []sdk.AccAddress { func (suite *TypesTestSuite) TestSerializeAndDeserializeCosmosTx() { testCases := []struct { name string - msgs []sdk.Msg + msgs []proto.Message expPass bool }{ { "single msg", - []sdk.Msg{ + []proto.Message{ &banktypes.MsgSend{ FromAddress: TestOwnerAddress, ToAddress: TestOwnerAddress, @@ -62,7 +56,7 @@ func (suite *TypesTestSuite) TestSerializeAndDeserializeCosmosTx() { }, { "multiple msgs, same types", - []sdk.Msg{ + []proto.Message{ &banktypes.MsgSend{ FromAddress: TestOwnerAddress, ToAddress: TestOwnerAddress, @@ -78,7 +72,7 @@ func (suite *TypesTestSuite) TestSerializeAndDeserializeCosmosTx() { }, { "multiple msgs, different types", - []sdk.Msg{ + []proto.Message{ &banktypes.MsgSend{ FromAddress: TestOwnerAddress, ToAddress: TestOwnerAddress, @@ -93,14 +87,14 @@ func (suite *TypesTestSuite) TestSerializeAndDeserializeCosmosTx() { }, { "unregistered msg type", - []sdk.Msg{ + []proto.Message{ &mockSdkMsg{}, }, false, }, { "multiple unregistered msg types", - []sdk.Msg{ + []proto.Message{ &mockSdkMsg{}, &mockSdkMsg{}, &mockSdkMsg{}, @@ -109,25 +103,35 @@ func (suite *TypesTestSuite) TestSerializeAndDeserializeCosmosTx() { }, } - testCasesAny := []caseRawBytes{} - for _, tc := range testCases { - bz, err := types.SerializeCosmosTx(simapp.MakeTestEncodingConfig().Marshaler, tc.msgs) - suite.Require().NoError(err, tc.name) - - testCasesAny = append(testCasesAny, caseRawBytes{tc.name, bz, tc.expPass}) - } + tc := tc - for i, tc := range testCasesAny { - msgs, err := types.DeserializeCosmosTx(simapp.MakeTestEncodingConfig().Marshaler, tc.bz) - if tc.expPass { + suite.Run(tc.name, func() { + bz, err := types.SerializeCosmosTx(simapp.MakeTestEncodingConfig().Marshaler, tc.msgs) suite.Require().NoError(err, tc.name) - suite.Require().Equal(testCases[i].msgs, msgs, tc.name) - } else { - suite.Require().Error(err, tc.name) - } + + msgs, err := types.DeserializeCosmosTx(simapp.MakeTestEncodingConfig().Marshaler, bz) + if tc.expPass { + suite.Require().NoError(err, tc.name) + } else { + suite.Require().Error(err, tc.name) + } + + for i, msg := range msgs { + suite.Require().Equal(tc.msgs[i], msg) + } + }) } + // test serializing non sdk.Msg type + bz, err := types.SerializeCosmosTx(simapp.MakeTestEncodingConfig().Marshaler, []proto.Message{&banktypes.MsgSendResponse{}}) + suite.Require().NoError(err) + suite.Require().NotEmpty(bz) + + // test deserializing unknown bytes + _, err = types.DeserializeCosmosTx(simapp.MakeTestEncodingConfig().Marshaler, bz) + suite.Require().Error(err) // unregistered type + // test deserializing unknown bytes msgs, err := types.DeserializeCosmosTx(simapp.MakeTestEncodingConfig().Marshaler, []byte("invalid")) suite.Require().Error(err) @@ -141,7 +145,7 @@ func (suite *TypesTestSuite) TestDeserializeAndSerializeCosmosTxWithAmino() { cdc := codec.NewLegacyAmino() marshaler := codec.NewAminoCodec(cdc) - msgs, err := types.SerializeCosmosTx(marshaler, []sdk.Msg{&banktypes.MsgSend{}}) + msgs, err := types.SerializeCosmosTx(marshaler, []proto.Message{&banktypes.MsgSend{}}) suite.Require().Error(err) suite.Require().Empty(msgs) diff --git a/modules/apps/27-interchain-accounts/types/errors.go b/modules/apps/27-interchain-accounts/types/errors.go index 7bb391dbe93..06989327dfb 100644 --- a/modules/apps/27-interchain-accounts/types/errors.go +++ b/modules/apps/27-interchain-accounts/types/errors.go @@ -1,25 +1,26 @@ package types import ( - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + errorsmod "cosmossdk.io/errors" ) var ( - ErrUnknownDataType = sdkerrors.Register(ModuleName, 2, "unknown data type") - ErrAccountAlreadyExist = sdkerrors.Register(ModuleName, 3, "account already exist") - ErrPortAlreadyBound = sdkerrors.Register(ModuleName, 4, "port is already bound") - ErrInvalidChannelFlow = sdkerrors.Register(ModuleName, 5, "invalid message sent to channel end") - ErrInvalidOutgoingData = sdkerrors.Register(ModuleName, 6, "invalid outgoing data") - ErrInvalidRoute = sdkerrors.Register(ModuleName, 7, "invalid route") - ErrInterchainAccountNotFound = sdkerrors.Register(ModuleName, 8, "interchain account not found") - ErrInterchainAccountAlreadySet = sdkerrors.Register(ModuleName, 9, "interchain account is already set") - ErrActiveChannelAlreadySet = sdkerrors.Register(ModuleName, 10, "active channel already set for this owner") - ErrActiveChannelNotFound = sdkerrors.Register(ModuleName, 11, "no active channel for this owner") - ErrInvalidVersion = sdkerrors.Register(ModuleName, 12, "invalid interchain accounts version") - ErrInvalidAccountAddress = sdkerrors.Register(ModuleName, 13, "invalid account address") - ErrUnsupported = sdkerrors.Register(ModuleName, 14, "interchain account does not support this action") - ErrInvalidControllerPort = sdkerrors.Register(ModuleName, 15, "invalid controller port") - 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") + ErrUnknownDataType = errorsmod.Register(ModuleName, 2, "unknown data type") + ErrAccountAlreadyExist = errorsmod.Register(ModuleName, 3, "account already exist") + ErrPortAlreadyBound = errorsmod.Register(ModuleName, 4, "port is already bound") + ErrInvalidChannelFlow = errorsmod.Register(ModuleName, 5, "invalid message sent to channel end") + ErrInvalidOutgoingData = errorsmod.Register(ModuleName, 6, "invalid outgoing data") + ErrInvalidRoute = errorsmod.Register(ModuleName, 7, "invalid route") + ErrInterchainAccountNotFound = errorsmod.Register(ModuleName, 8, "interchain account not found") + ErrInterchainAccountAlreadySet = errorsmod.Register(ModuleName, 9, "interchain account is already set") + ErrActiveChannelAlreadySet = errorsmod.Register(ModuleName, 10, "active channel already set for this owner") + ErrActiveChannelNotFound = errorsmod.Register(ModuleName, 11, "no active channel for this owner") + ErrInvalidVersion = errorsmod.Register(ModuleName, 12, "invalid interchain accounts version") + ErrInvalidAccountAddress = errorsmod.Register(ModuleName, 13, "invalid account address") + ErrUnsupported = errorsmod.Register(ModuleName, 14, "interchain account does not support this action") + ErrInvalidControllerPort = errorsmod.Register(ModuleName, 15, "invalid controller port") + ErrInvalidHostPort = errorsmod.Register(ModuleName, 16, "invalid host port") + ErrInvalidTimeoutTimestamp = errorsmod.Register(ModuleName, 17, "timeout timestamp must be in the future") + ErrInvalidCodec = errorsmod.Register(ModuleName, 18, "codec is not supported") + ErrInvalidAccountReopening = errorsmod.Register(ModuleName, 19, "invalid account reopening") ) diff --git a/modules/apps/27-interchain-accounts/types/expected_keepers.go b/modules/apps/27-interchain-accounts/types/expected_keepers.go index e4873c561fd..fdb9374da00 100644 --- a/modules/apps/27-interchain-accounts/types/expected_keepers.go +++ b/modules/apps/27-interchain-accounts/types/expected_keepers.go @@ -5,8 +5,8 @@ import ( authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" - ibcexported "github.com/cosmos/ibc-go/v4/modules/core/exported" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + ibcexported "github.com/cosmos/ibc-go/v7/modules/core/exported" ) // AccountKeeper defines the expected account keeper @@ -18,17 +18,12 @@ type AccountKeeper interface { GetModuleAddress(name string) sdk.AccAddress } -// ICS4Wrapper defines the expected ICS4Wrapper for middleware -type ICS4Wrapper interface { - SendPacket(ctx sdk.Context, channelCap *capabilitytypes.Capability, packet ibcexported.PacketI) error - GetAppVersion(ctx sdk.Context, portID, channelID string) (string, bool) -} - // ChannelKeeper defines the expected IBC channel keeper type ChannelKeeper interface { GetChannel(ctx sdk.Context, srcPort, srcChan string) (channel channeltypes.Channel, found bool) GetNextSequenceSend(ctx sdk.Context, portID, channelID string) (uint64, bool) GetConnection(ctx sdk.Context, connectionID string) (ibcexported.ConnectionI, error) + GetAllChannelsWithPortPrefix(ctx sdk.Context, portPrefix string) []channeltypes.IdentifiedChannel } // PortKeeper defines the expected IBC port keeper diff --git a/modules/apps/27-interchain-accounts/types/keys.go b/modules/apps/27-interchain-accounts/types/keys.go index 2bf05ffda3f..2a062130ce2 100644 --- a/modules/apps/27-interchain-accounts/types/keys.go +++ b/modules/apps/27-interchain-accounts/types/keys.go @@ -8,11 +8,11 @@ const ( // ModuleName defines the interchain accounts module name ModuleName = "interchainaccounts" - // PortID is the default port id that the interchain accounts host submodule binds to - PortID = "icahost" + // HostPortID is the default port id that the interchain accounts host submodule binds to + HostPortID = "icahost" - // PortPrefix is the default port prefix that the interchain accounts controller submodule binds to - PortPrefix = "icacontroller-" + // ControllerPortPrefix is the default port prefix that the interchain accounts controller submodule binds to + ControllerPortPrefix = "icacontroller-" // Version defines the current version for interchain accounts Version = "ics27-1" @@ -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 ( @@ -36,6 +39,15 @@ var ( // PortKeyPrefix defines the key prefix used to store ports PortKeyPrefix = "port" + + // IsMiddlewareEnabledPrefix defines the key prefix used to store a flag for legacy API callback routing via ibc middleware + IsMiddlewareEnabledPrefix = "isMiddlewareEnabled" + + // MiddlewareEnabled is the value used to signal that controller middleware is enabled + MiddlewareEnabled = []byte{0x01} + + // MiddlewareDisabled is the value used to signal that controller midleware is disabled + MiddlewareDisabled = []byte{0x02} ) // KeyActiveChannel creates and returns a new key used for active channels store operations @@ -52,3 +64,8 @@ func KeyOwnerAccount(portID, connectionID string) []byte { func KeyPort(portID string) []byte { return []byte(fmt.Sprintf("%s/%s", PortKeyPrefix, portID)) } + +// KeyIsMiddlewareEnabled creates and returns a new key used for signaling legacy API callback routing via ibc middleware +func KeyIsMiddlewareEnabled(portID, connectionID string) []byte { + return []byte(fmt.Sprintf("%s/%s/%s", IsMiddlewareEnabledPrefix, portID, connectionID)) +} diff --git a/modules/apps/27-interchain-accounts/types/keys_test.go b/modules/apps/27-interchain-accounts/types/keys_test.go index 58b5b9c71b3..d9255850dea 100644 --- a/modules/apps/27-interchain-accounts/types/keys_test.go +++ b/modules/apps/27-interchain-accounts/types/keys_test.go @@ -1,7 +1,10 @@ package types_test import ( - "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/types" + fmt "fmt" + + "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types" + ibctesting "github.com/cosmos/ibc-go/v7/testing" ) func (suite *TypesTestSuite) TestKeyActiveChannel() { @@ -13,3 +16,8 @@ func (suite *TypesTestSuite) TestKeyOwnerAccount() { key := types.KeyOwnerAccount("port-id", "connection-id") suite.Require().Equal("owner/port-id/connection-id", string(key)) } + +func (suite *TypesTestSuite) TestKeyIsMiddlewareEnabled() { + key := types.KeyIsMiddlewareEnabled(ibctesting.MockPort, ibctesting.FirstChannelID) + suite.Require().Equal(fmt.Sprintf("%s/%s/%s", types.IsMiddlewareEnabledPrefix, ibctesting.MockPort, ibctesting.FirstChannelID), string(key)) +} diff --git a/modules/apps/27-interchain-accounts/types/metadata.go b/modules/apps/27-interchain-accounts/types/metadata.go index 148e5221f28..ee986a006f9 100644 --- a/modules/apps/27-interchain-accounts/types/metadata.go +++ b/modules/apps/27-interchain-accounts/types/metadata.go @@ -1,10 +1,10 @@ package types import ( + errorsmod "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - connectiontypes "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types" + connectiontypes "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" ) const ( @@ -67,11 +67,11 @@ func IsPreviousMetadataEqual(previousVersion string, metadata Metadata) bool { // ValidateControllerMetadata performs validation of the provided ICS27 controller metadata parameters func ValidateControllerMetadata(ctx sdk.Context, channelKeeper ChannelKeeper, connectionHops []string, metadata Metadata) error { if !isSupportedEncoding(metadata.Encoding) { - return sdkerrors.Wrapf(ErrInvalidCodec, "unsupported encoding format %s", metadata.Encoding) + return errorsmod.Wrapf(ErrInvalidCodec, "unsupported encoding format %s", metadata.Encoding) } if !isSupportedTxType(metadata.TxType) { - return sdkerrors.Wrapf(ErrUnknownDataType, "unsupported transaction type %s", metadata.TxType) + return errorsmod.Wrapf(ErrUnknownDataType, "unsupported transaction type %s", metadata.TxType) } connection, err := channelKeeper.GetConnection(ctx, connectionHops[0]) @@ -90,7 +90,7 @@ func ValidateControllerMetadata(ctx sdk.Context, channelKeeper ChannelKeeper, co } if metadata.Version != Version { - return sdkerrors.Wrapf(ErrInvalidVersion, "expected %s, got %s", Version, metadata.Version) + return errorsmod.Wrapf(ErrInvalidVersion, "expected %s, got %s", Version, metadata.Version) } return nil @@ -99,11 +99,11 @@ func ValidateControllerMetadata(ctx sdk.Context, channelKeeper ChannelKeeper, co // ValidateHostMetadata performs validation of the provided ICS27 host metadata parameters func ValidateHostMetadata(ctx sdk.Context, channelKeeper ChannelKeeper, connectionHops []string, metadata Metadata) error { if !isSupportedEncoding(metadata.Encoding) { - return sdkerrors.Wrapf(ErrInvalidCodec, "unsupported encoding format %s", metadata.Encoding) + return errorsmod.Wrapf(ErrInvalidCodec, "unsupported encoding format %s", metadata.Encoding) } if !isSupportedTxType(metadata.TxType) { - return sdkerrors.Wrapf(ErrUnknownDataType, "unsupported transaction type %s", metadata.TxType) + return errorsmod.Wrapf(ErrUnknownDataType, "unsupported transaction type %s", metadata.TxType) } connection, err := channelKeeper.GetConnection(ctx, connectionHops[0]) @@ -122,7 +122,7 @@ func ValidateHostMetadata(ctx sdk.Context, channelKeeper ChannelKeeper, connecti } if metadata.Version != Version { - return sdkerrors.Wrapf(ErrInvalidVersion, "expected %s, got %s", Version, metadata.Version) + return errorsmod.Wrapf(ErrInvalidVersion, "expected %s, got %s", Version, metadata.Version) } return nil @@ -163,11 +163,11 @@ func getSupportedTxTypes() []string { // validateConnectionParams compares the given the controller and host connection IDs to those set in the provided ICS27 Metadata func validateConnectionParams(metadata Metadata, controllerConnectionID, hostConnectionID string) error { if metadata.ControllerConnectionId != controllerConnectionID { - return sdkerrors.Wrapf(connectiontypes.ErrInvalidConnection, "expected %s, got %s", controllerConnectionID, metadata.ControllerConnectionId) + return errorsmod.Wrapf(connectiontypes.ErrInvalidConnection, "expected %s, got %s", controllerConnectionID, metadata.ControllerConnectionId) } if metadata.HostConnectionId != hostConnectionID { - return sdkerrors.Wrapf(connectiontypes.ErrInvalidConnection, "expected %s, got %s", hostConnectionID, metadata.HostConnectionId) + return errorsmod.Wrapf(connectiontypes.ErrInvalidConnection, "expected %s, got %s", hostConnectionID, metadata.HostConnectionId) } return nil diff --git a/modules/apps/27-interchain-accounts/types/metadata.pb.go b/modules/apps/27-interchain-accounts/types/metadata.pb.go index f1924453d83..38294e2732f 100644 --- a/modules/apps/27-interchain-accounts/types/metadata.pb.go +++ b/modules/apps/27-interchain-accounts/types/metadata.pb.go @@ -5,8 +5,8 @@ package types import ( fmt "fmt" - _ "github.com/gogo/protobuf/gogoproto" - proto "github.com/gogo/protobuf/proto" + _ "github.com/cosmos/gogoproto/gogoproto" + proto "github.com/cosmos/gogoproto/proto" io "io" math "math" math_bits "math/bits" @@ -128,26 +128,26 @@ var fileDescriptor_c29c32e397d1f21e = []byte{ // 349 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x91, 0xcf, 0x4a, 0xeb, 0x40, 0x18, 0xc5, 0x9b, 0xde, 0x6b, 0x5b, 0x67, 0x25, 0x83, 0xe8, 0x58, 0x30, 0x95, 0xb8, 0xd0, 0x4d, - 0x33, 0x54, 0x45, 0xc1, 0x65, 0xc5, 0x85, 0x88, 0x9b, 0xe2, 0x4a, 0x90, 0x30, 0x99, 0x19, 0xd2, - 0x81, 0x64, 0xbe, 0x90, 0x99, 0x86, 0xf6, 0x2d, 0x7c, 0x03, 0x5f, 0xc7, 0x65, 0x97, 0xae, 0x8a, - 0xb4, 0x6f, 0xd0, 0x27, 0x90, 0xa4, 0xb5, 0xf5, 0xef, 0x2e, 0x27, 0xe7, 0x9c, 0xdf, 0xc7, 0x70, - 0xd0, 0xb9, 0x0a, 0x39, 0x65, 0x69, 0x1a, 0x2b, 0xce, 0xac, 0x02, 0x6d, 0xa8, 0xd2, 0x56, 0x66, - 0xbc, 0xcf, 0x94, 0x0e, 0x18, 0xe7, 0x30, 0xd0, 0xd6, 0xd0, 0xbc, 0x43, 0x13, 0x69, 0x99, 0x60, - 0x96, 0xf9, 0x69, 0x06, 0x16, 0xf0, 0x91, 0x0a, 0xb9, 0xff, 0xb9, 0xe7, 0xff, 0xd2, 0xf3, 0xf3, - 0x4e, 0x73, 0x3b, 0x82, 0x08, 0xca, 0x0e, 0x2d, 0xbe, 0x16, 0x75, 0xef, 0xb9, 0x8a, 0x1a, 0x77, - 0x4b, 0x22, 0x26, 0xa8, 0x9e, 0xcb, 0xcc, 0x28, 0xd0, 0xc4, 0x39, 0x70, 0x8e, 0x37, 0x7b, 0x1f, - 0x12, 0x3f, 0x22, 0xc2, 0x41, 0xdb, 0x0c, 0xe2, 0x58, 0x66, 0x01, 0x07, 0xad, 0x25, 0x2f, 0xae, - 0x05, 0x4a, 0x90, 0x6a, 0x11, 0xed, 0x1e, 0xce, 0x27, 0xad, 0xd6, 0x88, 0x25, 0xf1, 0xa5, 0xf7, - 0x57, 0xd2, 0xeb, 0xed, 0xac, 0xad, 0xab, 0x95, 0x73, 0x23, 0xf0, 0x2d, 0xc2, 0x7d, 0x30, 0xf6, - 0x1b, 0xf8, 0x5f, 0x09, 0xde, 0x9f, 0x4f, 0x5a, 0x7b, 0x0b, 0xf0, 0xcf, 0x8c, 0xd7, 0xdb, 0x2a, - 0x7e, 0x7e, 0x81, 0x11, 0x54, 0x67, 0x42, 0x64, 0xd2, 0x18, 0xf2, 0x7f, 0xf1, 0x8a, 0xa5, 0xc4, - 0x4d, 0xd4, 0x90, 0x9a, 0x83, 0x50, 0x3a, 0x22, 0x1b, 0xa5, 0xb5, 0xd2, 0x78, 0x17, 0xd5, 0xed, - 0x30, 0xb0, 0xa3, 0x54, 0x92, 0x5a, 0x69, 0xd5, 0xec, 0xf0, 0x7e, 0x94, 0xca, 0x6e, 0xf0, 0x32, - 0x75, 0x9d, 0xf1, 0xd4, 0x75, 0xde, 0xa6, 0xae, 0xf3, 0x34, 0x73, 0x2b, 0xe3, 0x99, 0x5b, 0x79, - 0x9d, 0xb9, 0x95, 0x87, 0xeb, 0x48, 0xd9, 0xfe, 0x20, 0xf4, 0x39, 0x24, 0x94, 0x83, 0x49, 0xc0, - 0x50, 0x15, 0xf2, 0x76, 0x04, 0x34, 0x3f, 0xa3, 0x09, 0x88, 0x41, 0x2c, 0x4d, 0x31, 0xa9, 0xa1, - 0x27, 0x17, 0xed, 0xf5, 0x2a, 0xed, 0xd5, 0x9a, 0xc5, 0x35, 0x13, 0xd6, 0xca, 0x25, 0x4e, 0xdf, - 0x03, 0x00, 0x00, 0xff, 0xff, 0x54, 0xa4, 0x03, 0x92, 0x02, 0x02, 0x00, 0x00, + 0x33, 0x54, 0xc1, 0x82, 0xcb, 0x8a, 0x0b, 0x11, 0x37, 0xc5, 0x95, 0x20, 0x61, 0x32, 0x33, 0xa4, + 0x03, 0xc9, 0x7c, 0x21, 0x33, 0x0d, 0xed, 0x5b, 0xf8, 0x06, 0xbe, 0x8e, 0xcb, 0x2e, 0x5d, 0x15, + 0x69, 0xdf, 0xa0, 0x4f, 0x20, 0x49, 0x6b, 0xeb, 0xdf, 0x5d, 0x4e, 0xce, 0x39, 0xbf, 0x8f, 0xe1, + 0xa0, 0x0b, 0x15, 0x72, 0xca, 0xd2, 0x34, 0x56, 0x9c, 0x59, 0x05, 0xda, 0x50, 0xa5, 0xad, 0xcc, + 0xf8, 0x80, 0x29, 0x1d, 0x30, 0xce, 0x61, 0xa8, 0xad, 0xa1, 0x79, 0x87, 0x26, 0xd2, 0x32, 0xc1, + 0x2c, 0xf3, 0xd3, 0x0c, 0x2c, 0xe0, 0x13, 0x15, 0x72, 0xff, 0x73, 0xcf, 0xff, 0xa5, 0xe7, 0xe7, + 0x9d, 0xe6, 0x6e, 0x04, 0x11, 0x94, 0x1d, 0x5a, 0x7c, 0x2d, 0xeb, 0xde, 0x73, 0x15, 0x35, 0xee, + 0x56, 0x44, 0x4c, 0x50, 0x3d, 0x97, 0x99, 0x51, 0xa0, 0x89, 0x73, 0xe4, 0x9c, 0x6e, 0xf7, 0x3f, + 0x24, 0x7e, 0x44, 0x84, 0x83, 0xb6, 0x19, 0xc4, 0xb1, 0xcc, 0x02, 0x0e, 0x5a, 0x4b, 0x5e, 0x5c, + 0x0b, 0x94, 0x20, 0xd5, 0x22, 0xda, 0x3b, 0x5e, 0x4c, 0x5b, 0xad, 0x31, 0x4b, 0xe2, 0x4b, 0xef, + 0xaf, 0xa4, 0xd7, 0xdf, 0xdb, 0x58, 0x57, 0x6b, 0xe7, 0x46, 0xe0, 0x5b, 0x84, 0x07, 0x60, 0xec, + 0x37, 0xf0, 0xbf, 0x12, 0x7c, 0xb8, 0x98, 0xb6, 0x0e, 0x96, 0xe0, 0x9f, 0x19, 0xaf, 0xbf, 0x53, + 0xfc, 0xfc, 0x02, 0x23, 0xa8, 0xce, 0x84, 0xc8, 0xa4, 0x31, 0xe4, 0xff, 0xf2, 0x15, 0x2b, 0x89, + 0x9b, 0xa8, 0x21, 0x35, 0x07, 0xa1, 0x74, 0x44, 0xb6, 0x4a, 0x6b, 0xad, 0xf1, 0x3e, 0xaa, 0xdb, + 0x51, 0x60, 0xc7, 0xa9, 0x24, 0xb5, 0xd2, 0xaa, 0xd9, 0xd1, 0xfd, 0x38, 0x95, 0xbd, 0xe0, 0x65, + 0xe6, 0x3a, 0x93, 0x99, 0xeb, 0xbc, 0xcd, 0x5c, 0xe7, 0x69, 0xee, 0x56, 0x26, 0x73, 0xb7, 0xf2, + 0x3a, 0x77, 0x2b, 0x0f, 0xd7, 0x91, 0xb2, 0x83, 0x61, 0xe8, 0x73, 0x48, 0x28, 0x07, 0x93, 0x80, + 0xa1, 0x2a, 0xe4, 0xed, 0x08, 0x68, 0xde, 0xa5, 0x09, 0x88, 0x61, 0x2c, 0x4d, 0x31, 0xa9, 0xa1, + 0x67, 0xdd, 0xf6, 0x66, 0x95, 0xf6, 0x7a, 0xcd, 0xe2, 0x9a, 0x09, 0x6b, 0xe5, 0x12, 0xe7, 0xef, + 0x01, 0x00, 0x00, 0xff, 0xff, 0xa0, 0xb6, 0x31, 0x17, 0x02, 0x02, 0x00, 0x00, } func (m *Metadata) Marshal() (dAtA []byte, err error) { diff --git a/modules/apps/27-interchain-accounts/types/metadata_test.go b/modules/apps/27-interchain-accounts/types/metadata_test.go index 30b1e98f66a..c0274368e07 100644 --- a/modules/apps/27-interchain-accounts/types/metadata_test.go +++ b/modules/apps/27-interchain-accounts/types/metadata_test.go @@ -1,8 +1,8 @@ package types_test import ( - "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/types" - ibctesting "github.com/cosmos/ibc-go/v4/testing" + "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types" + ibctesting "github.com/cosmos/ibc-go/v7/testing" ) // use TestVersion as metadata being compared against diff --git a/modules/apps/27-interchain-accounts/types/packet.go b/modules/apps/27-interchain-accounts/types/packet.go index e7669a77fc9..f7d1c6648be 100644 --- a/modules/apps/27-interchain-accounts/types/packet.go +++ b/modules/apps/27-interchain-accounts/types/packet.go @@ -1,27 +1,42 @@ package types import ( + "time" + + errorsmod "cosmossdk.io/errors" codectypes "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ) // MaxMemoCharLength defines the maximum length for the InterchainAccountPacketData memo field const MaxMemoCharLength = 256 +var ( + // DefaultRelativePacketTimeoutHeight is the default packet timeout height (in blocks) relative + // to the current block height of the counterparty chain provided by the client state. The + // timeout is disabled when set to 0. + DefaultRelativePacketTimeoutHeight = "0-1000" + + // DefaultRelativePacketTimeoutTimestamp is the default packet timeout timestamp (in nanoseconds) + // relative to the current block timestamp of the counterparty chain provided by the client + // state. The timeout is disabled when set to 0. The default is currently set to a 10 minute + // timeout. + DefaultRelativePacketTimeoutTimestamp = uint64((time.Duration(10) * time.Minute).Nanoseconds()) +) + // ValidateBasic performs basic validation of the interchain account packet data. // The memo may be empty. func (iapd InterchainAccountPacketData) ValidateBasic() error { if iapd.Type == UNSPECIFIED { - return sdkerrors.Wrap(ErrInvalidOutgoingData, "packet data type cannot be unspecified") + return errorsmod.Wrap(ErrInvalidOutgoingData, "packet data type cannot be unspecified") } - if iapd.Data == nil { - return sdkerrors.Wrap(ErrInvalidOutgoingData, "packet data cannot be empty") + if len(iapd.Data) == 0 { + return errorsmod.Wrap(ErrInvalidOutgoingData, "packet data cannot be empty") } if len(iapd.Memo) > MaxMemoCharLength { - return sdkerrors.Wrapf(ErrInvalidOutgoingData, "packet data memo cannot be greater than %d characters", MaxMemoCharLength) + return errorsmod.Wrapf(ErrInvalidOutgoingData, "packet data memo cannot be greater than %d characters", MaxMemoCharLength) } return nil @@ -39,8 +54,8 @@ func (ct CosmosTx) GetBytes() []byte { // UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces func (ct CosmosTx) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { - for _, any := range ct.Messages { - err := unpacker.UnpackAny(any, new(sdk.Msg)) + for _, protoAny := range ct.Messages { + err := unpacker.UnpackAny(protoAny, new(sdk.Msg)) if err != nil { return err } diff --git a/modules/apps/27-interchain-accounts/types/packet.pb.go b/modules/apps/27-interchain-accounts/types/packet.pb.go index faf353695d5..5a63e07f1eb 100644 --- a/modules/apps/27-interchain-accounts/types/packet.pb.go +++ b/modules/apps/27-interchain-accounts/types/packet.pb.go @@ -6,8 +6,8 @@ package types import ( fmt "fmt" types "github.com/cosmos/cosmos-sdk/codec/types" - _ "github.com/gogo/protobuf/gogoproto" - proto "github.com/gogo/protobuf/proto" + _ "github.com/cosmos/gogoproto/gogoproto" + proto "github.com/cosmos/gogoproto/proto" io "io" math "math" math_bits "math/bits" @@ -173,29 +173,29 @@ var fileDescriptor_89a080d7401cd393 = []byte{ // 393 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x51, 0x41, 0x8b, 0xd3, 0x40, 0x18, 0xcd, 0xb8, 0x41, 0xd6, 0x59, 0xd9, 0x2d, 0x61, 0x0f, 0x31, 0x42, 0x08, 0x2b, 0x62, 0x10, - 0x32, 0xe3, 0xd6, 0x82, 0x17, 0x2f, 0xb5, 0x8d, 0xd0, 0x8b, 0x94, 0x98, 0x42, 0xf5, 0x12, 0x26, + 0x32, 0xe3, 0x56, 0xa1, 0x17, 0x2f, 0xb5, 0x8d, 0xd0, 0x8b, 0x94, 0x98, 0x42, 0xf5, 0x12, 0x26, 0xd3, 0x31, 0x1d, 0x6c, 0x32, 0xa1, 0x33, 0x29, 0xe6, 0x1f, 0x94, 0x9e, 0xfc, 0x03, 0x3d, 0xf9, 0x67, 0x3c, 0xf6, 0xe8, 0x51, 0xda, 0x3f, 0x22, 0x99, 0x60, 0xdb, 0x83, 0x07, 0x6f, 0x8f, 0xc7, - 0xf7, 0xde, 0xf7, 0xbd, 0xef, 0xc1, 0x1e, 0x4f, 0x29, 0x26, 0x65, 0xb9, 0xe0, 0x94, 0x28, 0x2e, - 0x0a, 0x89, 0x79, 0xa1, 0xd8, 0x92, 0xce, 0x09, 0x2f, 0x12, 0x42, 0xa9, 0xa8, 0x0a, 0x25, 0xf1, - 0xea, 0x1e, 0x97, 0x84, 0x7e, 0x65, 0x0a, 0x95, 0x4b, 0xa1, 0x84, 0xf5, 0x82, 0xa7, 0x14, 0x9d, - 0xab, 0xd0, 0x3f, 0x54, 0x68, 0x75, 0xef, 0x3c, 0xc9, 0x84, 0xc8, 0x16, 0x0c, 0x6b, 0x59, 0x5a, - 0x7d, 0xc1, 0xa4, 0xa8, 0x5b, 0x0f, 0xe7, 0x36, 0x13, 0x99, 0xd0, 0x10, 0x37, 0xa8, 0x65, 0xef, - 0xd6, 0x00, 0x3e, 0x1d, 0x1d, 0xbd, 0xfa, 0xad, 0xd5, 0x58, 0xef, 0x1e, 0x12, 0x45, 0xac, 0x3e, - 0x34, 0x55, 0x5d, 0x32, 0x1b, 0x78, 0xc0, 0xbf, 0xee, 0x06, 0xe8, 0x3f, 0x0f, 0x41, 0x71, 0x5d, - 0xb2, 0x48, 0x4b, 0x2d, 0x0b, 0x9a, 0x33, 0xa2, 0x88, 0xfd, 0xc0, 0x03, 0xfe, 0xe3, 0x48, 0xe3, - 0x86, 0xcb, 0x59, 0x2e, 0xec, 0x0b, 0x0f, 0xf8, 0x8f, 0x22, 0x8d, 0xef, 0xde, 0xc2, 0xcb, 0x81, - 0x90, 0xb9, 0x90, 0xf1, 0x37, 0xeb, 0x15, 0xbc, 0xcc, 0x99, 0x94, 0x24, 0x63, 0xd2, 0x06, 0xde, - 0x85, 0x7f, 0xd5, 0xbd, 0x45, 0x6d, 0x34, 0xf4, 0x37, 0x1a, 0xea, 0x17, 0x75, 0x74, 0x9c, 0x7a, - 0x39, 0x85, 0x66, 0xb3, 0xd3, 0x7a, 0x0e, 0x3b, 0xf1, 0xa7, 0x71, 0x98, 0x4c, 0x3e, 0x7c, 0x1c, - 0x87, 0x83, 0xd1, 0xfb, 0x51, 0x38, 0xec, 0x18, 0xce, 0xcd, 0x66, 0xeb, 0x5d, 0x9d, 0x51, 0xd6, - 0x33, 0x78, 0xa3, 0xc7, 0xc2, 0x69, 0x38, 0x98, 0xc4, 0x61, 0x12, 0x4f, 0x3b, 0xc0, 0xb9, 0xde, - 0x6c, 0x3d, 0x78, 0x62, 0x1c, 0x73, 0xfd, 0xc3, 0x35, 0xde, 0x25, 0x3f, 0xf7, 0x2e, 0xd8, 0xed, - 0x5d, 0xf0, 0x7b, 0xef, 0x82, 0xef, 0x07, 0xd7, 0xd8, 0x1d, 0x5c, 0xe3, 0xd7, 0xc1, 0x35, 0x3e, - 0x87, 0x19, 0x57, 0xf3, 0x2a, 0x45, 0x54, 0xe4, 0x98, 0xea, 0xd3, 0x31, 0x4f, 0x69, 0x90, 0x09, - 0xbc, 0xea, 0xe1, 0x5c, 0xcc, 0xaa, 0x05, 0x93, 0x4d, 0xd9, 0x12, 0x77, 0xdf, 0x04, 0xa7, 0x47, + 0xf7, 0xde, 0xf7, 0xbd, 0xef, 0xc1, 0x37, 0x3c, 0xa5, 0x98, 0x94, 0xe5, 0x82, 0x53, 0xa2, 0xb8, + 0x28, 0x24, 0xe6, 0x85, 0x62, 0x4b, 0x3a, 0x27, 0xbc, 0x48, 0x08, 0xa5, 0xa2, 0x2a, 0x94, 0xc4, + 0xab, 0x7b, 0x5c, 0x12, 0xfa, 0x95, 0x29, 0x54, 0x2e, 0x85, 0x12, 0xd6, 0x0b, 0x9e, 0x52, 0x74, + 0xae, 0x42, 0xff, 0x50, 0xa1, 0xd5, 0xbd, 0xf3, 0x24, 0x13, 0x22, 0x5b, 0x30, 0xac, 0x65, 0x69, + 0xf5, 0x05, 0x93, 0xa2, 0x6e, 0x3d, 0x9c, 0xdb, 0x4c, 0x64, 0x42, 0x43, 0xdc, 0xa0, 0x96, 0xbd, + 0x5b, 0x03, 0xf8, 0x74, 0x74, 0xf4, 0xea, 0xb7, 0x56, 0x63, 0xbd, 0x7b, 0x48, 0x14, 0xb1, 0xfa, + 0xd0, 0x54, 0x75, 0xc9, 0x6c, 0xe0, 0x01, 0xff, 0xba, 0x1b, 0xa0, 0xff, 0x3c, 0x04, 0xc5, 0x75, + 0xc9, 0x22, 0x2d, 0xb5, 0x2c, 0x68, 0xce, 0x88, 0x22, 0xf6, 0x03, 0x0f, 0xf8, 0x8f, 0x23, 0x8d, + 0x1b, 0x2e, 0x67, 0xb9, 0xb0, 0x2f, 0x3c, 0xe0, 0x3f, 0x8a, 0x34, 0xbe, 0x7b, 0x0b, 0x2f, 0x07, + 0x42, 0xe6, 0x42, 0xc6, 0xdf, 0xac, 0x57, 0xf0, 0x32, 0x67, 0x52, 0x92, 0x8c, 0x49, 0x1b, 0x78, + 0x17, 0xfe, 0x55, 0xf7, 0x16, 0xb5, 0xd1, 0xd0, 0xdf, 0x68, 0xa8, 0x5f, 0xd4, 0xd1, 0x71, 0xea, + 0xe5, 0x14, 0x9a, 0xcd, 0x4e, 0xeb, 0x39, 0xec, 0xc4, 0x9f, 0xc6, 0x61, 0x32, 0xf9, 0xf0, 0x71, + 0x1c, 0x0e, 0x46, 0xef, 0x47, 0xe1, 0xb0, 0x63, 0x38, 0x37, 0x9b, 0xad, 0x77, 0x75, 0x46, 0x59, + 0xcf, 0xe0, 0x8d, 0x1e, 0x0b, 0xa7, 0xe1, 0x60, 0x12, 0x87, 0x49, 0x3c, 0xed, 0x00, 0xe7, 0x7a, + 0xb3, 0xf5, 0xe0, 0x89, 0x71, 0xcc, 0xf5, 0x0f, 0xd7, 0x78, 0x97, 0xfc, 0xdc, 0xbb, 0x60, 0xb7, + 0x77, 0xc1, 0xef, 0xbd, 0x0b, 0xbe, 0x1f, 0x5c, 0x63, 0x77, 0x70, 0x8d, 0x5f, 0x07, 0xd7, 0xf8, + 0x1c, 0x66, 0x5c, 0xcd, 0xab, 0x14, 0x51, 0x91, 0x63, 0xaa, 0x4f, 0xc7, 0x3c, 0xa5, 0x41, 0x26, + 0xf0, 0xaa, 0x87, 0x73, 0x31, 0xab, 0x16, 0x4c, 0x36, 0x65, 0x4b, 0xdc, 0xed, 0x05, 0xa7, 0x47, 0x05, 0xc7, 0x9e, 0x9b, 0xff, 0xc8, 0xf4, 0xa1, 0x8e, 0xf4, 0xfa, 0x4f, 0x00, 0x00, 0x00, 0xff, - 0xff, 0xe7, 0xf5, 0x15, 0xdd, 0x1c, 0x02, 0x00, 0x00, + 0xff, 0x13, 0xe7, 0x27, 0x58, 0x1c, 0x02, 0x00, 0x00, } func (m *InterchainAccountPacketData) Marshal() (dAtA []byte, err error) { diff --git a/modules/apps/27-interchain-accounts/types/packet_test.go b/modules/apps/27-interchain-accounts/types/packet_test.go index bfbfa9d3802..329e5a837f4 100644 --- a/modules/apps/27-interchain-accounts/types/packet_test.go +++ b/modules/apps/27-interchain-accounts/types/packet_test.go @@ -1,7 +1,7 @@ package types_test import ( - "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/types" + "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types" ) var largeMemo = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum" @@ -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/27-interchain-accounts/types/port.go b/modules/apps/27-interchain-accounts/types/port.go index 54949b8a1c4..ee814279cf8 100644 --- a/modules/apps/27-interchain-accounts/types/port.go +++ b/modules/apps/27-interchain-accounts/types/port.go @@ -4,14 +4,14 @@ import ( "fmt" "strings" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + errorsmod "cosmossdk.io/errors" ) // NewControllerPortID creates and returns a new prefixed controller port identifier using the provided owner string func NewControllerPortID(owner string) (string, error) { if strings.TrimSpace(owner) == "" { - return "", sdkerrors.Wrap(ErrInvalidAccountAddress, "owner address cannot be empty") + return "", errorsmod.Wrap(ErrInvalidAccountAddress, "owner address cannot be empty") } - return fmt.Sprint(PortPrefix, owner), nil + return fmt.Sprint(ControllerPortPrefix, owner), nil } diff --git a/modules/apps/27-interchain-accounts/types/port_test.go b/modules/apps/27-interchain-accounts/types/port_test.go index a5dc2532b60..0a64d517db9 100644 --- a/modules/apps/27-interchain-accounts/types/port_test.go +++ b/modules/apps/27-interchain-accounts/types/port_test.go @@ -3,8 +3,8 @@ package types_test import ( "fmt" - "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/types" - ibctesting "github.com/cosmos/ibc-go/v4/testing" + "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types" + ibctesting "github.com/cosmos/ibc-go/v7/testing" ) func (suite *TypesTestSuite) TestNewControllerPortID() { @@ -22,7 +22,7 @@ func (suite *TypesTestSuite) TestNewControllerPortID() { { "success", func() {}, - fmt.Sprint(types.PortPrefix, TestOwnerAddress), + fmt.Sprint(types.ControllerPortPrefix, TestOwnerAddress), true, }, { diff --git a/modules/apps/27-interchain-accounts/types/router.go b/modules/apps/27-interchain-accounts/types/router.go new file mode 100644 index 00000000000..007091b9d0d --- /dev/null +++ b/modules/apps/27-interchain-accounts/types/router.go @@ -0,0 +1,12 @@ +package types + +import ( + baseapp "github.com/cosmos/cosmos-sdk/baseapp" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// MessageRouter ADR 031 request type routing +// https://github.com/cosmos/cosmos-sdk/blob/main/docs/architecture/adr-031-msg-service.md +type MessageRouter interface { + Handler(msg sdk.Msg) baseapp.MsgServiceHandler +} diff --git a/modules/apps/29-fee/client/cli/cli.go b/modules/apps/29-fee/client/cli/cli.go index 332493deec8..0fdeb0f398e 100644 --- a/modules/apps/29-fee/client/cli/cli.go +++ b/modules/apps/29-fee/client/cli/cli.go @@ -1,8 +1,9 @@ package cli import ( - "github.com/cosmos/cosmos-sdk/client" "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client" ) // GetQueryCmd returns the query commands for 29-fee diff --git a/modules/apps/29-fee/client/cli/query.go b/modules/apps/29-fee/client/cli/query.go index 815eec4d35b..5c57386668d 100644 --- a/modules/apps/29-fee/client/cli/query.go +++ b/modules/apps/29-fee/client/cli/query.go @@ -10,8 +10,8 @@ import ( "github.com/cosmos/cosmos-sdk/version" "github.com/spf13/cobra" - "github.com/cosmos/ibc-go/v4/modules/apps/29-fee/types" - channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" + "github.com/cosmos/ibc-go/v7/modules/apps/29-fee/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" ) // GetCmdIncentivizedPacket returns the unrelayed incentivized packet for a given packetID @@ -34,7 +34,7 @@ func GetCmdIncentivizedPacket() *cobra.Command { return err } - packetID := channeltypes.NewPacketId(portID, channelID, seq) + packetID := channeltypes.NewPacketID(portID, channelID, seq) if err := packetID.Validate(); err != nil { return err @@ -122,7 +122,7 @@ func GetCmdTotalRecvFees() *cobra.Command { return err } - packetID := channeltypes.NewPacketId(portID, channelID, seq) + packetID := channeltypes.NewPacketID(portID, channelID, seq) if err := packetID.Validate(); err != nil { return err @@ -168,7 +168,7 @@ func GetCmdTotalAckFees() *cobra.Command { return err } - packetID := channeltypes.NewPacketId(portID, channelID, seq) + packetID := channeltypes.NewPacketID(portID, channelID, seq) if err := packetID.Validate(); err != nil { return err @@ -214,7 +214,7 @@ func GetCmdTotalTimeoutFees() *cobra.Command { return err } - packetID := channeltypes.NewPacketId(portID, channelID, seq) + packetID := channeltypes.NewPacketID(portID, channelID, seq) if err := packetID.Validate(); err != nil { return err diff --git a/modules/apps/29-fee/client/cli/tx.go b/modules/apps/29-fee/client/cli/tx.go index c31779c22f6..40d0f5e5c1c 100644 --- a/modules/apps/29-fee/client/cli/tx.go +++ b/modules/apps/29-fee/client/cli/tx.go @@ -12,8 +12,8 @@ import ( "github.com/cosmos/cosmos-sdk/version" "github.com/spf13/cobra" - "github.com/cosmos/ibc-go/v4/modules/apps/29-fee/types" - channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" + "github.com/cosmos/ibc-go/v7/modules/apps/29-fee/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" ) const ( @@ -95,7 +95,7 @@ func NewPayPacketFeeAsyncTxCmd() *cobra.Command { return err } - packetID := channeltypes.NewPacketId(args[0], args[1], seq) + packetID := channeltypes.NewPacketID(args[0], args[1], seq) recvFeeStr, err := cmd.Flags().GetString(flagRecvFee) if err != nil { diff --git a/modules/apps/29-fee/doc.go b/modules/apps/29-fee/doc.go new file mode 100644 index 00000000000..236b88b522c --- /dev/null +++ b/modules/apps/29-fee/doc.go @@ -0,0 +1,9 @@ +/* +Package fee implements the packet data structure, state machine handling logic, +and encoding details for handling fee payments on top of any ICS application protocol. +This implementation is based off the ICS 29 specification +(https://github.com/cosmos/ibc/tree/main/spec/app/ics-029-fee-payment) and follows +the middleware pattern specified in the ICS 30 specification +(https://github.com/cosmos/ibc/tree/main/spec/app/ics-030-middleware). +*/ +package fee diff --git a/modules/apps/29-fee/fee_test.go b/modules/apps/29-fee/fee_test.go index 33ac52a60b0..0505a186132 100644 --- a/modules/apps/29-fee/fee_test.go +++ b/modules/apps/29-fee/fee_test.go @@ -5,11 +5,11 @@ import ( "github.com/stretchr/testify/suite" - "github.com/cosmos/ibc-go/v4/modules/apps/29-fee/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" + "github.com/cosmos/ibc-go/v7/modules/apps/29-fee/types" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + ibctesting "github.com/cosmos/ibc-go/v7/testing" + ibcmock "github.com/cosmos/ibc-go/v7/testing/mock" ) type FeeTestSuite struct { diff --git a/modules/apps/29-fee/ibc_middleware.go b/modules/apps/29-fee/ibc_middleware.go index 2cbce3d2f9d..9ac7eec370c 100644 --- a/modules/apps/29-fee/ibc_middleware.go +++ b/modules/apps/29-fee/ibc_middleware.go @@ -3,15 +3,16 @@ package fee import ( "strings" + errorsmod "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - "github.com/cosmos/ibc-go/v4/modules/apps/29-fee/keeper" - "github.com/cosmos/ibc-go/v4/modules/apps/29-fee/types" - channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" - porttypes "github.com/cosmos/ibc-go/v4/modules/core/05-port/types" - "github.com/cosmos/ibc-go/v4/modules/core/exported" + "github.com/cosmos/ibc-go/v7/modules/apps/29-fee/keeper" + "github.com/cosmos/ibc-go/v7/modules/apps/29-fee/types" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + porttypes "github.com/cosmos/ibc-go/v7/modules/core/05-port/types" + "github.com/cosmos/ibc-go/v7/modules/core/exported" ) var _ porttypes.Middleware = &IBCMiddleware{} @@ -61,7 +62,7 @@ func (im IBCMiddleware) OnChanOpenInit( } if versionMetadata.FeeVersion != types.Version { - return "", sdkerrors.Wrapf(types.ErrInvalidVersion, "expected %s, got %s", types.Version, versionMetadata.FeeVersion) + return "", errorsmod.Wrapf(types.ErrInvalidVersion, "expected %s, got %s", types.Version, versionMetadata.FeeVersion) } appVersion, err := im.app.OnChanOpenInit(ctx, order, connectionHops, portID, channelID, chanCap, counterparty, versionMetadata.AppVersion) @@ -103,7 +104,7 @@ func (im IBCMiddleware) OnChanOpenTry( } if versionMetadata.FeeVersion != types.Version { - return "", sdkerrors.Wrapf(types.ErrInvalidVersion, "expected %s, got %s", types.Version, versionMetadata.FeeVersion) + return "", errorsmod.Wrapf(types.ErrInvalidVersion, "expected %s, got %s", types.Version, versionMetadata.FeeVersion) } im.keeper.SetFeeEnabled(ctx, portID, channelID) @@ -136,11 +137,11 @@ func (im IBCMiddleware) OnChanOpenAck( if im.keeper.IsFeeEnabled(ctx, portID, channelID) { var versionMetadata types.Metadata if err := types.ModuleCdc.UnmarshalJSON([]byte(counterpartyVersion), &versionMetadata); err != nil { - return sdkerrors.Wrapf(err, "failed to unmarshal ICS29 counterparty version metadata: %s", counterpartyVersion) + return errorsmod.Wrapf(err, "failed to unmarshal ICS29 counterparty version metadata: %s", counterpartyVersion) } if versionMetadata.FeeVersion != types.Version { - return sdkerrors.Wrapf(types.ErrInvalidVersion, "expected counterparty fee version: %s, got: %s", types.Version, versionMetadata.FeeVersion) + return errorsmod.Wrapf(types.ErrInvalidVersion, "expected counterparty fee version: %s, got: %s", types.Version, versionMetadata.FeeVersion) } // call underlying app's OnChanOpenAck callback with the counterparty app version. @@ -226,7 +227,7 @@ func (im IBCMiddleware) OnRecvPacket( // 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()) + im.keeper.SetRelayerAddressForAsyncAck(ctx, channeltypes.NewPacketID(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()), relayer.String()) return nil } @@ -248,9 +249,9 @@ func (im IBCMiddleware) OnAcknowledgementPacket( return im.app.OnAcknowledgementPacket(ctx, packet, acknowledgement, relayer) } - var ack = &types.IncentivizedAcknowledgement{} - if err := types.ModuleCdc.UnmarshalJSON(acknowledgement, ack); err != nil { - return sdkerrors.Wrapf(err, "cannot unmarshal ICS-29 incentivized packet acknowledgement: %v", ack) + var ack types.IncentivizedAcknowledgement + if err := types.ModuleCdc.UnmarshalJSON(acknowledgement, &ack); err != nil { + return errorsmod.Wrapf(err, "cannot unmarshal ICS-29 incentivized packet acknowledgement: %v", ack) } if im.keeper.IsLocked(ctx) { @@ -265,7 +266,7 @@ func (im IBCMiddleware) OnAcknowledgementPacket( return im.app.OnAcknowledgementPacket(ctx, packet, ack.AppAcknowledgement, relayer) } - packetID := channeltypes.NewPacketId(packet.SourcePort, packet.SourceChannel, packet.Sequence) + packetID := channeltypes.NewPacketID(packet.SourcePort, packet.SourceChannel, packet.Sequence) feesInEscrow, found := im.keeper.GetFeesInEscrow(ctx, packetID) if !found { // call underlying callback @@ -282,7 +283,7 @@ func (im IBCMiddleware) OnAcknowledgementPacket( payeeAddr, err := sdk.AccAddressFromBech32(payee) if err != nil { - return sdkerrors.Wrapf(err, "failed to create sdk.Address from payee: %s", payee) + return errorsmod.Wrapf(err, "failed to create sdk.Address from payee: %s", payee) } im.keeper.DistributePacketFeesOnAcknowledgement(ctx, ack.ForwardRelayerAddress, payeeAddr, feesInEscrow.PacketFees, packetID) @@ -307,7 +308,7 @@ func (im IBCMiddleware) OnTimeoutPacket( return im.app.OnTimeoutPacket(ctx, packet, relayer) } - packetID := channeltypes.NewPacketId(packet.SourcePort, packet.SourceChannel, packet.Sequence) + packetID := channeltypes.NewPacketID(packet.SourcePort, packet.SourceChannel, packet.Sequence) feesInEscrow, found := im.keeper.GetFeesInEscrow(ctx, packetID) if !found { // call underlying callback @@ -324,7 +325,7 @@ func (im IBCMiddleware) OnTimeoutPacket( payeeAddr, err := sdk.AccAddressFromBech32(payee) if err != nil { - return sdkerrors.Wrapf(err, "failed to create sdk.Address from payee: %s", payee) + return errorsmod.Wrapf(err, "failed to create sdk.Address from payee: %s", payee) } im.keeper.DistributePacketFeesOnTimeout(ctx, payeeAddr, feesInEscrow.PacketFees, packetID) @@ -337,9 +338,13 @@ func (im IBCMiddleware) OnTimeoutPacket( func (im IBCMiddleware) SendPacket( ctx sdk.Context, chanCap *capabilitytypes.Capability, - packet exported.PacketI, -) error { - return im.keeper.SendPacket(ctx, chanCap, packet) + sourcePort string, + sourceChannel string, + timeoutHeight clienttypes.Height, + timeoutTimestamp uint64, + data []byte, +) (uint64, error) { + return im.keeper.SendPacket(ctx, chanCap, sourcePort, sourceChannel, timeoutHeight, timeoutTimestamp, data) } // WriteAcknowledgement implements the ICS4 Wrapper interface diff --git a/modules/apps/29-fee/ibc_middleware_test.go b/modules/apps/29-fee/ibc_middleware_test.go index ea7cdcae8b6..0f1c1176210 100644 --- a/modules/apps/29-fee/ibc_middleware_test.go +++ b/modules/apps/29-fee/ibc_middleware_test.go @@ -6,14 +6,14 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - fee "github.com/cosmos/ibc-go/v4/modules/apps/29-fee" - "github.com/cosmos/ibc-go/v4/modules/apps/29-fee/types" - transfertypes "github.com/cosmos/ibc-go/v4/modules/apps/transfer/types" - channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" - "github.com/cosmos/ibc-go/v4/modules/core/exported" - ibctesting "github.com/cosmos/ibc-go/v4/testing" - ibcmock "github.com/cosmos/ibc-go/v4/testing/mock" + fee "github.com/cosmos/ibc-go/v7/modules/apps/29-fee" + "github.com/cosmos/ibc-go/v7/modules/apps/29-fee/types" + transfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + "github.com/cosmos/ibc-go/v7/modules/core/exported" + ibctesting "github.com/cosmos/ibc-go/v7/testing" + ibcmock "github.com/cosmos/ibc-go/v7/testing/mock" ) var ( @@ -172,7 +172,8 @@ func (suite *FeeTestSuite) TestOnChanOpenTry() { // reset suite suite.SetupTest() suite.coordinator.SetupConnections(suite.path) - suite.path.EndpointB.ChanOpenInit() + err := suite.path.EndpointB.ChanOpenInit() + suite.Require().NoError(err) // setup mock callback suite.chainA.GetSimApp().FeeMockModule.IBCApp.OnChanOpenTry = func(ctx sdk.Context, order channeltypes.Order, connectionHops []string, @@ -188,7 +189,6 @@ func (suite *FeeTestSuite) TestOnChanOpenTry() { var ( chanCap *capabilitytypes.Capability ok bool - err error ) chanCap, err = suite.chainA.App.GetScopedIBCKeeper().NewCapability(suite.chainA.GetContext(), host.ChannelCapabilityPath(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID)) @@ -286,8 +286,10 @@ func (suite *FeeTestSuite) TestOnChanOpenAck() { // malleate test case tc.malleate(suite) - suite.path.EndpointA.ChanOpenInit() - suite.path.EndpointB.ChanOpenTry() + err := suite.path.EndpointA.ChanOpenInit() + suite.Require().NoError(err) + err = suite.path.EndpointB.ChanOpenTry() + suite.Require().NoError(err) module, _, err := suite.chainA.App.GetIBCKeeper().PortKeeper.LookupModuleByPort(suite.chainA.GetContext(), ibctesting.MockFeePort) suite.Require().NoError(err) @@ -331,7 +333,7 @@ func (suite *FeeTestSuite) TestOnChanCloseInit() { { "RefundFeesOnChannelClosure continues - invalid refund address", func() { // store the fee in state & update escrow account balance - packetID := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, uint64(1)) + packetID := channeltypes.NewPacketID(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, uint64(1)) packetFees := types.NewPacketFees([]types.PacketFee{types.NewPacketFee(fee, "invalid refund address", nil)}) suite.chainA.GetSimApp().IBCFeeKeeper.SetFeesInEscrow(suite.chainA.GetContext(), packetID, packetFees) @@ -360,7 +362,7 @@ func (suite *FeeTestSuite) TestOnChanCloseInit() { suite.SetupTest() suite.coordinator.Setup(suite.path) // setup channel - packetID := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, 1) + packetID := channeltypes.NewPacketID(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, 1) fee = types.Fee{ RecvFee: defaultRecvFee, AckFee: defaultAckFee, @@ -420,7 +422,7 @@ func (suite *FeeTestSuite) TestOnChanCloseConfirm() { { "RefundChannelFeesOnClosure continues - refund address is invalid", func() { // store the fee in state & update escrow account balance - packetID := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, uint64(1)) + packetID := channeltypes.NewPacketID(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, uint64(1)) packetFees := types.NewPacketFees([]types.PacketFee{types.NewPacketFee(fee, "invalid refund address", nil)}) suite.chainA.GetSimApp().IBCFeeKeeper.SetFeesInEscrow(suite.chainA.GetContext(), packetID, packetFees) @@ -450,7 +452,7 @@ func (suite *FeeTestSuite) TestOnChanCloseConfirm() { suite.SetupTest() suite.coordinator.Setup(suite.path) // setup channel - packetID := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, 1) + packetID := channeltypes.NewPacketID(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, 1) fee = types.Fee{ RecvFee: defaultRecvFee, AckFee: defaultAckFee, @@ -573,7 +575,7 @@ func (suite *FeeTestSuite) TestOnRecvPacket() { case tc.forwardRelayer && result == nil: suite.Require().Equal(nil, result) - packetID := channeltypes.NewPacketId(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()) + packetID := channeltypes.NewPacketID(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()) // retrieve the forward relayer that was stored in `onRecvPacket` relayer, _ := suite.chainB.GetSimApp().IBCFeeKeeper.GetRelayerAddressForAsyncAck(suite.chainB.GetContext(), packetID) @@ -787,7 +789,7 @@ func (suite *FeeTestSuite) TestOnAcknowledgementPacket() { refundAddr = suite.chainA.SenderAccounts[1].SenderAccount.GetAddress() packet := suite.CreateMockPacket() - packetID = channeltypes.NewPacketId(packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) + packetID = channeltypes.NewPacketID(packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) packetFee = types.NewPacketFee(types.NewFee(defaultRecvFee, defaultAckFee, defaultTimeoutFee), refundAddr.String(), nil) suite.chainA.GetSimApp().IBCFeeKeeper.SetFeesInEscrow(suite.chainA.GetContext(), packetID, types.NewPacketFees([]types.PacketFee{packetFee})) @@ -973,7 +975,7 @@ func (suite *FeeTestSuite) TestOnTimeoutPacket() { refundAddr = suite.chainA.SenderAccounts[1].SenderAccount.GetAddress() packet := suite.CreateMockPacket() - packetID = channeltypes.NewPacketId(packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) + packetID = channeltypes.NewPacketID(packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) packetFee = types.NewPacketFee(types.NewFee(defaultRecvFee, defaultAckFee, defaultTimeoutFee), refundAddr.String(), nil) suite.chainA.GetSimApp().IBCFeeKeeper.SetFeesInEscrow(suite.chainA.GetContext(), packetID, types.NewPacketFees([]types.PacketFee{packetFee})) diff --git a/modules/apps/29-fee/ica_test.go b/modules/apps/29-fee/ica_test.go index bc10f4941dd..b8034b78914 100644 --- a/modules/apps/29-fee/ica_test.go +++ b/modules/apps/29-fee/ica_test.go @@ -4,23 +4,24 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - - icahosttypes "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" - "github.com/cosmos/ibc-go/v4/modules/apps/29-fee/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" + "github.com/cosmos/gogoproto/proto" + + icahosttypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/host/types" + icatypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types" + "github.com/cosmos/ibc-go/v7/modules/apps/29-fee/types" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + ibctesting "github.com/cosmos/ibc-go/v7/testing" ) var ( // defaultOwnerAddress defines a reusable bech32 address for testing purposes defaultOwnerAddress = "cosmos17dtl0mjt3t77kpuhg2edqzjpszulwhgzuj9ljs" - // defaultPortID defines a resuable port identifier for testing purposes + // defaultPortID defines a reusable port identifier for testing purposes defaultPortID, _ = icatypes.NewControllerPortID(defaultOwnerAddress) - // defaultICAVersion defines a resuable interchainaccounts version string for testing purposes + // defaultICAVersion defines a reusable interchainaccounts version string for testing purposes defaultICAVersion = icatypes.NewDefaultMetadataString(ibctesting.FirstConnectionID, ibctesting.FirstConnectionID) ) @@ -39,7 +40,7 @@ func NewIncentivizedICAPath(chainA, chainB *ibctesting.TestChain) *ibctesting.Pa path.EndpointA.ChannelConfig.Version = feeICAVersion path.EndpointB.ChannelConfig.Version = feeICAVersion path.EndpointA.ChannelConfig.PortID = defaultPortID - path.EndpointB.ChannelConfig.PortID = icatypes.PortID + path.EndpointB.ChannelConfig.PortID = icatypes.HostPortID return path } @@ -118,7 +119,7 @@ func (suite *FeeTestSuite) TestFeeInterchainAccounts() { postEscrowBalance := suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), suite.chainA.SenderAccount.GetAddress(), sdk.DefaultBondDenom) suite.Require().Equal(postEscrowBalance.AddAmount(expectedFee.Total().AmountOf(sdk.DefaultBondDenom)), preEscrowBalance) - packetID := channeltypes.NewPacketId(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, 1) + packetID := channeltypes.NewPacketID(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, 1) packetFees, found := suite.chainA.GetSimApp().IBCFeeKeeper.GetFeesInEscrow(suite.chainA.GetContext(), packetID) suite.Require().True(found) suite.Require().Equal(expectedFee, packetFees.PacketFees[0].Fee) @@ -146,7 +147,7 @@ func (suite *FeeTestSuite) TestFeeInterchainAccounts() { Amount: sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(5000)), } - data, err := icatypes.SerializeCosmosTx(suite.chainA.GetSimApp().AppCodec(), []sdk.Msg{msgDelegate}) + data, err := icatypes.SerializeCosmosTx(suite.chainA.GetSimApp().AppCodec(), []proto.Message{msgDelegate}) suite.Require().NoError(err) icaPacketData := icatypes.InterchainAccountPacketData{ @@ -189,7 +190,7 @@ func buildInterchainAccountsPacket(path *ibctesting.Path, data []byte, seq uint6 path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, - clienttypes.NewHeight(0, 100), + clienttypes.NewHeight(1, 100), 0, ) diff --git a/modules/apps/29-fee/keeper/escrow.go b/modules/apps/29-fee/keeper/escrow.go index fed2398f142..8f3f8bd417f 100644 --- a/modules/apps/29-fee/keeper/escrow.go +++ b/modules/apps/29-fee/keeper/escrow.go @@ -4,11 +4,11 @@ import ( "bytes" "fmt" + errorsmod "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/cosmos/ibc-go/v4/modules/apps/29-fee/types" - channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" + "github.com/cosmos/ibc-go/v7/modules/apps/29-fee/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" ) // escrowPacketFee sends the packet fee to the 29-fee module account to hold in escrow @@ -21,7 +21,7 @@ func (k Keeper) escrowPacketFee(ctx sdk.Context, packetID channeltypes.PacketId, refundAcc := k.authKeeper.GetAccount(ctx, refundAddr) if refundAcc == nil { - return sdkerrors.Wrapf(types.ErrRefundAccNotFound, "account with address: %s not found", packetFee.RefundAddress) + return errorsmod.Wrapf(types.ErrRefundAccNotFound, "account with address: %s not found", packetFee.RefundAddress) } coins := packetFee.Fee.Total() @@ -39,7 +39,7 @@ func (k Keeper) escrowPacketFee(ctx sdk.Context, packetID channeltypes.PacketId, packetFees := types.NewPacketFees(fees) k.SetFeesInEscrow(ctx, packetID, packetFees) - EmitIncentivizedPacketEvent(ctx, packetID, packetFees) + emitIncentivizedPacketEvent(ctx, packetID, packetFees) return nil } @@ -74,9 +74,6 @@ func (k Keeper) DistributePacketFeesOnAcknowledgement(ctx sdk.Context, forwardRe k.distributePacketFeeOnAcknowledgement(cacheCtx, refundAddr, forwardAddr, reverseRelayer, packetFee) } - // 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()) - // write the cache writeFn() @@ -130,9 +127,6 @@ func (k Keeper) DistributePacketFeesOnTimeout(ctx sdk.Context, timeoutRelayer sd k.distributePacketFeeOnTimeout(cacheCtx, refundAddr, timeoutRelayer, packetFee) } - // 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()) - // write the cache writeFn() @@ -173,13 +167,14 @@ func (k Keeper) distributeFee(ctx sdk.Context, receiver, refundAccAddress sdk.Ac k.Logger(ctx).Error("error refunding fee to the original sender", "refund address", refundAccAddress, "fee", fee) return // if sending to the refund address fails, no-op } + + emitDistributeFeeEvent(ctx, refundAccAddress.String(), fee) + } else { + emitDistributeFeeEvent(ctx, receiver.String(), fee) } // write the cache 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()) } // RefundFeesOnChannelClosure will refund all fees associated with the given port and channel identifiers. @@ -228,9 +223,6 @@ func (k Keeper) RefundFeesOnChannelClosure(ctx sdk.Context, portID, channelID st } } - // 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()) - // write the cache writeFn() diff --git a/modules/apps/29-fee/keeper/escrow_test.go b/modules/apps/29-fee/keeper/escrow_test.go index b1891e4e4e4..af0ce95902e 100644 --- a/modules/apps/29-fee/keeper/escrow_test.go +++ b/modules/apps/29-fee/keeper/escrow_test.go @@ -4,10 +4,10 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/tendermint/tendermint/crypto/secp256k1" - "github.com/cosmos/ibc-go/v4/modules/apps/29-fee/types" - transfertypes "github.com/cosmos/ibc-go/v4/modules/apps/transfer/types" - channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" - "github.com/cosmos/ibc-go/v4/testing/mock" + "github.com/cosmos/ibc-go/v7/modules/apps/29-fee/types" + transfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + "github.com/cosmos/ibc-go/v7/testing/mock" ) func (suite *KeeperTestSuite) TestDistributeFee() { @@ -36,7 +36,7 @@ func (suite *KeeperTestSuite) TestDistributeFee() { }, func() { // check if fees has been deleted - packetID := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, 1) + packetID := channeltypes.NewPacketID(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, 1) suite.Require().False(suite.chainA.GetSimApp().IBCFeeKeeper.HasFeesInEscrow(suite.chainA.GetContext(), packetID)) // check if the reverse relayer is paid @@ -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) }, @@ -90,7 +90,7 @@ func (suite *KeeperTestSuite) TestDistributeFee() { packetFees = append(packetFees, packetFee) }, func() { - packetID := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, 1) + packetID := channeltypes.NewPacketID(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, 1) suite.Require().True(suite.chainA.GetSimApp().IBCFeeKeeper.IsLocked(suite.chainA.GetContext())) suite.Require().True(suite.chainA.GetSimApp().IBCFeeKeeper.HasFeesInEscrow(suite.chainA.GetContext(), packetID)) @@ -176,7 +176,7 @@ func (suite *KeeperTestSuite) TestDistributeFee() { reverseRelayer = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()) refundAcc = suite.chainA.SenderAccount.GetAddress() - packetID := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, 1) + packetID := channeltypes.NewPacketID(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, 1) fee = types.NewFee(defaultRecvFee, defaultAckFee, defaultTimeoutFee) tc.malleate() @@ -238,7 +238,7 @@ func (suite *KeeperTestSuite) TestDistributePacketFeesOnTimeout() { packetFees = append(packetFees, packetFee) }, func() { - packetID := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, 1) + packetID := channeltypes.NewPacketID(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, 1) suite.Require().True(suite.chainA.GetSimApp().IBCFeeKeeper.IsLocked(suite.chainA.GetContext())) suite.Require().True(suite.chainA.GetSimApp().IBCFeeKeeper.HasFeesInEscrow(suite.chainA.GetContext(), packetID)) @@ -287,7 +287,7 @@ func (suite *KeeperTestSuite) TestDistributePacketFeesOnTimeout() { timeoutRelayer = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()) refundAcc = suite.chainA.SenderAccount.GetAddress() - packetID := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, 1) + packetID := channeltypes.NewPacketID(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, 1) fee := types.NewFee(defaultRecvFee, defaultAckFee, defaultTimeoutFee) // escrow the packet fees & store the fees in state @@ -331,7 +331,7 @@ func (suite *KeeperTestSuite) TestRefundFeesOnChannelClosure() { "success", func() { for i := 1; i < 6; i++ { // store the fee in state & update escrow account balance - packetID := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, uint64(i)) + packetID := channeltypes.NewPacketID(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, uint64(i)) packetFees := types.NewPacketFees([]types.PacketFee{types.NewPacketFee(fee, refundAcc.String(), nil)}) identifiedPacketFees := types.NewIdentifiedPacketFees(packetID, packetFees.PacketFees) @@ -348,7 +348,7 @@ func (suite *KeeperTestSuite) TestRefundFeesOnChannelClosure() { "success with undistributed packet fees on a different channel", func() { for i := 1; i < 6; i++ { // store the fee in state & update escrow account balance - packetID := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, uint64(i)) + packetID := channeltypes.NewPacketID(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, uint64(i)) packetFees := types.NewPacketFees([]types.PacketFee{types.NewPacketFee(fee, refundAcc.String(), nil)}) identifiedPacketFees := types.NewIdentifiedPacketFees(packetID, packetFees.PacketFees) @@ -361,7 +361,7 @@ func (suite *KeeperTestSuite) TestRefundFeesOnChannelClosure() { } // set packet fee for a different channel - packetID := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, "channel-1", uint64(1)) + packetID := channeltypes.NewPacketID(suite.path.EndpointA.ChannelConfig.PortID, "channel-1", uint64(1)) packetFees := types.NewPacketFees([]types.PacketFee{types.NewPacketFee(fee, refundAcc.String(), nil)}) suite.chainA.GetSimApp().IBCFeeKeeper.SetFeeEnabled(suite.chainA.GetContext(), suite.path.EndpointA.ChannelConfig.PortID, "channel-1") @@ -370,7 +370,7 @@ func (suite *KeeperTestSuite) TestRefundFeesOnChannelClosure() { suite.Require().NoError(err) expEscrowBal = fee.Total() - expRefundBal = expRefundBal.Sub(fee.Total()) + expRefundBal = expRefundBal.Sub(fee.Total()...) }, true, }, { @@ -378,7 +378,7 @@ func (suite *KeeperTestSuite) TestRefundFeesOnChannelClosure() { locked = true // store the fee in state without updating escrow account balance - packetID := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, uint64(1)) + packetID := channeltypes.NewPacketID(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, uint64(1)) packetFees := types.NewPacketFees([]types.PacketFee{types.NewPacketFee(fee, refundAcc.String(), nil)}) identifiedPacketFees := types.NewIdentifiedPacketFees(packetID, packetFees.PacketFees) @@ -393,8 +393,8 @@ func (suite *KeeperTestSuite) TestRefundFeesOnChannelClosure() { locked = true // store 2 fees in state - packetID1 := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, uint64(1)) - packetID2 := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, uint64(2)) + packetID1 := channeltypes.NewPacketID(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, uint64(1)) + packetID2 := channeltypes.NewPacketID(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, uint64(2)) packetFees := types.NewPacketFees([]types.PacketFee{types.NewPacketFee(fee, refundAcc.String(), nil)}) identifiedPacketFee1 := types.NewIdentifiedPacketFees(packetID1, packetFees.PacketFees) identifiedPacketFee2 := types.NewIdentifiedPacketFees(packetID2, packetFees.PacketFees) @@ -413,7 +413,7 @@ func (suite *KeeperTestSuite) TestRefundFeesOnChannelClosure() { "invalid refund acc address", func() { // store the fee in state & update escrow account balance expectEscrowFeesToBeDeleted = false - packetID := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, uint64(1)) + packetID := channeltypes.NewPacketID(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, uint64(1)) packetFees := types.NewPacketFees([]types.PacketFee{types.NewPacketFee(fee, "invalid refund address", nil)}) identifiedPacketFees := types.NewIdentifiedPacketFees(packetID, packetFees.PacketFees) @@ -425,7 +425,7 @@ func (suite *KeeperTestSuite) TestRefundFeesOnChannelClosure() { expIdentifiedPacketFees = []types.IdentifiedPacketFees{identifiedPacketFees} expEscrowBal = fee.Total() - expRefundBal = expRefundBal.Sub(fee.Total()) + expRefundBal = expRefundBal.Sub(fee.Total()...) }, true, }, { @@ -434,7 +434,7 @@ func (suite *KeeperTestSuite) TestRefundFeesOnChannelClosure() { blockedAddr := suite.chainA.GetSimApp().AccountKeeper.GetModuleAccount(suite.chainA.GetContext(), transfertypes.ModuleName).GetAddress().String() // store the fee in state & update escrow account balance - packetID := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, uint64(1)) + packetID := channeltypes.NewPacketID(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, uint64(1)) packetFees := types.NewPacketFees([]types.PacketFee{types.NewPacketFee(fee, blockedAddr, nil)}) identifiedPacketFees := types.NewIdentifiedPacketFees(packetID, packetFees.PacketFees) @@ -446,7 +446,7 @@ func (suite *KeeperTestSuite) TestRefundFeesOnChannelClosure() { expIdentifiedPacketFees = []types.IdentifiedPacketFees{identifiedPacketFees} expEscrowBal = fee.Total() - expRefundBal = expRefundBal.Sub(fee.Total()) + expRefundBal = expRefundBal.Sub(fee.Total()...) }, true, }, } diff --git a/modules/apps/29-fee/keeper/events.go b/modules/apps/29-fee/keeper/events.go index 162542f4e4d..e367482649e 100644 --- a/modules/apps/29-fee/keeper/events.go +++ b/modules/apps/29-fee/keeper/events.go @@ -5,13 +5,13 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/ibc-go/v4/modules/apps/29-fee/types" - channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" + "github.com/cosmos/ibc-go/v7/modules/apps/29-fee/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" ) -// EmitIncentivizedPacketEvent emits an event containing information on the total amount of fees incentivizing +// emitIncentivizedPacketEvent emits an event containing information on the total amount of fees incentivizing // a specific packet. It should be emitted on every fee escrowed for the given packetID. -func EmitIncentivizedPacketEvent(ctx sdk.Context, packetID channeltypes.PacketId, packetFees types.PacketFees) { +func emitIncentivizedPacketEvent(ctx sdk.Context, packetID channeltypes.PacketId, packetFees types.PacketFees) { var ( totalRecvFees sdk.Coins totalAckFees sdk.Coins @@ -44,8 +44,8 @@ func EmitIncentivizedPacketEvent(ctx sdk.Context, packetID channeltypes.PacketId }) } -// EmitRegisterPayeeEvent emits an event containing information of a registered payee for a relayer on a particular channel -func EmitRegisterPayeeEvent(ctx sdk.Context, relayer, payee, channelID string) { +// emitRegisterPayeeEvent emits an event containing information of a registered payee for a relayer on a particular channel +func emitRegisterPayeeEvent(ctx sdk.Context, relayer, payee, channelID string) { ctx.EventManager().EmitEvents(sdk.Events{ sdk.NewEvent( types.EventTypeRegisterPayee, @@ -60,8 +60,8 @@ func EmitRegisterPayeeEvent(ctx sdk.Context, relayer, payee, channelID string) { }) } -// EmitRegisterCounterpartyPayeeEvent emits an event containing information of a registered counterparty payee for a relayer on a particular channel -func EmitRegisterCounterpartyPayeeEvent(ctx sdk.Context, relayer, counterpartyPayee, channelID string) { +// emitRegisterCounterpartyPayeeEvent emits an event containing information of a registered counterparty payee for a relayer on a particular channel +func emitRegisterCounterpartyPayeeEvent(ctx sdk.Context, relayer, counterpartyPayee, channelID string) { ctx.EventManager().EmitEvents(sdk.Events{ sdk.NewEvent( types.EventTypeRegisterCounterpartyPayee, @@ -75,3 +75,18 @@ func EmitRegisterCounterpartyPayeeEvent(ctx sdk.Context, relayer, counterpartyPa ), }) } + +// emitDistributeFeeEvent emits an event containing a distribution fee and receiver address +func emitDistributeFeeEvent(ctx sdk.Context, receiver string, fee sdk.Coins) { + ctx.EventManager().EmitEvents(sdk.Events{ + sdk.NewEvent( + types.EventTypeDistributeFee, + sdk.NewAttribute(types.AttributeKeyReceiver, receiver), + sdk.NewAttribute(types.AttributeKeyFee, fee.String()), + ), + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName), + ), + }) +} diff --git a/modules/apps/29-fee/keeper/events_test.go b/modules/apps/29-fee/keeper/events_test.go index 2dc1f8c91c4..4fd2b6c62cb 100644 --- a/modules/apps/29-fee/keeper/events_test.go +++ b/modules/apps/29-fee/keeper/events_test.go @@ -4,7 +4,12 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" abcitypes "github.com/tendermint/tendermint/abci/types" - "github.com/cosmos/ibc-go/v4/modules/apps/29-fee/types" + "github.com/cosmos/ibc-go/v7/modules/apps/29-fee/types" + transfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + ibctesting "github.com/cosmos/ibc-go/v7/testing" ) func (suite *KeeperTestSuite) TestIncentivizePacketEvent() { @@ -40,15 +45,15 @@ func (suite *KeeperTestSuite) TestIncentivizePacketEvent() { } for _, attr := range incentivizedPacketEvent.Attributes { - switch string(attr.Key) { + switch attr.Key { case types.AttributeKeyRecvFee: - suite.Require().Equal(expRecvFees.String(), string(attr.Value)) + suite.Require().Equal(expRecvFees.String(), attr.Value) case types.AttributeKeyAckFee: - suite.Require().Equal(expAckFees.String(), string(attr.Value)) + suite.Require().Equal(expAckFees.String(), attr.Value) case types.AttributeKeyTimeoutFee: - suite.Require().Equal(expTimeoutFees.String(), string(attr.Value)) + suite.Require().Equal(expTimeoutFees.String(), attr.Value) } } @@ -69,15 +74,95 @@ func (suite *KeeperTestSuite) TestIncentivizePacketEvent() { } for _, attr := range incentivizedPacketEvent.Attributes { - switch string(attr.Key) { + switch attr.Key { case types.AttributeKeyRecvFee: - suite.Require().Equal(expRecvFees.String(), string(attr.Value)) + suite.Require().Equal(expRecvFees.String(), attr.Value) case types.AttributeKeyAckFee: - suite.Require().Equal(expAckFees.String(), string(attr.Value)) + suite.Require().Equal(expAckFees.String(), attr.Value) case types.AttributeKeyTimeoutFee: - suite.Require().Equal(expTimeoutFees.String(), string(attr.Value)) + suite.Require().Equal(expTimeoutFees.String(), attr.Value) } } } + +func (suite *KeeperTestSuite) TestDistributeFeeEvent() { + // create an incentivized transfer path + path := ibctesting.NewPath(suite.chainA, suite.chainB) + feeTransferVersion := string(types.ModuleCdc.MustMarshalJSON(&types.Metadata{FeeVersion: types.Version, AppVersion: transfertypes.Version})) + path.EndpointA.ChannelConfig.Version = feeTransferVersion + path.EndpointB.ChannelConfig.Version = feeTransferVersion + path.EndpointA.ChannelConfig.PortID = transfertypes.PortID + path.EndpointB.ChannelConfig.PortID = transfertypes.PortID + + suite.coordinator.Setup(path) + + // send a new MsgPayPacketFee and MsgTransfer to chainA + fee := types.NewFee(defaultRecvFee, defaultAckFee, defaultTimeoutFee) + msgPayPacketFee := types.NewMsgPayPacketFee( + fee, + path.EndpointA.ChannelConfig.PortID, + path.EndpointA.ChannelID, + suite.chainA.SenderAccount.GetAddress().String(), + nil, + ) + + msgTransfer := transfertypes.NewMsgTransfer( + path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, + sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100)), suite.chainA.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(), + clienttypes.NewHeight(1, 100), 0, "", + ) + + res, err := suite.chainA.SendMsgs(msgPayPacketFee, msgTransfer) + suite.Require().NoError(err) + suite.Require().NotNil(res) + + // parse the packet from result events and recv packet on chainB + packet, err := ibctesting.ParsePacketFromEvents(res.GetEvents()) + suite.Require().NoError(err) + suite.Require().NotNil(packet) + + err = path.EndpointB.UpdateClient() + suite.Require().NoError(err) + + res, err = path.EndpointB.RecvPacketWithResult(packet) + suite.Require().NoError(err) + suite.Require().NotNil(res) + + // parse the acknowledgement from result events and acknowledge packet on chainA + ack, err := ibctesting.ParseAckFromEvents(res.GetEvents()) + suite.Require().NoError(err) + suite.Require().NotNil(ack) + + packetKey := host.PacketAcknowledgementKey(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()) + proof, proofHeight := path.EndpointA.Counterparty.QueryProof(packetKey) + + msgAcknowledgement := channeltypes.NewMsgAcknowledgement(packet, ack, proof, proofHeight, path.EndpointA.Chain.SenderAccount.GetAddress().String()) + res, err = suite.chainA.SendMsgs(msgAcknowledgement) + suite.Require().NoError(err) + suite.Require().NotNil(res) + + events := res.GetEvents() + expectedEvents := sdk.Events{ + sdk.NewEvent( + types.EventTypeDistributeFee, + sdk.NewAttribute(types.AttributeKeyReceiver, suite.chainA.SenderAccount.GetAddress().String()), + sdk.NewAttribute(types.AttributeKeyFee, defaultRecvFee.String()), + ), + sdk.NewEvent( + types.EventTypeDistributeFee, + sdk.NewAttribute(types.AttributeKeyReceiver, suite.chainA.SenderAccount.GetAddress().String()), + sdk.NewAttribute(types.AttributeKeyFee, defaultAckFee.String()), + ), + sdk.NewEvent( + types.EventTypeDistributeFee, + sdk.NewAttribute(types.AttributeKeyReceiver, suite.chainA.SenderAccount.GetAddress().String()), + sdk.NewAttribute(types.AttributeKeyFee, defaultTimeoutFee.String()), + ), + } + + for _, evt := range expectedEvents { + suite.Require().Contains(events, evt) + } +} diff --git a/modules/apps/29-fee/keeper/genesis.go b/modules/apps/29-fee/keeper/genesis.go index 150058a734e..256b444e006 100644 --- a/modules/apps/29-fee/keeper/genesis.go +++ b/modules/apps/29-fee/keeper/genesis.go @@ -3,7 +3,7 @@ package keeper import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/ibc-go/v4/modules/apps/29-fee/types" + "github.com/cosmos/ibc-go/v7/modules/apps/29-fee/types" ) // InitGenesis initializes the fee middleware application state from a provided genesis state diff --git a/modules/apps/29-fee/keeper/genesis_test.go b/modules/apps/29-fee/keeper/genesis_test.go index 4aebd5121a9..b2359fc17b0 100644 --- a/modules/apps/29-fee/keeper/genesis_test.go +++ b/modules/apps/29-fee/keeper/genesis_test.go @@ -1,13 +1,13 @@ package keeper_test import ( - "github.com/cosmos/ibc-go/v4/modules/apps/29-fee/types" - channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" - ibctesting "github.com/cosmos/ibc-go/v4/testing" + "github.com/cosmos/ibc-go/v7/modules/apps/29-fee/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + ibctesting "github.com/cosmos/ibc-go/v7/testing" ) func (suite *KeeperTestSuite) TestInitGenesis() { - packetID := channeltypes.NewPacketId(ibctesting.MockFeePort, ibctesting.FirstChannelID, 1) + packetID := channeltypes.NewPacketID(ibctesting.MockFeePort, ibctesting.FirstChannelID, 1) genesisState := types.GenesisState{ IdentifiedFees: []types.IdentifiedPacketFees{ @@ -72,7 +72,7 @@ func (suite *KeeperTestSuite) TestExportGenesis() { // setup & escrow the packet fee refundAcc := suite.chainA.SenderAccount.GetAddress() - packetID := channeltypes.NewPacketId(ibctesting.MockFeePort, ibctesting.FirstChannelID, 1) + packetID := channeltypes.NewPacketID(ibctesting.MockFeePort, ibctesting.FirstChannelID, 1) fee := types.NewFee(defaultRecvFee, defaultAckFee, defaultTimeoutFee) packetFee := types.NewPacketFee(fee, refundAcc.String(), []string{}) diff --git a/modules/apps/29-fee/keeper/grpc_query.go b/modules/apps/29-fee/keeper/grpc_query.go index 3f92686f51f..021459e864a 100644 --- a/modules/apps/29-fee/keeper/grpc_query.go +++ b/modules/apps/29-fee/keeper/grpc_query.go @@ -3,14 +3,14 @@ package keeper import ( "context" + errorsmod "cosmossdk.io/errors" "github.com/cosmos/cosmos-sdk/store/prefix" sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/types/query" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" - "github.com/cosmos/ibc-go/v4/modules/apps/29-fee/types" + "github.com/cosmos/ibc-go/v7/modules/apps/29-fee/types" ) var _ types.QueryServer = Keeper{} @@ -25,7 +25,7 @@ func (k Keeper) IncentivizedPackets(goCtx context.Context, req *types.QueryIncen var identifiedPackets []types.IdentifiedPacketFees store := prefix.NewStore(ctx.KVStore(k.storeKey), []byte(types.FeesInEscrowPrefix)) - _, err := query.Paginate(store, req.Pagination, func(key, value []byte) error { + pagination, err := query.Paginate(store, req.Pagination, func(key, value []byte) error { packetID, err := types.ParseKeyFeesInEscrow(types.FeesInEscrowPrefix + string(key)) if err != nil { return err @@ -41,6 +41,7 @@ func (k Keeper) IncentivizedPackets(goCtx context.Context, req *types.QueryIncen return &types.QueryIncentivizedPacketsResponse{ IncentivizedPackets: identifiedPackets, + Pagination: pagination, }, nil } @@ -56,7 +57,7 @@ func (k Keeper) IncentivizedPacket(goCtx context.Context, req *types.QueryIncent if !exists { return nil, status.Error( codes.NotFound, - sdkerrors.Wrapf(types.ErrFeeNotFound, "channel: %s, port: %s, sequence: %d", req.PacketId.ChannelId, req.PacketId.PortId, req.PacketId.Sequence).Error()) + errorsmod.Wrapf(types.ErrFeeNotFound, "channel: %s, port: %s, sequence: %d", req.PacketId.ChannelId, req.PacketId.PortId, req.PacketId.Sequence).Error()) } return &types.QueryIncentivizedPacketResponse{ @@ -75,7 +76,7 @@ func (k Keeper) IncentivizedPacketsForChannel(goCtx context.Context, req *types. var packets []*types.IdentifiedPacketFees keyPrefix := types.KeyFeesInEscrowChannelPrefix(req.PortId, req.ChannelId) store := prefix.NewStore(ctx.KVStore(k.storeKey), keyPrefix) - _, err := query.Paginate(store, req.Pagination, func(key, value []byte) error { + pagination, err := query.Paginate(store, req.Pagination, func(key, value []byte) error { packetID, err := types.ParseKeyFeesInEscrow(string(keyPrefix) + string(key)) if err != nil { return err @@ -94,6 +95,7 @@ func (k Keeper) IncentivizedPacketsForChannel(goCtx context.Context, req *types. return &types.QueryIncentivizedPacketsForChannelResponse{ IncentivizedPackets: packets, + Pagination: pagination, }, nil } @@ -105,7 +107,7 @@ func (k Keeper) TotalRecvFees(goCtx context.Context, req *types.QueryTotalRecvFe if !found { return nil, status.Errorf( codes.NotFound, - sdkerrors.Wrapf(types.ErrFeeNotFound, "channel: %s, port: %s, sequence: %d", req.PacketId.ChannelId, req.PacketId.PortId, req.PacketId.Sequence).Error(), + errorsmod.Wrapf(types.ErrFeeNotFound, "channel: %s, port: %s, sequence: %d", req.PacketId.ChannelId, req.PacketId.PortId, req.PacketId.Sequence).Error(), ) } @@ -131,7 +133,7 @@ func (k Keeper) TotalAckFees(goCtx context.Context, req *types.QueryTotalAckFees if !found { return nil, status.Errorf( codes.NotFound, - sdkerrors.Wrapf(types.ErrFeeNotFound, "channel: %s, port: %s, sequence: %d", req.PacketId.ChannelId, req.PacketId.PortId, req.PacketId.Sequence).Error(), + errorsmod.Wrapf(types.ErrFeeNotFound, "channel: %s, port: %s, sequence: %d", req.PacketId.ChannelId, req.PacketId.PortId, req.PacketId.Sequence).Error(), ) } @@ -157,7 +159,7 @@ func (k Keeper) TotalTimeoutFees(goCtx context.Context, req *types.QueryTotalTim if !found { return nil, status.Errorf( codes.NotFound, - sdkerrors.Wrapf(types.ErrFeeNotFound, "channel: %s, port: %s, sequence: %d", req.PacketId.ChannelId, req.PacketId.PortId, req.PacketId.Sequence).Error(), + errorsmod.Wrapf(types.ErrFeeNotFound, "channel: %s, port: %s, sequence: %d", req.PacketId.ChannelId, req.PacketId.PortId, req.PacketId.Sequence).Error(), ) } @@ -217,7 +219,7 @@ func (k Keeper) FeeEnabledChannels(goCtx context.Context, req *types.QueryFeeEna var feeEnabledChannels []types.FeeEnabledChannel store := prefix.NewStore(ctx.KVStore(k.storeKey), []byte(types.FeeEnabledKeyPrefix)) - _, err := query.Paginate(store, req.Pagination, func(key, value []byte) error { + pagination, err := query.Paginate(store, req.Pagination, func(key, value []byte) error { portID, channelID, err := types.ParseKeyFeeEnabled(types.FeeEnabledKeyPrefix + string(key)) if err != nil { return err @@ -238,6 +240,7 @@ func (k Keeper) FeeEnabledChannels(goCtx context.Context, req *types.QueryFeeEna return &types.QueryFeeEnabledChannelsResponse{ FeeEnabledChannels: feeEnabledChannels, + Pagination: pagination, }, nil } diff --git a/modules/apps/29-fee/keeper/grpc_query_test.go b/modules/apps/29-fee/keeper/grpc_query_test.go index 1286f3c3350..a90b6e829a9 100644 --- a/modules/apps/29-fee/keeper/grpc_query_test.go +++ b/modules/apps/29-fee/keeper/grpc_query_test.go @@ -7,9 +7,9 @@ import ( "github.com/cosmos/cosmos-sdk/types/query" "github.com/tendermint/tendermint/crypto/secp256k1" - "github.com/cosmos/ibc-go/v4/modules/apps/29-fee/types" - channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" - ibctesting "github.com/cosmos/ibc-go/v4/testing" + "github.com/cosmos/ibc-go/v7/modules/apps/29-fee/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + ibctesting "github.com/cosmos/ibc-go/v7/testing" ) func (suite *KeeperTestSuite) TestQueryIncentivizedPackets() { @@ -33,7 +33,7 @@ func (suite *KeeperTestSuite) TestQueryIncentivizedPackets() { for i := 0; i < 3; i++ { // escrow packet fees for three different packets - packetID := channeltypes.NewPacketId(ibctesting.MockFeePort, ibctesting.FirstChannelID, uint64(i+1)) + packetID := channeltypes.NewPacketID(ibctesting.MockFeePort, ibctesting.FirstChannelID, uint64(i+1)) suite.chainA.GetSimApp().IBCFeeKeeper.SetFeesInEscrow(suite.chainA.GetContext(), packetID, types.NewPacketFees([]types.PacketFee{packetFee})) expectedPackets = append(expectedPackets, types.NewIdentifiedPacketFees(packetID, []types.PacketFee{packetFee})) @@ -96,7 +96,7 @@ func (suite *KeeperTestSuite) TestQueryIncentivizedPacket() { "fees not found for packet id", func() { req = &types.QueryIncentivizedPacketRequest{ - PacketId: channeltypes.NewPacketId(ibctesting.MockFeePort, ibctesting.FirstChannelID, 100), + PacketId: channeltypes.NewPacketID(ibctesting.MockFeePort, ibctesting.FirstChannelID, 100), QueryHeight: 0, } }, @@ -110,7 +110,7 @@ func (suite *KeeperTestSuite) TestQueryIncentivizedPacket() { suite.chainA.GetSimApp().IBCFeeKeeper.SetFeeEnabled(suite.chainA.GetContext(), ibctesting.MockFeePort, ibctesting.FirstChannelID) - packetID := channeltypes.NewPacketId(ibctesting.MockFeePort, ibctesting.FirstChannelID, 1) + packetID := channeltypes.NewPacketID(ibctesting.MockFeePort, ibctesting.FirstChannelID, 1) fee := types.NewFee(defaultRecvFee, defaultAckFee, defaultTimeoutFee) packetFee := types.NewPacketFee(fee, suite.chainA.SenderAccount.GetAddress().String(), []string(nil)) @@ -205,9 +205,9 @@ func (suite *KeeperTestSuite) TestQueryIncentivizedPacketsForChannel() { packetFee := types.NewPacketFee(fee, refundAcc.String(), nil) packetFees := types.NewPacketFees([]types.PacketFee{packetFee, packetFee, packetFee}) - identifiedFees1 := types.NewIdentifiedPacketFees(channeltypes.NewPacketId(ibctesting.MockFeePort, ibctesting.FirstChannelID, 1), packetFees.PacketFees) - identifiedFees2 := types.NewIdentifiedPacketFees(channeltypes.NewPacketId(ibctesting.MockFeePort, ibctesting.FirstChannelID, 2), packetFees.PacketFees) - identifiedFees3 := types.NewIdentifiedPacketFees(channeltypes.NewPacketId(ibctesting.MockFeePort, ibctesting.FirstChannelID, 3), packetFees.PacketFees) + identifiedFees1 := types.NewIdentifiedPacketFees(channeltypes.NewPacketID(ibctesting.MockFeePort, ibctesting.FirstChannelID, 1), packetFees.PacketFees) + identifiedFees2 := types.NewIdentifiedPacketFees(channeltypes.NewPacketID(ibctesting.MockFeePort, ibctesting.FirstChannelID, 2), packetFees.PacketFees) + identifiedFees3 := types.NewIdentifiedPacketFees(channeltypes.NewPacketID(ibctesting.MockFeePort, ibctesting.FirstChannelID, 3), packetFees.PacketFees) expIdentifiedPacketFees = append(expIdentifiedPacketFees, &identifiedFees1, &identifiedFees2, &identifiedFees3) @@ -248,7 +248,7 @@ func (suite *KeeperTestSuite) TestQueryTotalRecvFees() { { "packet not found", func() { - req.PacketId = channeltypes.NewPacketId(ibctesting.MockFeePort, ibctesting.FirstChannelID, 100) + req.PacketId = channeltypes.NewPacketID(ibctesting.MockFeePort, ibctesting.FirstChannelID, 100) }, false, }, @@ -260,7 +260,7 @@ func (suite *KeeperTestSuite) TestQueryTotalRecvFees() { suite.chainA.GetSimApp().IBCFeeKeeper.SetFeeEnabled(suite.chainA.GetContext(), ibctesting.MockFeePort, ibctesting.FirstChannelID) - packetID := channeltypes.NewPacketId(ibctesting.MockFeePort, ibctesting.FirstChannelID, 1) + packetID := channeltypes.NewPacketID(ibctesting.MockFeePort, ibctesting.FirstChannelID, 1) fee := types.NewFee(defaultRecvFee, defaultAckFee, defaultTimeoutFee) packetFee := types.NewPacketFee(fee, suite.chainA.SenderAccount.GetAddress().String(), []string(nil)) @@ -307,7 +307,7 @@ func (suite *KeeperTestSuite) TestQueryTotalAckFees() { { "packet not found", func() { - req.PacketId = channeltypes.NewPacketId(ibctesting.MockFeePort, ibctesting.FirstChannelID, 100) + req.PacketId = channeltypes.NewPacketID(ibctesting.MockFeePort, ibctesting.FirstChannelID, 100) }, false, }, @@ -319,7 +319,7 @@ func (suite *KeeperTestSuite) TestQueryTotalAckFees() { suite.chainA.GetSimApp().IBCFeeKeeper.SetFeeEnabled(suite.chainA.GetContext(), ibctesting.MockFeePort, ibctesting.FirstChannelID) - packetID := channeltypes.NewPacketId(ibctesting.MockFeePort, ibctesting.FirstChannelID, 1) + packetID := channeltypes.NewPacketID(ibctesting.MockFeePort, ibctesting.FirstChannelID, 1) fee := types.NewFee(defaultRecvFee, defaultAckFee, defaultTimeoutFee) packetFee := types.NewPacketFee(fee, suite.chainA.SenderAccount.GetAddress().String(), []string(nil)) @@ -366,7 +366,7 @@ func (suite *KeeperTestSuite) TestQueryTotalTimeoutFees() { { "packet not found", func() { - req.PacketId = channeltypes.NewPacketId(ibctesting.MockFeePort, ibctesting.FirstChannelID, 100) + req.PacketId = channeltypes.NewPacketID(ibctesting.MockFeePort, ibctesting.FirstChannelID, 100) }, false, }, @@ -378,7 +378,7 @@ func (suite *KeeperTestSuite) TestQueryTotalTimeoutFees() { suite.chainA.GetSimApp().IBCFeeKeeper.SetFeeEnabled(suite.chainA.GetContext(), ibctesting.MockFeePort, ibctesting.FirstChannelID) - packetID := channeltypes.NewPacketId(ibctesting.MockFeePort, ibctesting.FirstChannelID, 1) + packetID := channeltypes.NewPacketID(ibctesting.MockFeePort, ibctesting.FirstChannelID, 1) fee := types.NewFee(defaultRecvFee, defaultAckFee, defaultTimeoutFee) packetFee := types.NewPacketFee(fee, suite.chainA.SenderAccount.GetAddress().String(), []string(nil)) @@ -425,14 +425,14 @@ func (suite *KeeperTestSuite) TestQueryPayee() { { "payee address not found: invalid channel", func() { - req.ChannelId = "invalid-channel-id" + req.ChannelId = "invalid-channel-id" //nolint:goconst }, false, }, { "payee address not found: invalid relayer address", func() { - req.Relayer = "invalid-addr" + req.Relayer = "invalid-addr" //nolint:goconst }, false, }, diff --git a/modules/apps/29-fee/keeper/keeper.go b/modules/apps/29-fee/keeper/keeper.go index c60942debea..e2c1691041e 100644 --- a/modules/apps/29-fee/keeper/keeper.go +++ b/modules/apps/29-fee/keeper/keeper.go @@ -2,14 +2,15 @@ package keeper import ( "github.com/cosmos/cosmos-sdk/codec" + storetypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" "github.com/tendermint/tendermint/libs/log" - "github.com/cosmos/ibc-go/v4/modules/apps/29-fee/types" - channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" + "github.com/cosmos/ibc-go/v7/modules/apps/29-fee/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + porttypes "github.com/cosmos/ibc-go/v7/modules/core/05-port/types" + ibcexported "github.com/cosmos/ibc-go/v7/modules/core/exported" ) // Middleware must implement types.ChannelKeeper and types.PortKeeper expected interfaces @@ -21,11 +22,11 @@ var ( // Keeper defines the IBC fungible transfer keeper type Keeper struct { - storeKey sdk.StoreKey + storeKey storetypes.StoreKey cdc codec.BinaryCodec authKeeper types.AccountKeeper - ics4Wrapper types.ICS4Wrapper + ics4Wrapper porttypes.ICS4Wrapper channelKeeper types.ChannelKeeper portKeeper types.PortKeeper bankKeeper types.BankKeeper @@ -33,8 +34,9 @@ type Keeper struct { // NewKeeper creates a new 29-fee Keeper instance func NewKeeper( - cdc codec.BinaryCodec, key sdk.StoreKey, paramSpace paramtypes.Subspace, - ics4Wrapper types.ICS4Wrapper, channelKeeper types.ChannelKeeper, portKeeper types.PortKeeper, authKeeper types.AccountKeeper, bankKeeper types.BankKeeper, + cdc codec.BinaryCodec, key storetypes.StoreKey, + ics4Wrapper porttypes.ICS4Wrapper, channelKeeper types.ChannelKeeper, + portKeeper types.PortKeeper, authKeeper types.AccountKeeper, bankKeeper types.BankKeeper, ) Keeper { return Keeper{ cdc: cdc, @@ -49,7 +51,7 @@ func NewKeeper( // Logger returns a module-specific logger. func (k Keeper) Logger(ctx sdk.Context) log.Logger { - return ctx.Logger().With("module", "x/"+host.ModuleName+"-"+types.ModuleName) + return ctx.Logger().With("module", "x/"+ibcexported.ModuleName+"-"+types.ModuleName) } // BindPort defines a wrapper function for the port Keeper's function in @@ -121,14 +123,14 @@ 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 func (k Keeper) GetAllFeeEnabledChannels(ctx sdk.Context) []types.FeeEnabledChannel { store := ctx.KVStore(k.storeKey) iterator := sdk.KVStorePrefixIterator(store, []byte(types.FeeEnabledKeyPrefix)) - defer iterator.Close() + defer sdk.LogDeferred(ctx.Logger(), func() error { return iterator.Close() }) var enabledChArr []types.FeeEnabledChannel for ; iterator.Valid(); iterator.Next() { @@ -169,7 +171,7 @@ func (k Keeper) SetPayeeAddress(ctx sdk.Context, relayerAddr, payeeAddr, channel func (k Keeper) GetAllPayees(ctx sdk.Context) []types.RegisteredPayee { store := ctx.KVStore(k.storeKey) iterator := sdk.KVStorePrefixIterator(store, []byte(types.PayeeKeyPrefix)) - defer iterator.Close() + defer sdk.LogDeferred(ctx.Logger(), func() error { return iterator.Close() }) var registeredPayees []types.RegisteredPayee for ; iterator.Valid(); iterator.Next() { @@ -214,7 +216,7 @@ func (k Keeper) GetCounterpartyPayeeAddress(ctx sdk.Context, address, channelID func (k Keeper) GetAllCounterpartyPayees(ctx sdk.Context) []types.RegisteredCounterpartyPayee { store := ctx.KVStore(k.storeKey) iterator := sdk.KVStorePrefixIterator(store, []byte(types.CounterpartyPayeeKeyPrefix)) - defer iterator.Close() + defer sdk.LogDeferred(ctx.Logger(), func() error { return iterator.Close() }) var registeredCounterpartyPayees []types.RegisteredCounterpartyPayee for ; iterator.Valid(); iterator.Next() { @@ -257,7 +259,7 @@ func (k Keeper) GetRelayerAddressForAsyncAck(ctx sdk.Context, packetID channelty func (k Keeper) GetAllForwardRelayerAddresses(ctx sdk.Context) []types.ForwardRelayerAddress { store := ctx.KVStore(k.storeKey) iterator := sdk.KVStorePrefixIterator(store, []byte(types.ForwardRelayerPrefix)) - defer iterator.Close() + defer sdk.LogDeferred(ctx.Logger(), func() error { return iterator.Close() }) var forwardRelayerAddr []types.ForwardRelayerAddress for ; iterator.Valid(); iterator.Next() { @@ -289,7 +291,7 @@ func (k Keeper) GetFeesInEscrow(ctx sdk.Context, packetID channeltypes.PacketId) store := ctx.KVStore(k.storeKey) key := types.KeyFeesInEscrow(packetID) bz := store.Get(key) - if bz == nil { + if len(bz) == 0 { return types.PacketFees{}, false } @@ -325,7 +327,7 @@ func (k Keeper) GetIdentifiedPacketFeesForChannel(ctx sdk.Context, portID, chann store := ctx.KVStore(k.storeKey) iterator := sdk.KVStorePrefixIterator(store, types.KeyFeesInEscrowChannelPrefix(portID, channelID)) - defer iterator.Close() + defer sdk.LogDeferred(ctx.Logger(), func() error { return iterator.Close() }) for ; iterator.Valid(); iterator.Next() { packetID, err := types.ParseKeyFeesInEscrow(string(iterator.Key())) if err != nil { @@ -345,7 +347,7 @@ func (k Keeper) GetIdentifiedPacketFeesForChannel(ctx sdk.Context, portID, chann func (k Keeper) GetAllIdentifiedPacketFees(ctx sdk.Context) []types.IdentifiedPacketFees { store := ctx.KVStore(k.storeKey) iterator := sdk.KVStorePrefixIterator(store, []byte(types.FeesInEscrowPrefix)) - defer iterator.Close() + defer sdk.LogDeferred(ctx.Logger(), func() error { return iterator.Close() }) var identifiedFees []types.IdentifiedPacketFees for ; iterator.Valid(); iterator.Next() { diff --git a/modules/apps/29-fee/keeper/keeper_test.go b/modules/apps/29-fee/keeper/keeper_test.go index e4a6a8ab0d8..89dfcd7a30e 100644 --- a/modules/apps/29-fee/keeper/keeper_test.go +++ b/modules/apps/29-fee/keeper/keeper_test.go @@ -8,10 +8,10 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/suite" - "github.com/cosmos/ibc-go/v4/modules/apps/29-fee/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" + "github.com/cosmos/ibc-go/v7/modules/apps/29-fee/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + ibctesting "github.com/cosmos/ibc-go/v7/testing" + ibcmock "github.com/cosmos/ibc-go/v7/testing/mock" ) var ( @@ -114,7 +114,7 @@ func (suite *KeeperTestSuite) TestFeesInEscrow() { suite.coordinator.Setup(suite.path) // escrow five fees for packet sequence 1 - packetID := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, 1) + packetID := channeltypes.NewPacketID(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, 1) fee := types.NewFee(defaultRecvFee, defaultAckFee, defaultTimeoutFee) packetFee := types.NewPacketFee(fee, suite.chainA.SenderAccount.GetAddress().String(), nil) @@ -147,9 +147,9 @@ func (suite *KeeperTestSuite) TestGetIdentifiedPacketFeesForChannel() { // escrow a fee refundAcc := suite.chainA.SenderAccount.GetAddress() - packetID1 := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, 1) - packetID2 := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, 2) - packetID5 := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, 51) + packetID1 := channeltypes.NewPacketID(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, 1) + packetID2 := channeltypes.NewPacketID(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, 2) + packetID5 := channeltypes.NewPacketID(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, 51) fee := types.NewFee(defaultRecvFee, defaultAckFee, defaultTimeoutFee) @@ -161,9 +161,9 @@ func (suite *KeeperTestSuite) TestGetIdentifiedPacketFeesForChannel() { // set fees in escrow for packetIDs on different channel diffChannel := "channel-1" - diffPacketID1 := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, diffChannel, 1) - diffPacketID2 := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, diffChannel, 2) - diffPacketID5 := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, diffChannel, 5) + diffPacketID1 := channeltypes.NewPacketID(suite.path.EndpointA.ChannelConfig.PortID, diffChannel, 1) + diffPacketID2 := channeltypes.NewPacketID(suite.path.EndpointA.ChannelConfig.PortID, diffChannel, 2) + diffPacketID5 := channeltypes.NewPacketID(suite.path.EndpointA.ChannelConfig.PortID, diffChannel, 5) suite.chainA.GetSimApp().IBCFeeKeeper.SetFeesInEscrow(suite.chainA.GetContext(), diffPacketID1, types.NewPacketFees([]types.PacketFee{packetFee})) suite.chainA.GetSimApp().IBCFeeKeeper.SetFeesInEscrow(suite.chainA.GetContext(), diffPacketID2, types.NewPacketFees([]types.PacketFee{packetFee})) suite.chainA.GetSimApp().IBCFeeKeeper.SetFeesInEscrow(suite.chainA.GetContext(), diffPacketID5, types.NewPacketFees([]types.PacketFee{packetFee})) @@ -211,7 +211,7 @@ func (suite *KeeperTestSuite) TestGetAllIdentifiedPacketFees() { // escrow a fee refundAcc := suite.chainA.SenderAccount.GetAddress() - packetID := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, 1) + packetID := channeltypes.NewPacketID(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, 1) fee := types.NewFee(defaultRecvFee, defaultAckFee, defaultTimeoutFee) // escrow the packet fee @@ -237,14 +237,14 @@ func (suite *KeeperTestSuite) TestGetAllIdentifiedPacketFees() { } func (suite *KeeperTestSuite) TestGetAllFeeEnabledChannels() { - validPortId := "ibcmoduleport" + validPortID := "ibcmoduleport" // set two channels enabled suite.chainA.GetSimApp().IBCFeeKeeper.SetFeeEnabled(suite.chainA.GetContext(), ibctesting.MockFeePort, ibctesting.FirstChannelID) - suite.chainA.GetSimApp().IBCFeeKeeper.SetFeeEnabled(suite.chainA.GetContext(), validPortId, ibctesting.FirstChannelID) + suite.chainA.GetSimApp().IBCFeeKeeper.SetFeeEnabled(suite.chainA.GetContext(), validPortID, ibctesting.FirstChannelID) expectedCh := []types.FeeEnabledChannel{ { - PortId: validPortId, + PortId: validPortID, ChannelId: ibctesting.FirstChannelID, }, { diff --git a/modules/apps/29-fee/keeper/msg_server.go b/modules/apps/29-fee/keeper/msg_server.go index c2beea28da3..943ee0453fc 100644 --- a/modules/apps/29-fee/keeper/msg_server.go +++ b/modules/apps/29-fee/keeper/msg_server.go @@ -3,11 +3,12 @@ package keeper import ( "context" + errorsmod "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/cosmos/ibc-go/v4/modules/apps/29-fee/types" - channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" + ibcerrors "github.com/cosmos/ibc-go/v7/internal/errors" + "github.com/cosmos/ibc-go/v7/modules/apps/29-fee/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" ) var _ types.MsgServer = Keeper{} @@ -20,6 +21,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, errorsmod.Wrapf(ibcerrors.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 @@ -33,7 +43,7 @@ func (k Keeper) RegisterPayee(goCtx context.Context, msg *types.MsgRegisterPayee k.Logger(ctx).Info("registering payee address for relayer", "relayer", msg.Relayer, "payee", msg.Payee, "channel", msg.ChannelId) - EmitRegisterPayeeEvent(ctx, msg.Relayer, msg.Payee, msg.ChannelId) + emitRegisterPayeeEvent(ctx, msg.Relayer, msg.Payee, msg.ChannelId) return &types.MsgRegisterPayeeResponse{}, nil } @@ -59,7 +69,7 @@ func (k Keeper) RegisterCounterpartyPayee(goCtx context.Context, msg *types.MsgR k.Logger(ctx).Info("registering counterparty payee for relayer", "relayer", msg.Relayer, "counterparty payee", msg.CounterpartyPayee, "channel", msg.ChannelId) - EmitRegisterCounterpartyPayeeEvent(ctx, msg.Relayer, msg.CounterpartyPayee, msg.ChannelId) + emitRegisterCounterpartyPayeeEvent(ctx, msg.Relayer, msg.CounterpartyPayee, msg.ChannelId) return &types.MsgRegisterCounterpartyPayeeResponse{}, nil } @@ -78,13 +88,26 @@ 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, errorsmod.Wrapf(ibcerrors.ErrUnauthorized, "%s is not allowed to escrow fees", refundAcc) + } + // get the next sequence sequence, found := k.GetNextSequenceSend(ctx, msg.SourcePortId, msg.SourceChannelId) if !found { return nil, channeltypes.ErrSequenceSendNotFound } - packetID := channeltypes.NewPacketId(msg.SourcePortId, msg.SourceChannelId, sequence) + packetID := channeltypes.NewPacketID(msg.SourcePortId, msg.SourceChannelId, sequence) packetFee := types.NewPacketFee(msg.Fee, msg.Signer, msg.Relayers) if err := k.escrowPacketFee(ctx, packetID, packetFee); err != nil { @@ -110,9 +133,22 @@ 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, errorsmod.Wrapf(ibcerrors.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) + return nil, errorsmod.Wrapf(channeltypes.ErrSequenceSendNotFound, "channel does not exist, portID: %s, channelID: %s", msg.PacketId.PortId, msg.PacketId.ChannelId) } // only allow incentivizing of packets which have been sent @@ -122,7 +158,7 @@ func (k Keeper) PayPacketFeeAsync(goCtx context.Context, msg *types.MsgPayPacket // only allow incentivizng of packets which have not completed the packet life cycle if bz := k.GetPacketCommitment(ctx, msg.PacketId.PortId, msg.PacketId.ChannelId, msg.PacketId.Sequence); len(bz) == 0 { - return nil, sdkerrors.Wrapf(channeltypes.ErrPacketCommitmentNotFound, "packet has already been acknowledged or timed out") + return nil, errorsmod.Wrapf(channeltypes.ErrPacketCommitmentNotFound, "packet has already been acknowledged or timed out") } if err := k.escrowPacketFee(ctx, msg.PacketId, msg.PacketFee); err != nil { diff --git a/modules/apps/29-fee/keeper/msg_server_test.go b/modules/apps/29-fee/keeper/msg_server_test.go index e0c84d62c9b..4c7269931ad 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/cosmos/cosmos-sdk/types" - disttypes "github.com/cosmos/cosmos-sdk/x/distribution/types" - - "github.com/cosmos/ibc-go/v4/modules/apps/29-fee/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" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + + "github.com/cosmos/ibc-go/v7/modules/apps/29-fee/types" + transfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + ibctesting "github.com/cosmos/ibc-go/v7/testing" + ibcmock "github.com/cosmos/ibc-go/v7/testing/mock" ) func (suite *KeeperTestSuite) TestRegisterPayee() { - var ( - msg *types.MsgRegisterPayee - ) + var msg *types.MsgRegisterPayee testCases := []struct { name string @@ -29,7 +29,7 @@ func (suite *KeeperTestSuite) TestRegisterPayee() { "channel does not exist", false, func() { - msg.ChannelId = "channel-100" + msg.ChannelId = "channel-100" //nolint:goconst }, }, { @@ -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 { @@ -171,7 +185,7 @@ func (suite *KeeperTestSuite) TestPayPacketFee() { func() { fee := types.NewFee(defaultRecvFee, defaultAckFee, defaultTimeoutFee) - packetID := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, 1) + packetID := channeltypes.NewPacketID(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, 1) packetFee := types.NewPacketFee(fee, suite.chainA.SenderAccount.GetAddress().String(), nil) feesInEscrow := types.NewPacketFees([]types.PacketFee{packetFee}) @@ -184,10 +198,23 @@ func (suite *KeeperTestSuite) TestPayPacketFee() { }, true, }, + { + "bank send enabled for fee denom", + func() { + err := suite.chainA.GetSimApp().BankKeeper.SetParams(suite.chainA.GetContext(), + banktypes.Params{ + SendEnabled: []*banktypes.SendEnabled{{Denom: sdk.DefaultBondDenom, Enabled: true}}, + }, + ) + suite.Require().NoError(err) + }, + 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()) //nolint:errcheck // ignore error for testing + msg.Signer = suite.chainA.GetSimApp().AccountKeeper.GetModuleAddress(ibcmock.ModuleName).String() expPacketFee := types.NewPacketFee(fee, msg.Signer, nil) expFeesInEscrow = []types.PacketFee{expPacketFee} }, @@ -222,6 +249,26 @@ 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() { + err := suite.chainA.GetSimApp().BankKeeper.SetParams(suite.chainA.GetContext(), + banktypes.Params{ + SendEnabled: []*banktypes.SendEnabled{{Denom: sdk.DefaultBondDenom, Enabled: false}}, + }, + ) + suite.Require().NoError(err) + }, + false, + }, { "acknowledgement fee balance not found", func() { @@ -272,7 +319,7 @@ func (suite *KeeperTestSuite) TestPayPacketFee() { if tc.expPass { suite.Require().NoError(err) // message committed - packetID := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, 1) + packetID := channeltypes.NewPacketID(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, 1) feesInEscrow, found := suite.chainA.GetSimApp().IBCFeeKeeper.GetFeesInEscrow(suite.chainA.GetContext(), packetID) suite.Require().True(found) suite.Require().Equal(expFeesInEscrow, feesInEscrow.PacketFees) @@ -312,7 +359,7 @@ func (suite *KeeperTestSuite) TestPayPacketFeeAsync() { func() { fee := types.NewFee(defaultRecvFee, defaultAckFee, defaultTimeoutFee) - packetID := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, 1) + packetID := channeltypes.NewPacketID(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, 1) packetFee := types.NewPacketFee(fee, suite.chainA.SenderAccount.GetAddress().String(), nil) feesInEscrow := types.NewPacketFees([]types.PacketFee{packetFee}) @@ -325,6 +372,18 @@ func (suite *KeeperTestSuite) TestPayPacketFeeAsync() { }, true, }, + { + "bank send enabled for fee denom", + func() { + err := suite.chainA.GetSimApp().BankKeeper.SetParams(suite.chainA.GetContext(), + banktypes.Params{ + SendEnabled: []*banktypes.SendEnabled{{Denom: sdk.DefaultBondDenom, Enabled: true}}, + }, + ) + suite.Require().NoError(err) + }, + true, + }, { "fee module is locked", func() { @@ -354,7 +413,7 @@ func (suite *KeeperTestSuite) TestPayPacketFeeAsync() { { "packet not sent", func() { - msg.PacketId.Sequence = msg.PacketId.Sequence + 1 + msg.PacketId.Sequence++ }, false, }, @@ -369,20 +428,21 @@ func (suite *KeeperTestSuite) TestPayPacketFeeAsync() { { "packet already timed out", func() { - // try to incentivze a packet which is timed out - packetID := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, msg.PacketId.Sequence+1) - packet = channeltypes.NewPacket(ibctesting.MockPacketData, packetID.Sequence, packetID.PortId, packetID.ChannelId, suite.path.EndpointB.ChannelConfig.PortID, suite.path.EndpointB.ChannelID, clienttypes.GetSelfHeight(suite.chainB.GetContext()), 0) + timeoutHeight := clienttypes.GetSelfHeight(suite.chainB.GetContext()) - err := suite.path.EndpointA.SendPacket(packet) + // try to incentivize a packet which is timed out + sequence, err := suite.path.EndpointA.SendPacket(timeoutHeight, 0, ibctesting.MockPacketData) suite.Require().NoError(err) // need to update chainA's client representing chainB to prove missing ack err = suite.path.EndpointA.UpdateClient() suite.Require().NoError(err) + packet = channeltypes.NewPacket(ibctesting.MockPacketData, sequence, suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, suite.path.EndpointB.ChannelConfig.PortID, suite.path.EndpointB.ChannelID, timeoutHeight, 0) err = suite.path.EndpointA.TimeoutPacket(packet) suite.Require().NoError(err) + packetID := channeltypes.NewPacketID(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, sequence) msg.PacketId = packetID }, false, @@ -401,6 +461,26 @@ 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() { + err := suite.chainA.GetSimApp().BankKeeper.SetParams(suite.chainA.GetContext(), + banktypes.Params{ + SendEnabled: []*banktypes.SendEnabled{{Denom: sdk.DefaultBondDenom, Enabled: false}}, + }, + ) + suite.Require().NoError(err) + }, + false, + }, { "acknowledgement fee balance not found", func() { @@ -431,11 +511,13 @@ func (suite *KeeperTestSuite) TestPayPacketFeeAsync() { suite.SetupTest() suite.coordinator.Setup(suite.path) // setup channel + timeoutHeight := clienttypes.NewHeight(clienttypes.ParseChainID(suite.chainB.ChainID), 100) + // send a packet to incentivize - packetID := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, 1) - packet = channeltypes.NewPacket(ibctesting.MockPacketData, packetID.Sequence, packetID.PortId, packetID.ChannelId, suite.path.EndpointB.ChannelConfig.PortID, suite.path.EndpointB.ChannelID, clienttypes.NewHeight(clienttypes.ParseChainID(suite.chainB.ChainID), 100), 0) - err := suite.path.EndpointA.SendPacket(packet) + sequence, err := suite.path.EndpointA.SendPacket(timeoutHeight, 0, ibctesting.MockPacketData) suite.Require().NoError(err) + packetID := channeltypes.NewPacketID(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, sequence) + packet = channeltypes.NewPacket(ibctesting.MockPacketData, packetID.Sequence, packetID.PortId, packetID.ChannelId, suite.path.EndpointB.ChannelConfig.PortID, suite.path.EndpointB.ChannelID, timeoutHeight, 0) fee := types.NewFee(defaultRecvFee, defaultAckFee, defaultTimeoutFee) packetFee := types.NewPacketFee(fee, suite.chainA.SenderAccount.GetAddress().String(), nil) diff --git a/modules/apps/29-fee/keeper/relay.go b/modules/apps/29-fee/keeper/relay.go index 8c2e90f89c7..556e056c22d 100644 --- a/modules/apps/29-fee/keeper/relay.go +++ b/modules/apps/29-fee/keeper/relay.go @@ -3,18 +3,27 @@ package keeper import ( "fmt" + errorsmod "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - "github.com/cosmos/ibc-go/v4/modules/apps/29-fee/types" - channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" - ibcexported "github.com/cosmos/ibc-go/v4/modules/core/exported" + "github.com/cosmos/ibc-go/v7/modules/apps/29-fee/types" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + ibcexported "github.com/cosmos/ibc-go/v7/modules/core/exported" ) -// SendPacket wraps IBC ChannelKeeper's SendPacket function -func (k Keeper) SendPacket(ctx sdk.Context, chanCap *capabilitytypes.Capability, packet ibcexported.PacketI) error { - return k.ics4Wrapper.SendPacket(ctx, chanCap, packet) +// SendPacket wraps the ICS4Wrapper SendPacket function +func (k Keeper) SendPacket( + ctx sdk.Context, + chanCap *capabilitytypes.Capability, + sourcePort string, + sourceChannel string, + timeoutHeight clienttypes.Height, + timeoutTimestamp uint64, + data []byte, +) (uint64, error) { + return k.ics4Wrapper.SendPacket(ctx, chanCap, sourcePort, sourceChannel, timeoutHeight, timeoutTimestamp, data) } // WriteAcknowledgement wraps IBC ChannelKeeper's WriteAcknowledgement function @@ -25,12 +34,12 @@ func (k Keeper) WriteAcknowledgement(ctx sdk.Context, chanCap *capabilitytypes.C return k.ics4Wrapper.WriteAcknowledgement(ctx, chanCap, packet, acknowledgement) } - packetID := channeltypes.NewPacketId(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()) + packetID := channeltypes.NewPacketID(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()) // retrieve the forward relayer that was stored in `onRecvPacket` relayer, found := k.GetRelayerAddressForAsyncAck(ctx, packetID) if !found { - return sdkerrors.Wrapf(types.ErrRelayerNotFoundForAsyncAck, "no relayer address stored for async acknowledgement for packet with portID: %s, channelID: %s, sequence: %d", packetID.PortId, packetID.ChannelId, packetID.Sequence) + return errorsmod.Wrapf(types.ErrRelayerNotFoundForAsyncAck, "no relayer address stored for async acknowledgement for packet with portID: %s, channelID: %s, sequence: %d", packetID.PortId, packetID.ChannelId, packetID.Sequence) } // it is possible that a relayer has not registered a counterparty address. diff --git a/modules/apps/29-fee/keeper/relay_test.go b/modules/apps/29-fee/keeper/relay_test.go index ca58b1661af..f4aa8b6b7bd 100644 --- a/modules/apps/29-fee/keeper/relay_test.go +++ b/modules/apps/29-fee/keeper/relay_test.go @@ -1,11 +1,11 @@ package keeper_test import ( - "github.com/cosmos/ibc-go/v4/modules/apps/29-fee/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" + "github.com/cosmos/ibc-go/v7/modules/apps/29-fee/types" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + ibctesting "github.com/cosmos/ibc-go/v7/testing" + ibcmock "github.com/cosmos/ibc-go/v7/testing/mock" ) func (suite *KeeperTestSuite) TestWriteAcknowledgementAsync() { @@ -17,7 +17,7 @@ func (suite *KeeperTestSuite) TestWriteAcknowledgementAsync() { { "success", func() { - suite.chainB.GetSimApp().IBCFeeKeeper.SetRelayerAddressForAsyncAck(suite.chainB.GetContext(), channeltypes.NewPacketId(suite.path.EndpointB.ChannelConfig.PortID, suite.path.EndpointB.ChannelID, 1), suite.chainA.SenderAccount.GetAddress().String()) + suite.chainB.GetSimApp().IBCFeeKeeper.SetRelayerAddressForAsyncAck(suite.chainB.GetContext(), channeltypes.NewPacketID(suite.path.EndpointB.ChannelConfig.PortID, suite.path.EndpointB.ChannelID, 1), suite.chainA.SenderAccount.GetAddress().String()) suite.chainB.GetSimApp().IBCFeeKeeper.SetCounterpartyPayeeAddress(suite.chainB.GetContext(), suite.chainA.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(), suite.path.EndpointB.ChannelID) }, true, @@ -63,7 +63,7 @@ func (suite *KeeperTestSuite) TestWriteAcknowledgementAsync() { if tc.expPass { suite.Require().NoError(err) - _, found := suite.chainB.GetSimApp().IBCFeeKeeper.GetRelayerAddressForAsyncAck(suite.chainB.GetContext(), channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, 1)) + _, found := suite.chainB.GetSimApp().IBCFeeKeeper.GetRelayerAddressForAsyncAck(suite.chainB.GetContext(), channeltypes.NewPacketID(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, 1)) suite.Require().False(found) expectedAck := types.NewIncentivizedAcknowledgement(suite.chainB.SenderAccount.GetAddress().String(), ack.Acknowledgement(), ack.Success()) diff --git a/modules/apps/29-fee/module.go b/modules/apps/29-fee/module.go index a9bb1ca0d7c..09fea7a74ca 100644 --- a/modules/apps/29-fee/module.go +++ b/modules/apps/29-fee/module.go @@ -4,7 +4,6 @@ import ( "context" "encoding/json" "fmt" - "math/rand" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/codec" @@ -12,14 +11,13 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" simtypes "github.com/cosmos/cosmos-sdk/types/simulation" - "github.com/gorilla/mux" "github.com/grpc-ecosystem/grpc-gateway/runtime" "github.com/spf13/cobra" abci "github.com/tendermint/tendermint/abci/types" - "github.com/cosmos/ibc-go/v4/modules/apps/29-fee/client/cli" - "github.com/cosmos/ibc-go/v4/modules/apps/29-fee/keeper" - "github.com/cosmos/ibc-go/v4/modules/apps/29-fee/types" + "github.com/cosmos/ibc-go/v7/modules/apps/29-fee/client/cli" + "github.com/cosmos/ibc-go/v7/modules/apps/29-fee/keeper" + "github.com/cosmos/ibc-go/v7/modules/apps/29-fee/types" ) var ( @@ -59,13 +57,12 @@ func (AppModuleBasic) ValidateGenesis(cdc codec.JSONCodec, config client.TxEncod return gs.Validate() } -// RegisterRESTRoutes implements AppModuleBasic interface -func (AppModuleBasic) RegisterRESTRoutes(clientCtx client.Context, rtr *mux.Router) { -} - // RegisterGRPCGatewayRoutes registers the gRPC Gateway routes for ics29 fee module. func (AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *runtime.ServeMux) { - types.RegisterQueryHandlerClient(context.Background(), mux, types.NewQueryClient(clientCtx)) + err := types.RegisterQueryHandlerClient(context.Background(), mux, types.NewQueryClient(clientCtx)) + if err != nil { + panic(err) + } } // GetTxCmd implements AppModuleBasic interface @@ -95,21 +92,6 @@ func NewAppModule(k keeper.Keeper) AppModule { func (AppModule) RegisterInvariants(ir sdk.InvariantRegistry) { } -// Route implements the AppModule interface -func (am AppModule) Route() sdk.Route { - return sdk.Route{} -} - -// QuerierRoute implements the AppModule interface -func (AppModule) QuerierRoute() string { - return types.QuerierRoute -} - -// LegacyQuerierHandler implements the AppModule interface -func (am AppModule) LegacyQuerierHandler(*codec.LegacyAmino) sdk.Querier { - return nil -} - // RegisterServices registers module services. func (am AppModule) RegisterServices(cfg module.Configurator) { types.RegisterMsgServer(cfg.MsgServer(), am.keeper) @@ -150,16 +132,6 @@ func (am AppModule) EndBlock(ctx sdk.Context, req abci.RequestEndBlock) []abci.V func (AppModule) GenerateGenesisState(_ *module.SimulationState) { } -// ProposalContents doesn't return any content functions for governance proposals. -func (AppModule) ProposalContents(_ module.SimulationState) []simtypes.WeightedProposalContent { - return nil -} - -// RandomizedParams creates randomized ibc-29-fee param changes for the simulator. -func (AppModule) RandomizedParams(_ *rand.Rand) []simtypes.ParamChange { - return nil -} - // RegisterStoreDecoder registers a decoder for 29-fee module's types func (am AppModule) RegisterStoreDecoder(_ sdk.StoreDecoderRegistry) { } diff --git a/modules/apps/29-fee/transfer_test.go b/modules/apps/29-fee/transfer_test.go index 12a7a556e66..7cf070113ec 100644 --- a/modules/apps/29-fee/transfer_test.go +++ b/modules/apps/29-fee/transfer_test.go @@ -3,10 +3,10 @@ package fee_test import ( sdk "github.com/cosmos/cosmos-sdk/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" - ibctesting "github.com/cosmos/ibc-go/v4/testing" + "github.com/cosmos/ibc-go/v7/modules/apps/29-fee/types" + transfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + ibctesting "github.com/cosmos/ibc-go/v7/testing" ) // Integration test to ensure ics29 works with ics20 @@ -30,7 +30,7 @@ func (suite *FeeTestSuite) TestFeeTransfer() { msgs := []sdk.Msg{ types.NewMsgPayPacketFee(fee, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, suite.chainA.SenderAccount.GetAddress().String(), nil), - transfertypes.NewMsgTransfer(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, coin, suite.chainA.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(), clienttypes.NewHeight(0, 100), 0), + transfertypes.NewMsgTransfer(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, coin, suite.chainA.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(), clienttypes.NewHeight(1, 100), 0, ""), } res, err := suite.chainA.SendMsgs(msgs...) suite.Require().NoError(err) // message committed @@ -66,6 +66,5 @@ func (suite *FeeTestSuite) TestFeeTransfer() { suite.Require().Equal( fee.AckFee.Add(fee.TimeoutFee...), // ack fee paid, timeout fee refunded - sdk.NewCoins(suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), suite.chainA.SenderAccount.GetAddress(), ibctesting.TestCoin.Denom)).Sub(originalChainASenderAccountBalance), - ) + sdk.NewCoins(suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), suite.chainA.SenderAccount.GetAddress(), ibctesting.TestCoin.Denom)).Sub(originalChainASenderAccountBalance[0])) } diff --git a/modules/apps/29-fee/types/ack.pb.go b/modules/apps/29-fee/types/ack.pb.go index 890ba0b76d0..eec38c21121 100644 --- a/modules/apps/29-fee/types/ack.pb.go +++ b/modules/apps/29-fee/types/ack.pb.go @@ -5,8 +5,8 @@ package types import ( fmt "fmt" - _ "github.com/gogo/protobuf/gogoproto" - proto "github.com/gogo/protobuf/proto" + _ "github.com/cosmos/gogoproto/gogoproto" + proto "github.com/cosmos/gogoproto/proto" io "io" math "math" math_bits "math/bits" @@ -95,27 +95,27 @@ func init() { proto.RegisterFile("ibc/applications/fee/v1/ack.proto", fileDescri var fileDescriptor_ab2834946fb65ea4 = []byte{ // 330 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x91, 0xbf, 0x4e, 0xc2, 0x40, - 0x1c, 0xc7, 0x29, 0x26, 0x46, 0x1b, 0xa7, 0x8a, 0x42, 0x30, 0x39, 0xb0, 0x13, 0x0b, 0xbd, 0xe0, - 0x9f, 0x41, 0x37, 0xd8, 0x9c, 0x48, 0xea, 0x62, 0x58, 0x9a, 0xeb, 0xf5, 0x47, 0xbd, 0x70, 0xbd, - 0xbb, 0xdc, 0xb5, 0x25, 0xf5, 0x29, 0x7c, 0x08, 0x1f, 0xc6, 0x91, 0xd1, 0x89, 0x18, 0x78, 0x03, - 0x9e, 0xc0, 0x94, 0x9a, 0x88, 0x06, 0xb7, 0xcb, 0xf7, 0xfb, 0xc9, 0x27, 0x97, 0xdf, 0xd7, 0xbe, - 0x64, 0x21, 0xc5, 0x44, 0x29, 0xce, 0x28, 0x49, 0x99, 0x14, 0x06, 0x4f, 0x01, 0x70, 0x3e, 0xc0, - 0x84, 0xce, 0x3c, 0xa5, 0x65, 0x2a, 0x9d, 0x26, 0x0b, 0xa9, 0xb7, 0x8b, 0x78, 0x53, 0x00, 0x2f, - 0x1f, 0xb4, 0x1b, 0xb1, 0x8c, 0xe5, 0x96, 0xc1, 0xe5, 0xab, 0xc2, 0xdd, 0xb7, 0xba, 0x7d, 0xf1, - 0x20, 0x28, 0x88, 0x94, 0xe5, 0xec, 0x05, 0xa2, 0x21, 0x9d, 0x09, 0x39, 0xe7, 0x10, 0xc5, 0x90, - 0x80, 0x48, 0x9d, 0xb1, 0x7d, 0x4a, 0x94, 0x0a, 0xc8, 0xef, 0xb8, 0x65, 0x75, 0xad, 0xde, 0xc9, - 0x08, 0x6d, 0x96, 0x9d, 0x76, 0x41, 0x12, 0x7e, 0xef, 0xee, 0x81, 0x5c, 0xdf, 0x21, 0x4a, 0xfd, - 0x15, 0x4e, 0xec, 0xe6, 0x54, 0xea, 0x39, 0xd1, 0x51, 0xa0, 0x81, 0x93, 0x02, 0x74, 0x40, 0xa2, - 0x48, 0x83, 0x31, 0xad, 0x7a, 0xd7, 0xea, 0x1d, 0x8f, 0xdc, 0xcd, 0xb2, 0x83, 0x2a, 0xe9, 0x3f, - 0xa0, 0xeb, 0x9f, 0x7d, 0x37, 0x7e, 0x55, 0x0c, 0xab, 0xdc, 0x79, 0xb2, 0xcf, 0x33, 0x11, 0x81, - 0xe6, 0x05, 0x13, 0x71, 0x50, 0x7e, 0xc9, 0x64, 0x94, 0x96, 0xea, 0x83, 0xae, 0xd5, 0x3b, 0xda, - 0x55, 0xef, 0xe7, 0xb8, 0xeb, 0x37, 0x7e, 0x9a, 0xa1, 0x52, 0x8f, 0x55, 0x3e, 0x1a, 0xbf, 0xaf, - 0x90, 0xb5, 0x58, 0x21, 0xeb, 0x73, 0x85, 0xac, 0xd7, 0x35, 0xaa, 0x2d, 0xd6, 0xa8, 0xf6, 0xb1, - 0x46, 0xb5, 0xc9, 0x6d, 0xcc, 0xd2, 0xe7, 0x2c, 0xf4, 0xa8, 0x4c, 0x30, 0x95, 0x26, 0x91, 0x06, - 0xb3, 0x90, 0xf6, 0x63, 0x89, 0xf3, 0x1b, 0x9c, 0xc8, 0x28, 0xe3, 0x60, 0xca, 0xc9, 0x0c, 0xbe, - 0xba, 0xeb, 0x97, 0x6b, 0xa5, 0x85, 0x02, 0x13, 0x1e, 0x6e, 0xcf, 0x7f, 0xfd, 0x15, 0x00, 0x00, - 0xff, 0xff, 0xb3, 0x85, 0x93, 0xfa, 0xd2, 0x01, 0x00, 0x00, + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x91, 0xc1, 0x4a, 0xc3, 0x30, + 0x1c, 0xc6, 0xd7, 0x09, 0xa2, 0xc5, 0x53, 0x9d, 0x6e, 0x4c, 0xc8, 0x66, 0x4f, 0xbb, 0xac, 0x61, + 0x8a, 0x88, 0xde, 0xb6, 0x9b, 0xa7, 0x41, 0xbd, 0xc8, 0x2e, 0x25, 0x4d, 0xff, 0xab, 0x61, 0x69, + 0x12, 0x92, 0xb6, 0xa3, 0x3e, 0x85, 0x0f, 0xe1, 0xc3, 0x78, 0xdc, 0xd1, 0xd3, 0x90, 0xed, 0x0d, + 0xf6, 0x04, 0xd2, 0x55, 0x70, 0xca, 0xbc, 0x85, 0xef, 0xfb, 0xf1, 0x23, 0xfc, 0x3f, 0xfb, 0x92, + 0x85, 0x14, 0x13, 0xa5, 0x38, 0xa3, 0x24, 0x65, 0x52, 0x18, 0x3c, 0x05, 0xc0, 0xf9, 0x00, 0x13, + 0x3a, 0xf3, 0x94, 0x96, 0xa9, 0x74, 0x9a, 0x2c, 0xa4, 0xde, 0x2e, 0xe2, 0x4d, 0x01, 0xbc, 0x7c, + 0xd0, 0x6e, 0xc4, 0x32, 0x96, 0x5b, 0x06, 0x97, 0xaf, 0x0a, 0x77, 0xdf, 0xea, 0xf6, 0xc5, 0x83, + 0xa0, 0x20, 0x52, 0x96, 0xb3, 0x17, 0x88, 0x86, 0x74, 0x26, 0xe4, 0x9c, 0x43, 0x14, 0x43, 0x02, + 0x22, 0x75, 0xc6, 0xf6, 0x29, 0x51, 0x2a, 0x20, 0xbf, 0xe3, 0x96, 0xd5, 0xb5, 0x7a, 0x27, 0x23, + 0xb4, 0x59, 0x76, 0xda, 0x05, 0x49, 0xf8, 0xbd, 0xbb, 0x07, 0x72, 0x7d, 0x87, 0x28, 0xf5, 0x57, + 0x38, 0xb1, 0x9b, 0x53, 0xa9, 0xe7, 0x44, 0x47, 0x81, 0x06, 0x4e, 0x0a, 0xd0, 0x01, 0x89, 0x22, + 0x0d, 0xc6, 0xb4, 0xea, 0x5d, 0xab, 0x77, 0x3c, 0x72, 0x37, 0xcb, 0x0e, 0xaa, 0xa4, 0xff, 0x80, + 0xae, 0x7f, 0xf6, 0xdd, 0xf8, 0x55, 0x31, 0xac, 0x72, 0xe7, 0xc9, 0x3e, 0xcf, 0x44, 0x04, 0x9a, + 0x17, 0x4c, 0xc4, 0x41, 0xf9, 0x25, 0x93, 0x51, 0x5a, 0xaa, 0x0f, 0xba, 0x56, 0xef, 0x68, 0x57, + 0xbd, 0x9f, 0xe3, 0xae, 0xdf, 0xf8, 0x69, 0x86, 0x4a, 0x3d, 0x56, 0xf9, 0x68, 0xfc, 0xbe, 0x42, + 0xd6, 0x62, 0x85, 0xac, 0xcf, 0x15, 0xb2, 0x5e, 0xd7, 0xa8, 0xb6, 0x58, 0xa3, 0xda, 0xc7, 0x1a, + 0xd5, 0x26, 0x37, 0x31, 0x4b, 0x9f, 0xb3, 0xd0, 0xa3, 0x32, 0xc1, 0x54, 0x9a, 0x44, 0x1a, 0xcc, + 0x42, 0xda, 0x8f, 0x25, 0xce, 0x6f, 0x71, 0x22, 0xa3, 0x8c, 0x83, 0x29, 0x27, 0x33, 0xf8, 0xea, + 0xae, 0x5f, 0xae, 0x95, 0x16, 0x0a, 0x4c, 0x78, 0xb8, 0x3d, 0xff, 0xf5, 0x57, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xaa, 0xbb, 0x40, 0x71, 0xd2, 0x01, 0x00, 0x00, } func (m *IncentivizedAcknowledgement) Marshal() (dAtA []byte, err error) { diff --git a/modules/apps/29-fee/types/errors.go b/modules/apps/29-fee/types/errors.go index 0ac40e5f824..26167c71016 100644 --- a/modules/apps/29-fee/types/errors.go +++ b/modules/apps/29-fee/types/errors.go @@ -1,19 +1,19 @@ package types import ( - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + errorsmod "cosmossdk.io/errors" ) // 29-fee sentinel errors var ( - ErrInvalidVersion = sdkerrors.Register(ModuleName, 2, "invalid ICS29 middleware version") - 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") - 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") - ErrRelayerNotFoundForAsyncAck = sdkerrors.Register(ModuleName, 10, "relayer address must be stored for async WriteAcknowledgement") - ErrFeeModuleLocked = sdkerrors.Register(ModuleName, 11, "the fee module is currently locked, a severe bug has been detected") + ErrInvalidVersion = errorsmod.Register(ModuleName, 2, "invalid ICS29 middleware version") + ErrRefundAccNotFound = errorsmod.Register(ModuleName, 3, "no account found for given refund address") + ErrBalanceNotFound = errorsmod.Register(ModuleName, 4, "balance not found for given account address") + ErrFeeNotFound = errorsmod.Register(ModuleName, 5, "there is no fee escrowed for the given packetID") + ErrRelayersNotEmpty = errorsmod.Register(ModuleName, 6, "relayers must not be set. This feature is not supported") + ErrCounterpartyPayeeEmpty = errorsmod.Register(ModuleName, 7, "counterparty payee must not be empty") + ErrForwardRelayerAddressNotFound = errorsmod.Register(ModuleName, 8, "forward relayer address not found") + ErrFeeNotEnabled = errorsmod.Register(ModuleName, 9, "fee module is not enabled for this channel. If this error occurs after channel setup, fee module may not be enabled") + ErrRelayerNotFoundForAsyncAck = errorsmod.Register(ModuleName, 10, "relayer address must be stored for async WriteAcknowledgement") + ErrFeeModuleLocked = errorsmod.Register(ModuleName, 11, "the fee module is currently locked, a severe bug has been detected") ) diff --git a/modules/apps/29-fee/types/events.go b/modules/apps/29-fee/types/events.go index cffca5dabdd..ea9cdd0f870 100644 --- a/modules/apps/29-fee/types/events.go +++ b/modules/apps/29-fee/types/events.go @@ -5,6 +5,7 @@ const ( EventTypeIncentivizedPacket = "incentivized_ibc_packet" EventTypeRegisterPayee = "register_payee" EventTypeRegisterCounterpartyPayee = "register_counterparty_payee" + EventTypeDistributeFee = "distribute_fee" AttributeKeyRecvFee = "recv_fee" AttributeKeyAckFee = "ack_fee" @@ -13,4 +14,6 @@ const ( AttributeKeyRelayer = "relayer" AttributeKeyPayee = "payee" AttributeKeyCounterpartyPayee = "counterparty_payee" + AttributeKeyReceiver = "receiver" + AttributeKeyFee = "fee" ) diff --git a/modules/apps/29-fee/types/expected_keepers.go b/modules/apps/29-fee/types/expected_keepers.go index 9e6abfd358b..589224b9d2f 100644 --- a/modules/apps/29-fee/types/expected_keepers.go +++ b/modules/apps/29-fee/types/expected_keepers.go @@ -5,8 +5,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/auth/types" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" - ibcexported "github.com/cosmos/ibc-go/v4/modules/core/exported" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" ) // AccountKeeper defines the contract required for account APIs. @@ -15,13 +14,6 @@ type AccountKeeper interface { GetAccount(sdk.Context, sdk.AccAddress) types.AccountI } -// ICS4Wrapper defines the expected ICS4Wrapper for middleware -type ICS4Wrapper interface { - WriteAcknowledgement(ctx sdk.Context, chanCap *capabilitytypes.Capability, packet ibcexported.PacketI, acknowledgement ibcexported.Acknowledgement) error - SendPacket(ctx sdk.Context, channelCap *capabilitytypes.Capability, packet ibcexported.PacketI) error - GetAppVersion(ctx sdk.Context, portID, channelID string) (string, bool) -} - // ChannelKeeper defines the expected IBC channel keeper type ChannelKeeper interface { GetChannel(ctx sdk.Context, srcPort, srcChan string) (channel channeltypes.Channel, found bool) @@ -41,4 +33,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 774d8857e56..81948ebdcbc 100644 --- a/modules/apps/29-fee/types/fee.go +++ b/modules/apps/29-fee/types/fee.go @@ -3,13 +3,14 @@ package types import ( "strings" + errorsmod "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" + ibcerrors "github.com/cosmos/ibc-go/v7/internal/errors" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" ) -// NewPacketFee creates and returns a new PacketFee struct including the incentivization fees, refund addres and relayers +// NewPacketFee creates and returns a new PacketFee struct including the incentivization fees, refund address and relayers func NewPacketFee(fee Fee, refundAddr string, relayers []string) PacketFee { return PacketFee{ Fee: fee, @@ -22,12 +23,12 @@ func NewPacketFee(fee Fee, refundAddr string, relayers []string) PacketFee { func (p PacketFee) Validate() error { _, err := sdk.AccAddressFromBech32(p.RefundAddress) if err != nil { - return sdkerrors.Wrap(err, "failed to convert RefundAddress into sdk.AccAddress") + return errorsmod.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 { @@ -67,25 +68,25 @@ func (f Fee) Total() sdk.Coins { } // Validate asserts that each Fee is valid and all three Fees are not empty or zero -func (fee Fee) Validate() error { +func (f Fee) Validate() error { var errFees []string - if !fee.AckFee.IsValid() { + if !f.AckFee.IsValid() { errFees = append(errFees, "ack fee invalid") } - if !fee.RecvFee.IsValid() { + if !f.RecvFee.IsValid() { errFees = append(errFees, "recv fee invalid") } - if !fee.TimeoutFee.IsValid() { + if !f.TimeoutFee.IsValid() { errFees = append(errFees, "timeout fee invalid") } if len(errFees) > 0 { - return sdkerrors.Wrapf(sdkerrors.ErrInvalidCoins, "contains invalid fees: %s", strings.Join(errFees, " , ")) + return errorsmod.Wrapf(ibcerrors.ErrInvalidCoins, "contains invalid fees: %s", strings.Join(errFees, " , ")) } // if all three fee's are zero or empty return an error - if fee.AckFee.IsZero() && fee.RecvFee.IsZero() && fee.TimeoutFee.IsZero() { - return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, "all fees are zero") + if f.AckFee.IsZero() && f.RecvFee.IsZero() && f.TimeoutFee.IsZero() { + return errorsmod.Wrap(ibcerrors.ErrInvalidCoins, "all fees are zero") } return nil diff --git a/modules/apps/29-fee/types/fee.pb.go b/modules/apps/29-fee/types/fee.pb.go index d36f5e7cb18..7268ecfb746 100644 --- a/modules/apps/29-fee/types/fee.pb.go +++ b/modules/apps/29-fee/types/fee.pb.go @@ -7,9 +7,9 @@ import ( fmt "fmt" github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" types "github.com/cosmos/cosmos-sdk/types" - types1 "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" - _ "github.com/gogo/protobuf/gogoproto" - proto "github.com/gogo/protobuf/proto" + _ "github.com/cosmos/gogoproto/gogoproto" + proto "github.com/cosmos/gogoproto/proto" + types1 "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" io "io" math "math" math_bits "math/bits" @@ -266,39 +266,39 @@ func init() { proto.RegisterFile("ibc/applications/fee/v1/fee.proto", fileDescri var fileDescriptor_cb3319f1af2a53e5 = []byte{ // 525 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x94, 0x31, 0x6f, 0x13, 0x31, - 0x14, 0xc7, 0x73, 0x09, 0x6a, 0x1b, 0x47, 0x14, 0x74, 0x2a, 0x22, 0x8d, 0xe0, 0x52, 0x3c, 0x65, - 0x89, 0xad, 0x84, 0x32, 0xc0, 0x04, 0x57, 0x29, 0x52, 0x27, 0xd0, 0x89, 0x89, 0x25, 0xf2, 0xd9, - 0x2f, 0xa9, 0x95, 0xdc, 0xf9, 0x74, 0xbe, 0x44, 0xca, 0xca, 0x27, 0xe0, 0x1b, 0xb0, 0xf3, 0x49, - 0xba, 0x20, 0x75, 0x64, 0x0a, 0x28, 0xf9, 0x06, 0xdd, 0x91, 0x90, 0x7d, 0x4e, 0x94, 0x82, 0xaa, - 0xaa, 0x12, 0xd3, 0xf9, 0xd9, 0xef, 0xef, 0xdf, 0xb3, 0xdf, 0xff, 0x8c, 0x5e, 0xc8, 0x98, 0x53, - 0x96, 0x65, 0x53, 0xc9, 0x59, 0x21, 0x55, 0xaa, 0xe9, 0x08, 0x80, 0xce, 0x7b, 0xe6, 0x43, 0xb2, - 0x5c, 0x15, 0xca, 0x7f, 0x2a, 0x63, 0x4e, 0x76, 0x53, 0x88, 0x59, 0x9b, 0xf7, 0x5a, 0x01, 0x57, - 0x3a, 0x51, 0x9a, 0xc6, 0x4c, 0x1b, 0x49, 0x0c, 0x05, 0xeb, 0x51, 0xae, 0x64, 0x5a, 0x0a, 0x5b, - 0x47, 0x63, 0x35, 0x56, 0x76, 0x48, 0xcd, 0xc8, 0xcd, 0x5a, 0x22, 0x57, 0x39, 0x50, 0x7e, 0xc1, - 0xd2, 0x14, 0xa6, 0x86, 0xe6, 0x86, 0x65, 0x0a, 0xfe, 0x5d, 0x45, 0xb5, 0x01, 0x80, 0xbf, 0x40, - 0x07, 0x39, 0xf0, 0xf9, 0x70, 0x04, 0xd0, 0xf4, 0x4e, 0x6a, 0x9d, 0x46, 0xff, 0x98, 0x94, 0x4c, - 0x62, 0x98, 0xc4, 0x31, 0xc9, 0x99, 0x92, 0x69, 0x78, 0x76, 0xb9, 0x6c, 0x57, 0xae, 0x97, 0xed, - 0x47, 0x0b, 0x96, 0x4c, 0xdf, 0xe0, 0x8d, 0x10, 0x7f, 0xfb, 0xd9, 0xee, 0x8c, 0x65, 0x71, 0x31, - 0x8b, 0x09, 0x57, 0x09, 0x75, 0x35, 0x97, 0x9f, 0xae, 0x16, 0x13, 0x5a, 0x2c, 0x32, 0xd0, 0x76, - 0x0f, 0x1d, 0xed, 0x1b, 0x99, 0x41, 0xcf, 0xd1, 0x3e, 0xe3, 0x13, 0x4b, 0xae, 0xde, 0x45, 0x0e, - 0x1d, 0xf9, 0xb0, 0x24, 0x3b, 0xdd, 0xfd, 0xc0, 0x7b, 0x8c, 0x4f, 0x0c, 0xf7, 0xb3, 0x87, 0x1a, - 0x85, 0x4c, 0x40, 0xcd, 0x0a, 0x0b, 0xaf, 0xdd, 0x05, 0x1f, 0x38, 0xb8, 0x5f, 0xc2, 0x77, 0xb4, - 0xf7, 0x2b, 0x00, 0x39, 0xe5, 0x00, 0x00, 0x7f, 0xf5, 0x50, 0xfd, 0x03, 0xe3, 0x13, 0x30, 0x91, - 0x7f, 0x8a, 0x6a, 0x65, 0x03, 0xbc, 0x4e, 0xa3, 0xff, 0x8c, 0xdc, 0xe2, 0x06, 0x32, 0x00, 0x08, - 0x1f, 0x98, 0x62, 0x22, 0x93, 0xee, 0xbf, 0x45, 0x87, 0x39, 0x8c, 0x66, 0xa9, 0x18, 0x32, 0x21, - 0x72, 0xd0, 0xba, 0x59, 0x3d, 0xf1, 0x3a, 0xf5, 0xf0, 0xf8, 0x7a, 0xd9, 0x7e, 0xb2, 0x69, 0xd1, - 0xee, 0x3a, 0x8e, 0x1e, 0x96, 0x13, 0xef, 0xca, 0xd8, 0x6f, 0x99, 0xee, 0x4f, 0xd9, 0x02, 0x72, + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x94, 0x41, 0x8b, 0x13, 0x31, + 0x14, 0xc7, 0x3b, 0xad, 0xec, 0x6e, 0x53, 0x5c, 0x65, 0x58, 0xb1, 0x5b, 0x74, 0xba, 0xe6, 0xd4, + 0x4b, 0x13, 0x5a, 0x15, 0xd1, 0x93, 0xce, 0x42, 0x61, 0x4f, 0xca, 0xe0, 0xc9, 0x4b, 0xc9, 0x24, + 0xaf, 0xdd, 0xd0, 0xce, 0x64, 0x98, 0x4c, 0x0b, 0xbd, 0xfa, 0x09, 0xfc, 0x06, 0xde, 0xfd, 0x24, + 0x7b, 0x11, 0xf6, 0xe8, 0xa9, 0x4a, 0xfb, 0x0d, 0xf6, 0x2e, 0x48, 0x32, 0x69, 0xe9, 0x2a, 0xcb, + 0xb2, 0xe0, 0x69, 0xf2, 0x92, 0xf7, 0xcf, 0xef, 0x25, 0xef, 0x3f, 0x41, 0xcf, 0x64, 0xcc, 0x29, + 0xcb, 0xb2, 0xa9, 0xe4, 0xac, 0x90, 0x2a, 0xd5, 0x74, 0x04, 0x40, 0xe7, 0x3d, 0xf3, 0x21, 0x59, + 0xae, 0x0a, 0xe5, 0x3f, 0x96, 0x31, 0x27, 0xbb, 0x29, 0xc4, 0xac, 0xcd, 0x7b, 0xad, 0x80, 0x2b, + 0x9d, 0x28, 0x4d, 0x63, 0xa6, 0x8d, 0x24, 0x86, 0x82, 0xf5, 0x28, 0x57, 0x32, 0x2d, 0x85, 0xad, + 0xa3, 0xb1, 0x1a, 0x2b, 0x3b, 0xa4, 0x66, 0xe4, 0x66, 0x2d, 0x91, 0xab, 0x1c, 0x28, 0x3f, 0x67, + 0x69, 0x0a, 0x53, 0x43, 0x73, 0xc3, 0x32, 0x05, 0xff, 0xae, 0xa2, 0xda, 0x00, 0xc0, 0x5f, 0xa0, + 0x83, 0x1c, 0xf8, 0x7c, 0x38, 0x02, 0x68, 0x7a, 0x27, 0xb5, 0x4e, 0xa3, 0x7f, 0x4c, 0x4a, 0x26, + 0x31, 0x4c, 0xe2, 0x98, 0xe4, 0x54, 0xc9, 0x34, 0x3c, 0xbd, 0x58, 0xb6, 0x2b, 0x57, 0xcb, 0xf6, + 0x83, 0x05, 0x4b, 0xa6, 0x6f, 0xf0, 0x46, 0x88, 0xbf, 0xfd, 0x6c, 0x77, 0xc6, 0xb2, 0x38, 0x9f, + 0xc5, 0x84, 0xab, 0x84, 0xba, 0x9a, 0xcb, 0x4f, 0x57, 0x8b, 0x09, 0x2d, 0x16, 0x19, 0x68, 0xbb, + 0x87, 0x8e, 0xf6, 0x8d, 0xcc, 0xa0, 0xe7, 0x68, 0x9f, 0xf1, 0x89, 0x25, 0x57, 0x6f, 0x23, 0x87, + 0x8e, 0x7c, 0x58, 0x92, 0x9d, 0xee, 0x6e, 0xe0, 0x3d, 0xc6, 0x27, 0x86, 0xfb, 0xd9, 0x43, 0x8d, + 0x42, 0x26, 0xa0, 0x66, 0x85, 0x85, 0xd7, 0x6e, 0x83, 0x0f, 0x1c, 0xdc, 0x2f, 0xe1, 0x3b, 0xda, + 0xbb, 0x15, 0x80, 0x9c, 0x72, 0x00, 0x80, 0xbf, 0x7a, 0xa8, 0xfe, 0x81, 0xf1, 0x09, 0x98, 0xc8, + 0x7f, 0x81, 0x6a, 0x65, 0x03, 0xbc, 0x4e, 0xa3, 0xff, 0x84, 0xdc, 0xe0, 0x06, 0x32, 0x00, 0x08, + 0xef, 0x99, 0x62, 0x22, 0x93, 0xee, 0xbf, 0x45, 0x87, 0x39, 0x8c, 0x66, 0xa9, 0x18, 0x32, 0x21, + 0x72, 0xd0, 0xba, 0x59, 0x3d, 0xf1, 0x3a, 0xf5, 0xf0, 0xf8, 0x6a, 0xd9, 0x7e, 0xb4, 0x69, 0xd1, + 0xee, 0x3a, 0x8e, 0xee, 0x97, 0x13, 0xef, 0xca, 0xd8, 0x6f, 0x99, 0xee, 0x4f, 0xd9, 0x02, 0x72, 0x6d, 0xaf, 0xa1, 0x1e, 0x6d, 0x63, 0x9c, 0x20, 0xb4, 0x2d, 0x50, 0xfb, 0x43, 0xd4, 0xc8, 0x6c, - 0x64, 0x8e, 0xad, 0x9d, 0x55, 0xf0, 0xad, 0x95, 0x6e, 0x95, 0x61, 0xeb, 0xe6, 0xe5, 0xed, 0x6c, - 0x82, 0x23, 0x94, 0x6d, 0x01, 0xf8, 0xbb, 0x87, 0x8e, 0xce, 0x05, 0xa4, 0x85, 0x1c, 0x49, 0x10, - 0x3b, 0xe4, 0x8f, 0xa8, 0xee, 0x44, 0x52, 0xb8, 0x1b, 0x7a, 0x6e, 0xb9, 0xc6, 0xe0, 0x64, 0xe3, - 0xea, 0x2d, 0xf3, 0x5c, 0x84, 0x4d, 0x87, 0x7c, 0x7c, 0x03, 0x29, 0x05, 0x8e, 0x0e, 0x32, 0x97, - 0xf3, 0xf7, 0x79, 0xaa, 0xff, 0xfb, 0x3c, 0xe1, 0xfb, 0xcb, 0x55, 0xe0, 0x5d, 0xad, 0x02, 0xef, - 0xd7, 0x2a, 0xf0, 0xbe, 0xac, 0x83, 0xca, 0xd5, 0x3a, 0xa8, 0xfc, 0x58, 0x07, 0x95, 0x4f, 0xaf, - 0xfe, 0x35, 0x8c, 0x8c, 0x79, 0x77, 0xac, 0xe8, 0xfc, 0x94, 0x26, 0x4a, 0xcc, 0xa6, 0xa0, 0xcd, - 0x7b, 0xa1, 0x69, 0xff, 0x75, 0xd7, 0x3c, 0x15, 0xd6, 0x43, 0xf1, 0x9e, 0xfd, 0x71, 0x5f, 0xfe, - 0x09, 0x00, 0x00, 0xff, 0xff, 0x7a, 0x01, 0xeb, 0x42, 0x4f, 0x04, 0x00, 0x00, + 0x64, 0x8e, 0xad, 0x9d, 0x55, 0xf0, 0x8d, 0x95, 0x6e, 0x95, 0x61, 0xeb, 0xfa, 0xe5, 0xed, 0x6c, + 0x82, 0x23, 0x94, 0x6d, 0x01, 0xf8, 0xbb, 0x87, 0x8e, 0xce, 0x04, 0xa4, 0x85, 0x1c, 0x49, 0x10, + 0x3b, 0xe4, 0x8f, 0xa8, 0xee, 0x44, 0x52, 0xb8, 0x1b, 0x7a, 0x6a, 0xb9, 0xc6, 0xe0, 0x64, 0xe3, + 0xea, 0x2d, 0xf3, 0x4c, 0x84, 0x4d, 0x87, 0x7c, 0x78, 0x0d, 0x29, 0x05, 0x8e, 0x0e, 0x32, 0x97, + 0xf3, 0xf7, 0x79, 0xaa, 0xff, 0xfb, 0x3c, 0xe1, 0xfb, 0x8b, 0x55, 0xe0, 0x5d, 0xae, 0x02, 0xef, + 0xd7, 0x2a, 0xf0, 0xbe, 0xac, 0x83, 0xca, 0xe5, 0x3a, 0xa8, 0xfc, 0x58, 0x07, 0x95, 0x4f, 0x2f, + 0xff, 0x35, 0x8c, 0x8c, 0x79, 0x77, 0xac, 0xe8, 0xfc, 0x15, 0x4d, 0x94, 0x98, 0x4d, 0x41, 0x9b, + 0xf7, 0x42, 0xd3, 0xfe, 0xeb, 0xae, 0x79, 0x2a, 0xac, 0x87, 0xe2, 0x3d, 0xfb, 0xe3, 0x3e, 0xff, + 0x13, 0x00, 0x00, 0xff, 0xff, 0x63, 0x3f, 0x38, 0xc9, 0x4f, 0x04, 0x00, 0x00, } func (m *Fee) Marshal() (dAtA []byte, err error) { diff --git a/modules/apps/29-fee/types/fee_test.go b/modules/apps/29-fee/types/fee_test.go index 3abe0e382b3..71758559baa 100644 --- a/modules/apps/29-fee/types/fee_test.go +++ b/modules/apps/29-fee/types/fee_test.go @@ -7,7 +7,7 @@ import ( "github.com/stretchr/testify/require" "github.com/tendermint/tendermint/crypto/secp256k1" - "github.com/cosmos/ibc-go/v4/modules/apps/29-fee/types" + "github.com/cosmos/ibc-go/v7/modules/apps/29-fee/types" ) var ( @@ -27,6 +27,8 @@ var ( defaultAccAddress = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()).String() ) +const invalidAddress = "invalid-address" + func TestFeeTotal(t *testing.T) { fee := types.NewFee(defaultRecvFee, defaultAckFee, defaultTimeoutFee) @@ -47,10 +49,17 @@ 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() { - packetFee.RefundAddress = "invalid-address" + packetFee.RefundAddress = invalidAddress }, false, }, @@ -102,6 +111,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 +129,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/genesis.go b/modules/apps/29-fee/types/genesis.go index 8c9281faa15..e08d457367d 100644 --- a/modules/apps/29-fee/types/genesis.go +++ b/modules/apps/29-fee/types/genesis.go @@ -3,10 +3,11 @@ package types import ( "strings" + errorsmod "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" + ibcerrors "github.com/cosmos/ibc-go/v7/internal/errors" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" ) // NewGenesisState creates a 29-fee GenesisState instance. @@ -56,36 +57,36 @@ func (gs GenesisState) Validate() error { // Validate FeeEnabledChannels for _, feeCh := range gs.FeeEnabledChannels { if err := host.PortIdentifierValidator(feeCh.PortId); err != nil { - return sdkerrors.Wrap(err, "invalid source port ID") + return errorsmod.Wrap(err, "invalid source port ID") } if err := host.ChannelIdentifierValidator(feeCh.ChannelId); err != nil { - return sdkerrors.Wrap(err, "invalid source channel ID") + return errorsmod.Wrap(err, "invalid source channel ID") } } // Validate RegisteredPayees for _, registeredPayee := range gs.RegisteredPayees { if registeredPayee.Relayer == registeredPayee.Payee { - return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "relayer address and payee address must not be equal") + return errorsmod.Wrap(ibcerrors.ErrInvalidAddress, "relayer address and payee address must not be equal") } if _, err := sdk.AccAddressFromBech32(registeredPayee.Relayer); err != nil { - return sdkerrors.Wrap(err, "failed to convert relayer address into sdk.AccAddress") + return errorsmod.Wrap(err, "failed to convert relayer address into sdk.AccAddress") } if _, err := sdk.AccAddressFromBech32(registeredPayee.Payee); err != nil { - return sdkerrors.Wrap(err, "failed to convert payee address into sdk.AccAddress") + return errorsmod.Wrap(err, "failed to convert payee address into sdk.AccAddress") } if err := host.ChannelIdentifierValidator(registeredPayee.ChannelId); err != nil { - return sdkerrors.Wrapf(err, "invalid channel identifier: %s", registeredPayee.ChannelId) + return errorsmod.Wrapf(err, "invalid channel identifier: %s", registeredPayee.ChannelId) } } // Validate RegisteredCounterpartyPayees for _, registeredCounterpartyPayee := range gs.RegisteredCounterpartyPayees { if _, err := sdk.AccAddressFromBech32(registeredCounterpartyPayee.Relayer); err != nil { - return sdkerrors.Wrap(err, "failed to convert relayer address into sdk.AccAddress") + return errorsmod.Wrap(err, "failed to convert relayer address into sdk.AccAddress") } if strings.TrimSpace(registeredCounterpartyPayee.CounterpartyPayee) == "" { @@ -93,14 +94,14 @@ func (gs GenesisState) Validate() error { } if err := host.ChannelIdentifierValidator(registeredCounterpartyPayee.ChannelId); err != nil { - return sdkerrors.Wrapf(err, "invalid channel identifier: %s", registeredCounterpartyPayee.ChannelId) + return errorsmod.Wrapf(err, "invalid channel identifier: %s", registeredCounterpartyPayee.ChannelId) } } // Validate ForwardRelayers for _, rel := range gs.ForwardRelayers { if _, err := sdk.AccAddressFromBech32(rel.Address); err != nil { - return sdkerrors.Wrap(err, "failed to convert forward relayer address into sdk.AccAddress") + return errorsmod.Wrap(err, "failed to convert forward relayer address into sdk.AccAddress") } if err := rel.PacketId.Validate(); err != nil { diff --git a/modules/apps/29-fee/types/genesis.pb.go b/modules/apps/29-fee/types/genesis.pb.go index 4fc869a658c..8bca28ff748 100644 --- a/modules/apps/29-fee/types/genesis.pb.go +++ b/modules/apps/29-fee/types/genesis.pb.go @@ -5,9 +5,9 @@ package types import ( fmt "fmt" - types "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" - _ "github.com/gogo/protobuf/gogoproto" - proto "github.com/gogo/protobuf/proto" + _ "github.com/cosmos/gogoproto/gogoproto" + proto "github.com/cosmos/gogoproto/proto" + types "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" io "io" math "math" math_bits "math/bits" @@ -358,48 +358,48 @@ func init() { } var fileDescriptor_7191992e856dff95 = []byte{ - // 649 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x54, 0xcf, 0x4e, 0xd4, 0x4e, - 0x1c, 0xdf, 0xc2, 0x0f, 0xf8, 0x31, 0x18, 0x60, 0x27, 0x20, 0x15, 0xa4, 0x8b, 0x63, 0x48, 0x88, - 0x66, 0xdb, 0x80, 0x78, 0xd0, 0x9b, 0x25, 0x62, 0x36, 0x31, 0x91, 0x8c, 0x9e, 0xbc, 0x6c, 0xba, - 0xed, 0xb7, 0x4b, 0xe3, 0x6e, 0xa7, 0x99, 0x19, 0x96, 0xac, 0x37, 0x4f, 0x5e, 0x7d, 0x0d, 0xe3, - 0x23, 0xf8, 0x02, 0x1c, 0x39, 0x7a, 0xda, 0x18, 0x78, 0x83, 0x7d, 0x02, 0x33, 0x9d, 0x29, 0x2c, - 0xbb, 0xd4, 0x70, 0xf0, 0x36, 0xd3, 0xf9, 0xfc, 0x9b, 0x7e, 0x26, 0x5f, 0xb4, 0x9d, 0xb4, 0x42, - 0x2f, 0xc8, 0xb2, 0x4e, 0x12, 0x06, 0x32, 0x61, 0xa9, 0xf0, 0x62, 0x00, 0xaf, 0xb7, 0xeb, 0xb5, - 0x21, 0x05, 0x91, 0x08, 0x37, 0xe3, 0x4c, 0x32, 0xbc, 0x96, 0xb4, 0x42, 0x77, 0x14, 0xe6, 0xc6, - 0x00, 0x6e, 0x6f, 0x77, 0x7d, 0xa5, 0xcd, 0xda, 0x2c, 0xc7, 0x78, 0x6a, 0xa5, 0xe1, 0xeb, 0x8f, - 0xca, 0x54, 0x15, 0x6b, 0x04, 0x12, 0x32, 0x0e, 0x5e, 0x78, 0x1c, 0xa4, 0x29, 0x74, 0xd4, 0xb1, - 0x59, 0x6a, 0x08, 0xf9, 0x31, 0x83, 0xee, 0xbd, 0xd1, 0x31, 0xde, 0xcb, 0x40, 0x02, 0xee, 0xa1, - 0xa5, 0x24, 0x82, 0x54, 0x26, 0x71, 0x02, 0x51, 0x33, 0x06, 0x10, 0xb6, 0xb5, 0x35, 0xbd, 0xb3, - 0xb0, 0x57, 0x77, 0x4b, 0xf2, 0xb9, 0x8d, 0x2b, 0xfc, 0x51, 0x10, 0x7e, 0x02, 0x79, 0x08, 0x20, - 0x7c, 0xe7, 0x6c, 0x50, 0xab, 0x0c, 0x07, 0xb5, 0xfb, 0xfd, 0xa0, 0xdb, 0x79, 0x49, 0xc6, 0x34, - 0x09, 0x5d, 0xbc, 0xfe, 0xa2, 0xf0, 0xf8, 0x8b, 0x85, 0x56, 0x62, 0x80, 0x26, 0xa4, 0x41, 0xab, - 0x03, 0x51, 0xd3, 0xc4, 0x14, 0xf6, 0x54, 0xee, 0xfe, 0xa4, 0xd4, 0xfd, 0x10, 0xe0, 0xb5, 0xe6, - 0x1c, 0x68, 0x8a, 0xff, 0xd8, 0x58, 0x6f, 0x68, 0xeb, 0xdb, 0x54, 0x09, 0xc5, 0xf1, 0x38, 0x4f, - 0xe0, 0x53, 0x54, 0xe5, 0xd0, 0x4e, 0x84, 0x04, 0x0e, 0x51, 0x33, 0x0b, 0xfa, 0xea, 0xf6, 0xd3, - 0xb9, 0xff, 0x4e, 0xa9, 0x3f, 0xbd, 0x62, 0x1c, 0x29, 0x82, 0xbf, 0x65, 0xdc, 0x6d, 0xed, 0x3e, - 0x21, 0x48, 0xe8, 0x32, 0xbf, 0x49, 0x11, 0xf8, 0xbb, 0x85, 0x9c, 0x11, 0x60, 0xc8, 0x4e, 0x52, - 0x09, 0x3c, 0x0b, 0xb8, 0xec, 0x17, 0x31, 0xfe, 0xcb, 0x63, 0xec, 0xdf, 0x21, 0xc6, 0xc1, 0x08, - 0x5b, 0x47, 0xaa, 0x9b, 0x48, 0xdb, 0x13, 0x91, 0x6e, 0x71, 0x22, 0xf4, 0x21, 0x2f, 0xd7, 0x12, - 0xf8, 0x33, 0x5a, 0x8e, 0x19, 0x3f, 0x0d, 0x78, 0xd4, 0xe4, 0xd0, 0x09, 0xfa, 0xc0, 0x85, 0x3d, - 0x93, 0x87, 0x73, 0xcb, 0x3b, 0xd2, 0x04, 0xaa, 0xf1, 0xaf, 0xa2, 0x88, 0x83, 0x10, 0x7e, 0xcd, - 0xc4, 0x5a, 0x33, 0x3d, 0x8d, 0xa9, 0x12, 0xba, 0x14, 0xdf, 0xe0, 0x09, 0xd2, 0x43, 0xd5, 0x89, - 0xba, 0xf1, 0x53, 0x34, 0x97, 0x31, 0x2e, 0x9b, 0x49, 0x64, 0x5b, 0x5b, 0xd6, 0xce, 0xbc, 0x8f, - 0x87, 0x83, 0xda, 0xa2, 0xd6, 0x34, 0x07, 0x84, 0xce, 0xaa, 0x55, 0x23, 0xc2, 0xfb, 0x08, 0x99, - 0x37, 0xa0, 0xf0, 0x53, 0x39, 0x7e, 0x75, 0x38, 0xa8, 0x55, 0x35, 0xfe, 0xfa, 0x8c, 0xd0, 0x79, - 0xb3, 0x69, 0x44, 0xe4, 0x14, 0x2d, 0x8d, 0xd5, 0x3c, 0x26, 0x64, 0xdd, 0x4d, 0x08, 0xdb, 0x68, - 0xce, 0x5c, 0x4f, 0x7b, 0xd3, 0x62, 0x8b, 0x57, 0xd0, 0x4c, 0xfe, 0xff, 0xed, 0xe9, 0xfc, 0xbb, - 0xde, 0x90, 0x9f, 0x16, 0xda, 0xf8, 0x4b, 0xb3, 0xff, 0x3c, 0xc5, 0x5b, 0x84, 0x27, 0x9f, 0x84, - 0x8e, 0xe4, 0x6f, 0x0e, 0x07, 0xb5, 0x07, 0x46, 0x77, 0x02, 0x43, 0x68, 0x35, 0x1c, 0x4f, 0x47, - 0xbe, 0x5a, 0x68, 0xf5, 0xd6, 0xea, 0x55, 0x82, 0x40, 0x2f, 0x75, 0x68, 0x5a, 0x6c, 0xf1, 0x07, - 0x34, 0x9f, 0xe5, 0x53, 0xa4, 0xe8, 0x67, 0x61, 0x6f, 0x33, 0x7f, 0x57, 0x6a, 0x8e, 0xb9, 0xc5, - 0xf0, 0xea, 0xed, 0xba, 0x7a, 0xd6, 0x34, 0x22, 0xdf, 0x36, 0xcf, 0x68, 0xd9, 0x54, 0x5e, 0xb0, - 0x09, 0xfd, 0x3f, 0x2b, 0x30, 0xef, 0xce, 0x2e, 0x1c, 0xeb, 0xfc, 0xc2, 0xb1, 0x7e, 0x5f, 0x38, - 0xd6, 0xb7, 0x4b, 0xa7, 0x72, 0x7e, 0xe9, 0x54, 0x7e, 0x5d, 0x3a, 0x95, 0x8f, 0xcf, 0xdb, 0x89, - 0x3c, 0x3e, 0x69, 0xb9, 0x21, 0xeb, 0x7a, 0x21, 0x13, 0x5d, 0x26, 0xbc, 0xa4, 0x15, 0xd6, 0xdb, - 0xcc, 0xeb, 0xed, 0x7b, 0x5d, 0x16, 0x9d, 0x74, 0x40, 0xa8, 0x31, 0x2b, 0xbc, 0xbd, 0x17, 0x75, - 0x35, 0x61, 0x65, 0x3f, 0x03, 0xd1, 0x9a, 0xcd, 0xc7, 0xe7, 0xb3, 0x3f, 0x01, 0x00, 0x00, 0xff, - 0xff, 0x3c, 0x18, 0x93, 0x1b, 0xdc, 0x05, 0x00, 0x00, + // 648 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x54, 0xcd, 0x4e, 0xd4, 0x40, + 0x1c, 0xdf, 0x82, 0x80, 0x0c, 0x06, 0xd8, 0x09, 0x48, 0x05, 0xe9, 0xe2, 0x18, 0x12, 0xa2, 0xd9, + 0x36, 0x20, 0xc6, 0xe8, 0xcd, 0x12, 0x31, 0x9b, 0x98, 0x48, 0x46, 0x4f, 0x5e, 0x36, 0xdd, 0xf6, + 0xdf, 0xa5, 0x71, 0xb7, 0xd3, 0xcc, 0x0c, 0x4b, 0xd6, 0x9b, 0x27, 0xaf, 0xbe, 0x86, 0xf1, 0x11, + 0x7c, 0x01, 0x8e, 0x1c, 0x3d, 0x6d, 0x0c, 0xbc, 0xc1, 0x3e, 0x81, 0x99, 0xce, 0x14, 0x96, 0xfd, + 0x30, 0x1c, 0xbc, 0xcd, 0x74, 0x7e, 0x5f, 0xd3, 0xdf, 0xe4, 0x8f, 0xb6, 0x93, 0x46, 0xe8, 0x05, + 0x59, 0xd6, 0x4a, 0xc2, 0x40, 0x26, 0x2c, 0x15, 0x5e, 0x0c, 0xe0, 0x75, 0x76, 0xbd, 0x26, 0xa4, + 0x20, 0x12, 0xe1, 0x66, 0x9c, 0x49, 0x86, 0xd7, 0x92, 0x46, 0xe8, 0x0e, 0xc2, 0xdc, 0x18, 0xc0, + 0xed, 0xec, 0xae, 0xaf, 0x34, 0x59, 0x93, 0xe5, 0x18, 0x4f, 0xad, 0x34, 0x7c, 0xfd, 0xd1, 0x24, + 0x55, 0xc5, 0x1a, 0x80, 0x84, 0x8c, 0x83, 0x17, 0x1e, 0x07, 0x69, 0x0a, 0x2d, 0x75, 0x6c, 0x96, + 0x1a, 0x42, 0x7e, 0xce, 0xa0, 0x7b, 0x6f, 0x75, 0x8c, 0x0f, 0x32, 0x90, 0x80, 0x3b, 0x68, 0x29, + 0x89, 0x20, 0x95, 0x49, 0x9c, 0x40, 0x54, 0x8f, 0x01, 0x84, 0x6d, 0x6d, 0x4d, 0xef, 0x2c, 0xec, + 0x55, 0xdd, 0x09, 0xf9, 0xdc, 0xda, 0x15, 0xfe, 0x28, 0x08, 0x3f, 0x83, 0x3c, 0x04, 0x10, 0xbe, + 0x73, 0xd6, 0xab, 0x94, 0xfa, 0xbd, 0xca, 0xfd, 0x6e, 0xd0, 0x6e, 0xbd, 0x22, 0x43, 0x9a, 0x84, + 0x2e, 0x5e, 0x7f, 0x51, 0x78, 0xfc, 0xd5, 0x42, 0x2b, 0x31, 0x40, 0x1d, 0xd2, 0xa0, 0xd1, 0x82, + 0xa8, 0x6e, 0x62, 0x0a, 0x7b, 0x2a, 0x77, 0x7f, 0x32, 0xd1, 0xfd, 0x10, 0xe0, 0x8d, 0xe6, 0x1c, + 0x68, 0x8a, 0xff, 0xd8, 0x58, 0x6f, 0x68, 0xeb, 0x71, 0xaa, 0x84, 0xe2, 0x78, 0x98, 0x27, 0xf0, + 0x29, 0x2a, 0x73, 0x68, 0x26, 0x42, 0x02, 0x87, 0xa8, 0x9e, 0x05, 0x5d, 0x75, 0xfb, 0xe9, 0xdc, + 0x7f, 0x67, 0xa2, 0x3f, 0xbd, 0x62, 0x1c, 0x29, 0x82, 0xbf, 0x65, 0xdc, 0x6d, 0xed, 0x3e, 0x22, + 0x48, 0xe8, 0x32, 0xbf, 0x49, 0x11, 0xf8, 0x87, 0x85, 0x9c, 0x01, 0x60, 0xc8, 0x4e, 0x52, 0x09, + 0x3c, 0x0b, 0xb8, 0xec, 0x16, 0x31, 0xee, 0xe4, 0x31, 0xf6, 0x6f, 0x11, 0xe3, 0x60, 0x80, 0xad, + 0x23, 0x55, 0x4d, 0xa4, 0xed, 0x91, 0x48, 0x63, 0x9c, 0x08, 0x7d, 0xc8, 0x27, 0x6b, 0x09, 0xfc, + 0x05, 0x2d, 0xc7, 0x8c, 0x9f, 0x06, 0x3c, 0xaa, 0x73, 0x68, 0x05, 0x5d, 0xe0, 0xc2, 0x9e, 0xc9, + 0xc3, 0xb9, 0x93, 0x3b, 0xd2, 0x04, 0xaa, 0xf1, 0xaf, 0xa3, 0x88, 0x83, 0x10, 0x7e, 0xc5, 0xc4, + 0x5a, 0x33, 0x3d, 0x0d, 0xa9, 0x12, 0xba, 0x14, 0xdf, 0xe0, 0x09, 0xd2, 0x41, 0xe5, 0x91, 0xba, + 0xf1, 0x53, 0x34, 0x97, 0x31, 0x2e, 0xeb, 0x49, 0x64, 0x5b, 0x5b, 0xd6, 0xce, 0xbc, 0x8f, 0xfb, + 0xbd, 0xca, 0xa2, 0xd6, 0x34, 0x07, 0x84, 0xce, 0xaa, 0x55, 0x2d, 0xc2, 0xfb, 0x08, 0x99, 0x37, + 0xa0, 0xf0, 0x53, 0x39, 0x7e, 0xb5, 0xdf, 0xab, 0x94, 0x35, 0xfe, 0xfa, 0x8c, 0xd0, 0x79, 0xb3, + 0xa9, 0x45, 0xe4, 0x14, 0x2d, 0x0d, 0xd5, 0x3c, 0x24, 0x64, 0xdd, 0x4e, 0x08, 0xdb, 0x68, 0xce, + 0x5c, 0x4f, 0x7b, 0xd3, 0x62, 0x8b, 0x57, 0xd0, 0x4c, 0xfe, 0xff, 0xed, 0xe9, 0xfc, 0xbb, 0xde, + 0x90, 0x5f, 0x16, 0xda, 0xf8, 0x47, 0xb3, 0xff, 0x3d, 0xc5, 0x3b, 0x84, 0x47, 0x9f, 0x84, 0x8e, + 0xe4, 0x6f, 0xf6, 0x7b, 0x95, 0x07, 0x46, 0x77, 0x04, 0x43, 0x68, 0x39, 0x1c, 0x4e, 0x47, 0xbe, + 0x59, 0x68, 0x75, 0x6c, 0xf5, 0x2a, 0x41, 0xa0, 0x97, 0x3a, 0x34, 0x2d, 0xb6, 0xf8, 0x23, 0x9a, + 0xcf, 0xf2, 0x29, 0x52, 0xf4, 0xb3, 0xb0, 0xb7, 0x99, 0xbf, 0x2b, 0x35, 0xc7, 0xdc, 0x62, 0x78, + 0x75, 0x76, 0x5d, 0x3d, 0x6b, 0x6a, 0x91, 0x6f, 0x9b, 0x67, 0xb4, 0x6c, 0x2a, 0x2f, 0xd8, 0x84, + 0xde, 0xcd, 0x0a, 0xcc, 0xfb, 0xb3, 0x0b, 0xc7, 0x3a, 0xbf, 0x70, 0xac, 0x3f, 0x17, 0x8e, 0xf5, + 0xfd, 0xd2, 0x29, 0x9d, 0x5f, 0x3a, 0xa5, 0xdf, 0x97, 0x4e, 0xe9, 0xd3, 0xf3, 0x66, 0x22, 0x8f, + 0x4f, 0x1a, 0x6e, 0xc8, 0xda, 0x5e, 0xc8, 0x44, 0x9b, 0x09, 0x2f, 0x69, 0x84, 0xd5, 0x26, 0xf3, + 0x3a, 0x2f, 0xbc, 0x36, 0x8b, 0x4e, 0x5a, 0x20, 0xd4, 0x98, 0x15, 0xde, 0xde, 0xcb, 0xaa, 0x9a, + 0xb0, 0xb2, 0x9b, 0x81, 0x68, 0xcc, 0xe6, 0xe3, 0xf3, 0xd9, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x25, 0x26, 0x40, 0x90, 0xdc, 0x05, 0x00, 0x00, } func (m *GenesisState) Marshal() (dAtA []byte, err error) { diff --git a/modules/apps/29-fee/types/genesis_test.go b/modules/apps/29-fee/types/genesis_test.go index bbec115baf7..cdf217f3afd 100644 --- a/modules/apps/29-fee/types/genesis_test.go +++ b/modules/apps/29-fee/types/genesis_test.go @@ -7,9 +7,9 @@ import ( "github.com/stretchr/testify/require" "github.com/tendermint/tendermint/crypto/secp256k1" - "github.com/cosmos/ibc-go/v4/modules/apps/29-fee/types" - channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" - ibctesting "github.com/cosmos/ibc-go/v4/testing" + "github.com/cosmos/ibc-go/v7/modules/apps/29-fee/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + ibctesting "github.com/cosmos/ibc-go/v7/testing" ) func TestValidateDefaultGenesis(t *testing.T) { @@ -33,21 +33,21 @@ func TestValidateGenesis(t *testing.T) { { "invalid packetID: invalid port ID", func() { - genState.IdentifiedFees[0].PacketId = channeltypes.NewPacketId("", ibctesting.FirstChannelID, 1) + genState.IdentifiedFees[0].PacketId = channeltypes.NewPacketID("", ibctesting.FirstChannelID, 1) }, false, }, { "invalid packetID: invalid channel ID", func() { - genState.IdentifiedFees[0].PacketId = channeltypes.NewPacketId(ibctesting.MockFeePort, "", 1) + genState.IdentifiedFees[0].PacketId = channeltypes.NewPacketID(ibctesting.MockFeePort, "", 1) }, false, }, { "invalid packetID: invalid sequence", func() { - genState.IdentifiedFees[0].PacketId = channeltypes.NewPacketId(ibctesting.MockFeePort, ibctesting.FirstChannelID, 0) + genState.IdentifiedFees[0].PacketId = channeltypes.NewPacketID(ibctesting.MockFeePort, ibctesting.FirstChannelID, 0) }, false, }, @@ -134,7 +134,7 @@ func TestValidateGenesis(t *testing.T) { genState = &types.GenesisState{ IdentifiedFees: []types.IdentifiedPacketFees{ { - PacketId: channeltypes.NewPacketId(ibctesting.MockFeePort, ibctesting.FirstChannelID, 1), + PacketId: channeltypes.NewPacketID(ibctesting.MockFeePort, ibctesting.FirstChannelID, 1), PacketFees: []types.PacketFee{types.NewPacketFee(types.NewFee(defaultRecvFee, defaultAckFee, defaultTimeoutFee), defaultAccAddress, nil)}, }, }, @@ -154,7 +154,7 @@ func TestValidateGenesis(t *testing.T) { ForwardRelayers: []types.ForwardRelayerAddress{ { Address: defaultAccAddress, - PacketId: channeltypes.NewPacketId(ibctesting.MockFeePort, ibctesting.FirstChannelID, 1), + PacketId: channeltypes.NewPacketID(ibctesting.MockFeePort, ibctesting.FirstChannelID, 1), }, }, RegisteredPayees: []types.RegisteredPayee{ diff --git a/modules/apps/29-fee/types/keys.go b/modules/apps/29-fee/types/keys.go index c547432400b..823fe951071 100644 --- a/modules/apps/29-fee/types/keys.go +++ b/modules/apps/29-fee/types/keys.go @@ -5,9 +5,10 @@ import ( "strconv" "strings" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + errorsmod "cosmossdk.io/errors" - channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" + ibcerrors "github.com/cosmos/ibc-go/v7/internal/errors" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" ) const ( @@ -58,13 +59,13 @@ func KeyFeeEnabled(portID, channelID string) []byte { func ParseKeyFeeEnabled(key string) (portID, channelID string, err error) { keySplit := strings.Split(key, "/") if len(keySplit) != 3 { - return "", "", sdkerrors.Wrapf( - sdkerrors.ErrLogic, "key provided is incorrect: the key split has incorrect length, expected %d, got %d", 3, len(keySplit), + return "", "", errorsmod.Wrapf( + ibcerrors.ErrLogic, "key provided is incorrect: the key split has incorrect length, expected %d, got %d", 3, len(keySplit), ) } if keySplit[0] != FeeEnabledKeyPrefix { - return "", "", sdkerrors.Wrapf(sdkerrors.ErrLogic, "key prefix is incorrect: expected %s, got %s", FeeEnabledKeyPrefix, keySplit[0]) + return "", "", errorsmod.Wrapf(ibcerrors.ErrLogic, "key prefix is incorrect: expected %s, got %s", FeeEnabledKeyPrefix, keySplit[0]) } portID = keySplit[1] @@ -82,8 +83,8 @@ func KeyPayee(relayerAddr, channelID string) []byte { func ParseKeyPayeeAddress(key string) (relayerAddr, channelID string, err error) { keySplit := strings.Split(key, "/") if len(keySplit) != 3 { - return "", "", sdkerrors.Wrapf( - sdkerrors.ErrLogic, "key provided is incorrect: the key split has incorrect length, expected %d, got %d", 3, len(keySplit), + return "", "", errorsmod.Wrapf( + ibcerrors.ErrLogic, "key provided is incorrect: the key split has incorrect length, expected %d, got %d", 3, len(keySplit), ) } @@ -99,8 +100,8 @@ func KeyCounterpartyPayee(address, channelID string) []byte { func ParseKeyCounterpartyPayee(key string) (address string, channelID string, error error) { keySplit := strings.Split(key, "/") if len(keySplit) != 3 { - return "", "", sdkerrors.Wrapf( - sdkerrors.ErrLogic, "key provided is incorrect: the key split has incorrect length, expected %d, got %d", 3, len(keySplit), + return "", "", errorsmod.Wrapf( + ibcerrors.ErrLogic, "key provided is incorrect: the key split has incorrect length, expected %d, got %d", 3, len(keySplit), ) } @@ -116,8 +117,8 @@ func KeyRelayerAddressForAsyncAck(packetID channeltypes.PacketId) []byte { func ParseKeyRelayerAddressForAsyncAck(key string) (channeltypes.PacketId, error) { keySplit := strings.Split(key, "/") if len(keySplit) != 4 { - return channeltypes.PacketId{}, sdkerrors.Wrapf( - sdkerrors.ErrLogic, "key provided is incorrect: the key split has incorrect length, expected %d, got %d", 4, len(keySplit), + return channeltypes.PacketId{}, errorsmod.Wrapf( + ibcerrors.ErrLogic, "key provided is incorrect: the key split has incorrect length, expected %d, got %d", 4, len(keySplit), ) } @@ -126,7 +127,7 @@ func ParseKeyRelayerAddressForAsyncAck(key string) (channeltypes.PacketId, error return channeltypes.PacketId{}, err } - packetID := channeltypes.NewPacketId(keySplit[1], keySplit[2], seq) + packetID := channeltypes.NewPacketID(keySplit[1], keySplit[2], seq) return packetID, nil } @@ -139,8 +140,8 @@ func KeyFeesInEscrow(packetID channeltypes.PacketId) []byte { func ParseKeyFeesInEscrow(key string) (channeltypes.PacketId, error) { keySplit := strings.Split(key, "/") if len(keySplit) != 4 { - return channeltypes.PacketId{}, sdkerrors.Wrapf( - sdkerrors.ErrLogic, "key provided is incorrect: the key split has incorrect length, expected %d, got %d", 4, len(keySplit), + return channeltypes.PacketId{}, errorsmod.Wrapf( + ibcerrors.ErrLogic, "key provided is incorrect: the key split has incorrect length, expected %d, got %d", 4, len(keySplit), ) } @@ -149,7 +150,7 @@ func ParseKeyFeesInEscrow(key string) (channeltypes.PacketId, error) { return channeltypes.PacketId{}, err } - packetID := channeltypes.NewPacketId(keySplit[1], keySplit[2], seq) + packetID := channeltypes.NewPacketID(keySplit[1], keySplit[2], seq) return packetID, nil } diff --git a/modules/apps/29-fee/types/keys_test.go b/modules/apps/29-fee/types/keys_test.go index 26c9158fd7f..d9965c4bf98 100644 --- a/modules/apps/29-fee/types/keys_test.go +++ b/modules/apps/29-fee/types/keys_test.go @@ -6,12 +6,12 @@ import ( "github.com/stretchr/testify/require" - "github.com/cosmos/ibc-go/v4/modules/apps/29-fee/types" - channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" - ibctesting "github.com/cosmos/ibc-go/v4/testing" + "github.com/cosmos/ibc-go/v7/modules/apps/29-fee/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + ibctesting "github.com/cosmos/ibc-go/v7/testing" ) -var validPacketID = channeltypes.NewPacketId(ibctesting.MockFeePort, ibctesting.FirstChannelID, 1) +var validPacketID = channeltypes.NewPacketID(ibctesting.MockFeePort, ibctesting.FirstChannelID, 1) func TestKeyPayee(t *testing.T) { key := types.KeyPayee("relayer-address", ibctesting.FirstChannelID) diff --git a/modules/apps/29-fee/types/metadata.pb.go b/modules/apps/29-fee/types/metadata.pb.go index 5715cc7e676..5d89eaa8719 100644 --- a/modules/apps/29-fee/types/metadata.pb.go +++ b/modules/apps/29-fee/types/metadata.pb.go @@ -5,8 +5,8 @@ package types import ( fmt "fmt" - _ "github.com/gogo/protobuf/gogoproto" - proto "github.com/gogo/protobuf/proto" + _ "github.com/cosmos/gogoproto/gogoproto" + proto "github.com/cosmos/gogoproto/proto" io "io" math "math" math_bits "math/bits" @@ -101,10 +101,10 @@ var fileDescriptor_03d0f000eda681ce = []byte{ 0x95, 0x58, 0x50, 0x00, 0xd5, 0xe8, 0xe4, 0x7f, 0xe2, 0x91, 0x1c, 0xe3, 0x85, 0x47, 0x72, 0x8c, 0x0f, 0x1e, 0xc9, 0x31, 0x4e, 0x78, 0x2c, 0xc7, 0x70, 0xe1, 0xb1, 0x1c, 0xc3, 0x8d, 0xc7, 0x72, 0x0c, 0x51, 0xa6, 0xe9, 0x99, 0x25, 0x19, 0xa5, 0x49, 0x7a, 0xc9, 0xf9, 0xb9, 0xfa, 0xc9, 0xf9, - 0xc5, 0xb9, 0xf9, 0xc5, 0xfa, 0x99, 0x49, 0xc9, 0xba, 0xe9, 0xf9, 0xfa, 0x65, 0x26, 0xfa, 0xb9, + 0xc5, 0xb9, 0xf9, 0xc5, 0xfa, 0x99, 0x49, 0xc9, 0xba, 0xe9, 0xf9, 0xfa, 0x65, 0xe6, 0xfa, 0xb9, 0xf9, 0x29, 0xa5, 0x39, 0xa9, 0xc5, 0xa0, 0xe0, 0x28, 0xd6, 0x37, 0xb2, 0xd4, 0x05, 0x85, 0x44, 0x49, 0x65, 0x41, 0x6a, 0x71, 0x12, 0x1b, 0xd8, 0x57, 0xc6, 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, - 0xbf, 0xe9, 0x11, 0xe3, 0x2e, 0x01, 0x00, 0x00, + 0xa6, 0xd7, 0xc2, 0x68, 0x2e, 0x01, 0x00, 0x00, } func (m *Metadata) Marshal() (dAtA []byte, err error) { diff --git a/modules/apps/29-fee/types/msgs.go b/modules/apps/29-fee/types/msgs.go index 4b0fd331c9d..0828beba868 100644 --- a/modules/apps/29-fee/types/msgs.go +++ b/modules/apps/29-fee/types/msgs.go @@ -3,17 +3,12 @@ package types import ( "strings" + errorsmod "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" -) - -// msg types -const ( - TypeMsgPayPacketFee = "payPacketFee" - TypeMsgPayPacketFeeAsync = "payPacketFeeAsync" + ibcerrors "github.com/cosmos/ibc-go/v7/internal/errors" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" ) // NewMsgRegisterPayee creates a new instance of MsgRegisterPayee @@ -37,17 +32,17 @@ func (msg MsgRegisterPayee) ValidateBasic() error { } if msg.Relayer == msg.Payee { - return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "relayer address and payee must not be equal") + return errorsmod.Wrap(ibcerrors.ErrInvalidRequest, "relayer address and payee must not be equal") } _, err := sdk.AccAddressFromBech32(msg.Relayer) if err != nil { - return sdkerrors.Wrap(err, "failed to create sdk.AccAddress from relayer address") + return errorsmod.Wrap(err, "failed to create sdk.AccAddress from relayer address") } _, err = sdk.AccAddressFromBech32(msg.Payee) if err != nil { - return sdkerrors.Wrap(err, "failed to create sdk.AccAddress from payee address") + return errorsmod.Wrap(err, "failed to create sdk.AccAddress from payee address") } return nil @@ -85,7 +80,7 @@ func (msg MsgRegisterCounterpartyPayee) ValidateBasic() error { _, err := sdk.AccAddressFromBech32(msg.Relayer) if err != nil { - return sdkerrors.Wrap(err, "failed to create sdk.AccAddress from relayer address") + return errorsmod.Wrap(err, "failed to create sdk.AccAddress from relayer address") } if strings.TrimSpace(msg.CounterpartyPayee) == "" { @@ -106,11 +101,11 @@ func (msg MsgRegisterCounterpartyPayee) GetSigners() []sdk.AccAddress { } // NewMsgPayPacketFee creates a new instance of MsgPayPacketFee -func NewMsgPayPacketFee(fee Fee, sourcePortId, sourceChannelId, signer string, relayers []string) *MsgPayPacketFee { +func NewMsgPayPacketFee(fee Fee, sourcePortID, sourceChannelID, signer string, relayers []string) *MsgPayPacketFee { return &MsgPayPacketFee{ Fee: fee, - SourcePortId: sourcePortId, - SourceChannelId: sourceChannelId, + SourcePortId: sourcePortID, + SourceChannelId: sourceChannelID, Signer: signer, Relayers: relayers, } @@ -130,12 +125,12 @@ func (msg MsgPayPacketFee) ValidateBasic() error { // signer check if _, err := sdk.AccAddressFromBech32(msg.Signer); err != nil { - return sdkerrors.Wrap(err, "failed to convert msg.Signer into sdk.AccAddress") + return errorsmod.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 { @@ -159,11 +154,6 @@ func (msg MsgPayPacketFee) Route() string { return RouterKey } -// Type implements sdk.Msg -func (msg MsgPayPacketFee) Type() string { - return TypeMsgPayPacketFee -} - // GetSignBytes implements sdk.Msg. func (msg MsgPayPacketFee) GetSignBytes() []byte { return sdk.MustSortJSON(AminoCdc.MustMarshalJSON(&msg)) @@ -205,11 +195,6 @@ func (msg MsgPayPacketFeeAsync) Route() string { return RouterKey } -// Type implements sdk.Msg -func (msg MsgPayPacketFeeAsync) Type() string { - return TypeMsgPayPacketFeeAsync -} - // GetSignBytes implements sdk.Msg. func (msg MsgPayPacketFeeAsync) GetSignBytes() []byte { return sdk.MustSortJSON(AminoCdc.MustMarshalJSON(&msg)) diff --git a/modules/apps/29-fee/types/msgs_test.go b/modules/apps/29-fee/types/msgs_test.go index 93573e354a8..2bba74bff42 100644 --- a/modules/apps/29-fee/types/msgs_test.go +++ b/modules/apps/29-fee/types/msgs_test.go @@ -4,13 +4,12 @@ import ( "testing" sdk "github.com/cosmos/cosmos-sdk/types" - - "github.com/cosmos/ibc-go/v4/modules/apps/29-fee/types" - channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" - ibctesting "github.com/cosmos/ibc-go/v4/testing" - "github.com/stretchr/testify/require" "github.com/tendermint/tendermint/crypto/secp256k1" + + "github.com/cosmos/ibc-go/v7/modules/apps/29-fee/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + ibctesting "github.com/cosmos/ibc-go/v7/testing" ) func TestMsgRegisterPayeeValidation(t *testing.T) { @@ -51,14 +50,14 @@ func TestMsgRegisterPayeeValidation(t *testing.T) { { "invalid relayer address", func() { - msg.Relayer = "invalid-address" + msg.Relayer = invalidAddress }, false, }, { "invalid payee address", func() { - msg.Payee = "invalid-address" + msg.Payee = invalidAddress }, false, }, @@ -85,7 +84,7 @@ func TestMsgRegisterPayeeValidation(t *testing.T) { func TestRegisterPayeeGetSigners(t *testing.T) { accAddress := sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()) msg := types.NewMsgRegisterPayee(ibctesting.MockPort, ibctesting.FirstChannelID, accAddress.String(), defaultAccAddress) - require.Equal(t, []sdk.AccAddress{sdk.AccAddress(accAddress)}, msg.GetSigners()) + require.Equal(t, []sdk.AccAddress{accAddress}, msg.GetSigners()) } func TestMsgRegisterCountepartyPayeeValidation(t *testing.T) { @@ -118,7 +117,7 @@ func TestMsgRegisterCountepartyPayeeValidation(t *testing.T) { { "validate with incorrect destination relayer address", func() { - msg.Relayer = "invalid-address" + msg.Relayer = invalidAddress }, false, }, @@ -156,7 +155,7 @@ func TestMsgRegisterCountepartyPayeeValidation(t *testing.T) { func TestRegisterCountepartyAddressGetSigners(t *testing.T) { accAddress := sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()) msg := types.NewMsgRegisterCounterpartyPayee(ibctesting.MockPort, ibctesting.FirstChannelID, accAddress.String(), defaultAccAddress) - require.Equal(t, []sdk.AccAddress{sdk.AccAddress(accAddress)}, msg.GetSigners()) + require.Equal(t, []sdk.AccAddress{accAddress}, msg.GetSigners()) } func TestMsgPayPacketFeeValidation(t *testing.T) { @@ -172,6 +171,13 @@ func TestMsgPayPacketFeeValidation(t *testing.T) { func() {}, true, }, + { + "success with empty relayers", + func() { + msg.Relayers = []string{} + }, + true, + }, { "invalid channelID", func() { @@ -196,7 +202,7 @@ func TestMsgPayPacketFeeValidation(t *testing.T) { { "invalid signer address", func() { - msg.Signer = "invalid-address" + msg.Signer = invalidAddress }, false, }, @@ -211,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) } } } @@ -231,11 +237,6 @@ func TestMsgPayPacketFeeRoute(t *testing.T) { require.Equal(t, types.RouterKey, msg.Route()) } -func TestMsgPayPacketFeeType(t *testing.T) { - var msg types.MsgPayPacketFee - require.Equal(t, "payPacketFee", msg.Type()) -} - func TestMsgPayPacketFeeGetSignBytes(t *testing.T) { fee := types.NewFee(defaultRecvFee, defaultAckFee, defaultTimeoutFee) msg := types.NewMsgPayPacketFee(fee, ibctesting.MockFeePort, ibctesting.FirstChannelID, defaultAccAddress, nil) @@ -258,6 +259,13 @@ func TestMsgPayPacketFeeAsyncValidation(t *testing.T) { func() {}, true, }, + { + "success with empty relayers", + func() { + msg.PacketFee.Relayers = []string{} + }, + true, + }, { "invalid channelID", func() { @@ -344,7 +352,7 @@ func TestMsgPayPacketFeeAsyncValidation(t *testing.T) { } for _, tc := range testCases { - packetID := channeltypes.NewPacketId(ibctesting.MockFeePort, ibctesting.FirstChannelID, 1) + packetID := channeltypes.NewPacketID(ibctesting.MockFeePort, ibctesting.FirstChannelID, 1) fee := types.NewFee(defaultRecvFee, defaultAckFee, defaultTimeoutFee) packetFee := types.NewPacketFee(fee, defaultAccAddress, nil) @@ -355,16 +363,16 @@ 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) } } } func TestPayPacketFeeAsyncGetSigners(t *testing.T) { refundAddr := sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()) - packetID := channeltypes.NewPacketId(ibctesting.MockFeePort, ibctesting.FirstChannelID, 1) + packetID := channeltypes.NewPacketID(ibctesting.MockFeePort, ibctesting.FirstChannelID, 1) fee := types.NewFee(defaultRecvFee, defaultAckFee, defaultTimeoutFee) packetFee := types.NewPacketFee(fee, refundAddr.String(), nil) @@ -378,13 +386,8 @@ func TestMsgPayPacketFeeAsyncRoute(t *testing.T) { require.Equal(t, types.RouterKey, msg.Route()) } -func TestMsgPayPacketFeeAsyncType(t *testing.T) { - var msg types.MsgPayPacketFeeAsync - require.Equal(t, "payPacketFeeAsync", msg.Type()) -} - func TestMsgPayPacketFeeAsyncGetSignBytes(t *testing.T) { - packetID := channeltypes.NewPacketId(ibctesting.MockFeePort, ibctesting.FirstChannelID, 1) + packetID := channeltypes.NewPacketID(ibctesting.MockFeePort, ibctesting.FirstChannelID, 1) fee := types.NewFee(defaultRecvFee, defaultAckFee, defaultTimeoutFee) packetFee := types.NewPacketFee(fee, defaultAccAddress, nil) diff --git a/modules/apps/29-fee/types/query.pb.go b/modules/apps/29-fee/types/query.pb.go index 87a5f18f894..379d32bca12 100644 --- a/modules/apps/29-fee/types/query.pb.go +++ b/modules/apps/29-fee/types/query.pb.go @@ -9,10 +9,10 @@ import ( github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" types1 "github.com/cosmos/cosmos-sdk/types" query "github.com/cosmos/cosmos-sdk/types/query" - types "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" - _ "github.com/gogo/protobuf/gogoproto" - grpc1 "github.com/gogo/protobuf/grpc" - proto "github.com/gogo/protobuf/proto" + _ "github.com/cosmos/gogoproto/gogoproto" + grpc1 "github.com/cosmos/gogoproto/grpc" + proto "github.com/cosmos/gogoproto/proto" + types "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" _ "google.golang.org/genproto/googleapis/api/annotations" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" @@ -92,6 +92,8 @@ func (m *QueryIncentivizedPacketsRequest) GetQueryHeight() uint64 { type QueryIncentivizedPacketsResponse struct { // list of identified fees for incentivized packets IncentivizedPackets []IdentifiedPacketFees `protobuf:"bytes,1,rep,name=incentivized_packets,json=incentivizedPackets,proto3" json:"incentivized_packets"` + // pagination defines the pagination in the response. + Pagination *query.PageResponse `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` } func (m *QueryIncentivizedPacketsResponse) Reset() { *m = QueryIncentivizedPacketsResponse{} } @@ -134,6 +136,13 @@ func (m *QueryIncentivizedPacketsResponse) GetIncentivizedPackets() []Identified return nil } +func (m *QueryIncentivizedPacketsResponse) GetPagination() *query.PageResponse { + if m != nil { + return m.Pagination + } + return nil +} + // QueryIncentivizedPacketRequest defines the request type for the IncentivizedPacket rpc type QueryIncentivizedPacketRequest struct { // unique packet identifier comprised of channel ID, port ID and sequence @@ -315,6 +324,8 @@ func (m *QueryIncentivizedPacketsForChannelRequest) GetQueryHeight() uint64 { type QueryIncentivizedPacketsForChannelResponse struct { // Map of all incentivized_packets IncentivizedPackets []*IdentifiedPacketFees `protobuf:"bytes,1,rep,name=incentivized_packets,json=incentivizedPackets,proto3" json:"incentivized_packets,omitempty"` + // pagination defines the pagination in the response. + Pagination *query.PageResponse `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` } func (m *QueryIncentivizedPacketsForChannelResponse) Reset() { @@ -361,6 +372,13 @@ func (m *QueryIncentivizedPacketsForChannelResponse) GetIncentivizedPackets() [] return nil } +func (m *QueryIncentivizedPacketsForChannelResponse) GetPagination() *query.PageResponse { + if m != nil { + return m.Pagination + } + return nil +} + // QueryTotalRecvFeesRequest defines the request type for the TotalRecvFees rpc type QueryTotalRecvFeesRequest struct { // the packet identifier for the associated fees @@ -898,6 +916,8 @@ func (m *QueryFeeEnabledChannelsRequest) GetQueryHeight() uint64 { type QueryFeeEnabledChannelsResponse struct { // list of fee enabled channels FeeEnabledChannels []FeeEnabledChannel `protobuf:"bytes,1,rep,name=fee_enabled_channels,json=feeEnabledChannels,proto3" json:"fee_enabled_channels" yaml:"fee_enabled_channels"` + // pagination defines the pagination in the response. + Pagination *query.PageResponse `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` } func (m *QueryFeeEnabledChannelsResponse) Reset() { *m = QueryFeeEnabledChannelsResponse{} } @@ -940,6 +960,13 @@ func (m *QueryFeeEnabledChannelsResponse) GetFeeEnabledChannels() []FeeEnabledCh return nil } +func (m *QueryFeeEnabledChannelsResponse) GetPagination() *query.PageResponse { + if m != nil { + return m.Pagination + } + return nil +} + // QueryFeeEnabledChannelRequest defines the request type for the FeeEnabledChannel rpc type QueryFeeEnabledChannelRequest struct { // unique port identifier @@ -1069,91 +1096,92 @@ func init() { } var fileDescriptor_0638a8a78ca2503c = []byte{ - // 1340 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x58, 0x5f, 0x6f, 0xdb, 0xd4, - 0x1b, 0xee, 0xe9, 0xba, 0xb5, 0x3d, 0xed, 0x7e, 0xbf, 0xe5, 0xb4, 0xb0, 0x34, 0xb4, 0x49, 0xe7, - 0x31, 0x28, 0x9d, 0x6a, 0xab, 0xd9, 0x46, 0x37, 0x24, 0x04, 0x4d, 0x47, 0x47, 0x61, 0x40, 0xc9, - 0x7a, 0x03, 0x02, 0x65, 0x8e, 0x73, 0x92, 0x5a, 0x4d, 0x7d, 0x3c, 0xdb, 0x89, 0xc8, 0xba, 0x02, - 0x9b, 0xa8, 0x40, 0x80, 0x00, 0x09, 0x89, 0x0b, 0xee, 0x11, 0x02, 0x89, 0x0f, 0xc0, 0x37, 0xd8, - 0x15, 0xaa, 0xc4, 0x0d, 0xe2, 0x22, 0xa0, 0x16, 0xf1, 0x01, 0x72, 0xc5, 0x05, 0x48, 0xc8, 0xe7, - 0xbc, 0x4e, 0x9c, 0xd9, 0x6e, 0x93, 0x52, 0xca, 0x55, 0x6d, 0x9f, 0xf7, 0xcf, 0xf3, 0x3c, 0xe7, - 0xb5, 0xcf, 0x93, 0xe2, 0xb3, 0x7a, 0x5e, 0x53, 0x54, 0xd3, 0x2c, 0xeb, 0x9a, 0xea, 0xe8, 0xcc, - 0xb0, 0x95, 0x22, 0xa5, 0x4a, 0x75, 0x56, 0xb9, 0x55, 0xa1, 0x56, 0x4d, 0x36, 0x2d, 0xe6, 0x30, - 0x72, 0x5a, 0xcf, 0x6b, 0xb2, 0x3f, 0x48, 0x2e, 0x52, 0x2a, 0x57, 0x67, 0x13, 0xa3, 0x25, 0x56, - 0x62, 0x3c, 0x46, 0x71, 0xaf, 0x44, 0x78, 0x62, 0xbc, 0xc4, 0x58, 0xa9, 0x4c, 0x15, 0xd5, 0xd4, - 0x15, 0xd5, 0x30, 0x98, 0x03, 0x49, 0x62, 0x35, 0xa9, 0x31, 0x7b, 0x9d, 0xd9, 0x4a, 0x5e, 0xb5, - 0xdd, 0x46, 0x79, 0xea, 0xa8, 0xb3, 0x8a, 0xc6, 0x74, 0x03, 0xd6, 0xa7, 0xfd, 0xeb, 0x1c, 0x45, - 0x33, 0xca, 0x54, 0x4b, 0xba, 0xc1, 0x8b, 0x41, 0xec, 0x99, 0x28, 0xf4, 0x2e, 0x3e, 0x11, 0x72, - 0x2e, 0x2a, 0xa4, 0x44, 0x0d, 0x6a, 0xeb, 0xb6, 0xbf, 0x92, 0xc6, 0x2c, 0xaa, 0x68, 0xab, 0xaa, - 0x61, 0xd0, 0xb2, 0x1b, 0x02, 0x97, 0x22, 0x44, 0xfa, 0x18, 0xe1, 0xd4, 0xab, 0x2e, 0x9e, 0x25, - 0x43, 0xa3, 0x86, 0xa3, 0x57, 0xf5, 0xdb, 0xb4, 0xb0, 0xac, 0x6a, 0x6b, 0xd4, 0xb1, 0xb3, 0xf4, - 0x56, 0x85, 0xda, 0x0e, 0x59, 0xc4, 0xb8, 0x05, 0x32, 0x8e, 0x26, 0xd1, 0xd4, 0x50, 0xfa, 0x31, - 0x59, 0x30, 0x92, 0x5d, 0x46, 0xb2, 0xd0, 0x15, 0x18, 0xc9, 0xcb, 0x6a, 0x89, 0x42, 0x6e, 0xd6, - 0x97, 0x49, 0xce, 0xe0, 0x61, 0x1e, 0x98, 0x5b, 0xa5, 0x7a, 0x69, 0xd5, 0x89, 0xf7, 0x4e, 0xa2, - 0xa9, 0xbe, 0xec, 0x10, 0x7f, 0xf6, 0x3c, 0x7f, 0x24, 0x7d, 0x88, 0xf0, 0x64, 0x34, 0x1c, 0xdb, - 0x64, 0x86, 0x4d, 0x49, 0x11, 0x8f, 0xea, 0xbe, 0xe5, 0x9c, 0x29, 0xd6, 0xe3, 0x68, 0xf2, 0xd8, - 0xd4, 0x50, 0x7a, 0x46, 0x8e, 0xd8, 0x58, 0x79, 0xa9, 0xe0, 0xe6, 0x14, 0x75, 0xaf, 0xe2, 0x22, - 0xa5, 0x76, 0xa6, 0xef, 0x7e, 0x3d, 0xd5, 0x93, 0x1d, 0xd1, 0x83, 0xfd, 0xa4, 0x2d, 0x84, 0x93, - 0x11, 0x60, 0x3c, 0x69, 0x9e, 0xc5, 0x83, 0xa2, 0x7b, 0x4e, 0x2f, 0x80, 0x32, 0x13, 0xbc, 0xbf, - 0xab, 0xba, 0xec, 0x49, 0x5d, 0x75, 0x35, 0x71, 0xa3, 0x96, 0x0a, 0xd0, 0x6f, 0xc0, 0x84, 0xfb, - 0x4e, 0x44, 0x79, 0x3f, 0x7a, 0x8f, 0x9a, 0x9a, 0x14, 0xf0, 0x48, 0x88, 0x26, 0x00, 0xe9, 0x40, - 0x92, 0x90, 0xa0, 0x24, 0xd2, 0x0f, 0x08, 0x3f, 0x11, 0xb5, 0x3d, 0x8b, 0xcc, 0x5a, 0x10, 0x7c, - 0x0f, 0x7b, 0x6e, 0x4e, 0xe3, 0x7e, 0x93, 0x59, 0x5c, 0x62, 0x57, 0x9d, 0xc1, 0xec, 0x09, 0xf7, - 0x76, 0xa9, 0x40, 0x26, 0x30, 0x06, 0x89, 0xdd, 0xb5, 0x63, 0x7c, 0x6d, 0x10, 0x9e, 0x84, 0x48, - 0xdb, 0x17, 0x94, 0xf6, 0x13, 0x84, 0xa7, 0x3b, 0x21, 0x04, 0x2a, 0xdf, 0x3c, 0xc4, 0xc9, 0x0b, - 0x9f, 0xb9, 0x37, 0xf1, 0x18, 0xc7, 0xb3, 0xc2, 0x1c, 0xb5, 0x9c, 0xa5, 0x5a, 0x95, 0x87, 0x1e, - 0xd6, 0xb4, 0x49, 0x5f, 0x22, 0x9c, 0x08, 0xab, 0x0f, 0xfc, 0xee, 0xe0, 0x41, 0x8b, 0x6a, 0xd5, - 0x5c, 0x91, 0x52, 0x8f, 0xd4, 0x58, 0xdb, 0x86, 0x79, 0x5b, 0xb5, 0xc0, 0x74, 0x23, 0x73, 0xd5, - 0x2d, 0xde, 0xa8, 0xa7, 0x4e, 0xd5, 0xd4, 0xf5, 0xf2, 0x53, 0x52, 0x33, 0x53, 0xfa, 0xf6, 0x97, - 0xd4, 0x54, 0x49, 0x77, 0x56, 0x2b, 0x79, 0x59, 0x63, 0xeb, 0x0a, 0x7c, 0xfb, 0xc4, 0x9f, 0x19, - 0xbb, 0xb0, 0xa6, 0x38, 0x35, 0x93, 0xda, 0xbc, 0x88, 0x9d, 0x1d, 0xb0, 0x00, 0x85, 0xf4, 0x06, - 0x8e, 0xb7, 0xb0, 0xcd, 0x6b, 0x6b, 0x87, 0x4b, 0xfd, 0x0b, 0xe4, 0x97, 0xb6, 0x59, 0x1e, 0x98, - 0xd7, 0xf0, 0x80, 0xaa, 0xad, 0x75, 0x48, 0x7c, 0x01, 0x88, 0xff, 0x5f, 0x10, 0xf7, 0x12, 0xbb, - 0xe3, 0xdd, 0xaf, 0x0a, 0x08, 0xd2, 0x4d, 0x3c, 0xde, 0xc2, 0xb5, 0xa2, 0xaf, 0x53, 0x56, 0x71, - 0x0e, 0x97, 0xfa, 0xd7, 0x08, 0x4f, 0x44, 0xb4, 0x00, 0xfa, 0x5b, 0x08, 0x0f, 0x3b, 0xe2, 0x79, - 0x87, 0x1a, 0x5c, 0x03, 0x0d, 0x46, 0x84, 0x06, 0xfe, 0xe4, 0xee, 0x74, 0x18, 0x72, 0x5a, 0x78, - 0x24, 0x0d, 0xc7, 0x38, 0xd0, 0x65, 0xb5, 0x46, 0xbd, 0x6f, 0x01, 0xb9, 0xd8, 0xf6, 0x9a, 0xbb, - 0x0a, 0x0c, 0x66, 0x1e, 0x6a, 0xd4, 0x53, 0x31, 0xd1, 0xba, 0xb5, 0x26, 0xf9, 0xdf, 0xfe, 0x38, - 0xee, 0xb7, 0x68, 0x59, 0xad, 0x51, 0x0b, 0xbe, 0x1a, 0xde, 0xad, 0x74, 0x03, 0x13, 0x7f, 0x13, - 0x90, 0xe0, 0x69, 0x7c, 0xd2, 0x74, 0x1f, 0xe4, 0xd4, 0x42, 0xc1, 0xa2, 0xb6, 0x0d, 0x8d, 0xe2, - 0x8d, 0x7a, 0x6a, 0x54, 0x34, 0x6a, 0x5b, 0x96, 0xb2, 0xc3, 0xfc, 0x7e, 0x1e, 0x6e, 0x19, 0x48, - 0xbc, 0xc0, 0x2a, 0x86, 0x43, 0x2d, 0x53, 0xb5, 0x9c, 0x7f, 0x97, 0x85, 0x01, 0x87, 0x53, 0x48, - 0x43, 0x60, 0x74, 0x1d, 0x13, 0xcd, 0xb7, 0x98, 0xe3, 0x78, 0xa1, 0xf3, 0x44, 0xa3, 0x9e, 0x1a, - 0x83, 0xce, 0x81, 0x18, 0x29, 0x1b, 0xd3, 0x1e, 0xac, 0x2a, 0x7d, 0xe4, 0x9d, 0x86, 0x8b, 0x94, - 0x3e, 0x67, 0xa8, 0xf9, 0x32, 0x2d, 0xc0, 0xe7, 0xf1, 0xbf, 0x30, 0x0a, 0x5f, 0x79, 0x67, 0x62, - 0x18, 0x1a, 0xe0, 0x7f, 0x17, 0xe1, 0xd1, 0x22, 0xa5, 0x39, 0x2a, 0xd6, 0x73, 0xa0, 0xaa, 0x37, - 0xdc, 0xd3, 0x91, 0x9f, 0xeb, 0x40, 0xcd, 0xcc, 0x59, 0x98, 0xf6, 0x47, 0x84, 0x64, 0x61, 0x55, - 0xa5, 0x2c, 0x29, 0x06, 0xb0, 0x48, 0xf7, 0xbc, 0x57, 0x2f, 0x50, 0xd3, 0x13, 0xed, 0x7c, 0xeb, - 0x74, 0x13, 0x5b, 0x43, 0x1a, 0xf5, 0xd4, 0xff, 0x60, 0xe2, 0xc4, 0x82, 0xd4, 0x3c, 0xf1, 0xda, - 0x87, 0xa8, 0xb7, 0xb3, 0x21, 0x92, 0x5e, 0x8b, 0xda, 0xb9, 0xa6, 0x54, 0x73, 0x78, 0xc8, 0xc7, - 0x89, 0x03, 0x19, 0xc8, 0x3c, 0xdc, 0xa8, 0xa7, 0x48, 0x80, 0xb0, 0x94, 0xc5, 0x2d, 0x9e, 0xe9, - 0xdf, 0x63, 0xf8, 0x38, 0xaf, 0x4d, 0xbe, 0x47, 0x78, 0x24, 0xe4, 0x14, 0x25, 0x97, 0x23, 0x65, - 0xde, 0xc7, 0x77, 0x26, 0xae, 0x1c, 0x20, 0x53, 0xf0, 0x91, 0x66, 0xee, 0xfd, 0xf8, 0xdb, 0xe7, - 0xbd, 0x8f, 0x93, 0x73, 0x0a, 0x38, 0xe5, 0xa6, 0x43, 0x0e, 0x3b, 0xbf, 0xc9, 0xa7, 0xbd, 0x98, - 0x04, 0xcb, 0x91, 0xb9, 0x6e, 0x01, 0x78, 0xc8, 0x2f, 0x77, 0x9f, 0x08, 0xc0, 0xb7, 0x10, 0x47, - 0xfe, 0x0e, 0xd9, 0x0c, 0x20, 0xf7, 0x06, 0x4d, 0xd9, 0x68, 0x1e, 0x07, 0x72, 0x6b, 0xc3, 0x37, - 0x15, 0x77, 0x44, 0xda, 0x16, 0x61, 0x7a, 0x36, 0x15, 0xdb, 0x85, 0x65, 0x68, 0xb4, 0x6d, 0xd5, - 0x7b, 0xb8, 0x19, 0x26, 0x09, 0xf9, 0x0b, 0xe1, 0x89, 0x3d, 0x3d, 0x11, 0xc9, 0x74, 0xbd, 0x3b, - 0x01, 0x87, 0x98, 0x58, 0xf8, 0x47, 0x35, 0x40, 0xb2, 0x1b, 0x5c, 0xb1, 0x97, 0xc8, 0x8b, 0x7b, - 0x28, 0x16, 0xa6, 0x93, 0xa7, 0x4e, 0xe8, 0x44, 0xfc, 0x89, 0xf0, 0xc9, 0x36, 0x8f, 0x44, 0xd2, - 0x7b, 0x63, 0x0d, 0x33, 0x6c, 0x89, 0x0b, 0x5d, 0xe5, 0x00, 0x9f, 0xbb, 0x62, 0x04, 0x36, 0x48, - 0xed, 0xe8, 0x46, 0xc0, 0x71, 0x91, 0xe4, 0x9a, 0x0e, 0x8e, 0xfc, 0x81, 0xf0, 0xb0, 0xdf, 0x27, - 0x91, 0xd9, 0x0e, 0x98, 0xb4, 0x5b, 0xb6, 0x44, 0xba, 0x9b, 0x14, 0xe0, 0xfe, 0xae, 0xe0, 0x7e, - 0x9b, 0xbc, 0x75, 0xd4, 0xdc, 0x3d, 0x13, 0x47, 0x3e, 0xe8, 0xc5, 0xa7, 0x1e, 0xf4, 0x49, 0xe4, - 0x52, 0x07, 0x5c, 0x82, 0xd6, 0x2d, 0xf1, 0x64, 0xb7, 0x69, 0x20, 0xc3, 0x7b, 0x42, 0x86, 0xb7, - 0xc9, 0x9d, 0xa3, 0x96, 0xc1, 0xef, 0xe3, 0xc8, 0x37, 0x08, 0x1f, 0xe7, 0x87, 0x3f, 0x99, 0xde, - 0x9b, 0x88, 0xdf, 0xe8, 0x24, 0xce, 0x77, 0x14, 0x0b, 0x4c, 0xaf, 0x71, 0xa2, 0xf3, 0xe4, 0x99, - 0x0e, 0x5f, 0x5e, 0x70, 0x3f, 0xb6, 0xb2, 0x01, 0x57, 0x9b, 0x0a, 0xb7, 0x2c, 0xe4, 0x67, 0x84, - 0x63, 0x01, 0x2b, 0x44, 0xf6, 0xd9, 0x80, 0x28, 0xb3, 0x96, 0x98, 0xeb, 0x3a, 0x0f, 0xf8, 0xac, - 0x70, 0x3e, 0x2f, 0x93, 0xeb, 0x07, 0xe7, 0x13, 0xf4, 0x63, 0xe4, 0x3b, 0x84, 0x49, 0xd0, 0xe8, - 0xec, 0x77, 0x3e, 0x45, 0x1a, 0xb5, 0xfd, 0xce, 0xa7, 0x68, 0x4f, 0x25, 0x3d, 0xca, 0xf9, 0x25, - 0xc9, 0x78, 0x80, 0x9f, 0xcf, 0x22, 0x90, 0x6d, 0x84, 0x63, 0x81, 0x22, 0xfb, 0x6d, 0x46, 0x94, - 0x43, 0x4a, 0xcc, 0x75, 0x9d, 0x07, 0x60, 0x5f, 0xe0, 0x60, 0xaf, 0x92, 0xcc, 0x01, 0x4f, 0x06, - 0x1f, 0xa5, 0xcc, 0x2b, 0xf7, 0x77, 0x92, 0x68, 0x7b, 0x27, 0x89, 0x7e, 0xdd, 0x49, 0xa2, 0xcf, - 0x76, 0x93, 0x3d, 0xdb, 0xbb, 0xc9, 0x9e, 0x9f, 0x76, 0x93, 0x3d, 0xaf, 0x5f, 0x0a, 0xfe, 0xd4, - 0xd1, 0xf3, 0xda, 0x4c, 0x89, 0x29, 0xd5, 0x8b, 0xca, 0x3a, 0x2b, 0x54, 0xca, 0xd4, 0x16, 0xcd, - 0xd3, 0x57, 0x66, 0xdc, 0xfe, 0xfc, 0xd7, 0x4f, 0xfe, 0x04, 0xff, 0x07, 0xdc, 0x85, 0xbf, 0x03, - 0x00, 0x00, 0xff, 0xff, 0xae, 0x96, 0x61, 0xf5, 0xad, 0x14, 0x00, 0x00, + // 1358 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x58, 0x51, 0x6f, 0xdb, 0x54, + 0x14, 0xee, 0xcd, 0xba, 0xb5, 0xbd, 0xed, 0x60, 0xb9, 0x2d, 0x2c, 0x0d, 0x6d, 0xd2, 0x79, 0x8c, + 0x95, 0x4e, 0xb5, 0xd5, 0x8c, 0xd1, 0x0d, 0x09, 0x41, 0xd3, 0xd1, 0x52, 0x18, 0x50, 0xb2, 0xbe, + 0x80, 0x40, 0x99, 0xe3, 0xdc, 0xa4, 0x56, 0x53, 0xdb, 0xb3, 0x9d, 0x88, 0xac, 0x2b, 0xb0, 0x89, + 0x0a, 0x24, 0x90, 0x40, 0x42, 0xe2, 0x81, 0x3f, 0x80, 0x40, 0xe2, 0x07, 0xf0, 0x0f, 0xf6, 0x84, + 0x2a, 0xed, 0x01, 0xc4, 0x43, 0x40, 0x2d, 0xe2, 0x91, 0x87, 0x3c, 0xf1, 0x00, 0x12, 0xf2, 0xbd, + 0xc7, 0x89, 0x33, 0xdb, 0x4d, 0x52, 0xb2, 0xf2, 0xd4, 0xd8, 0xe7, 0x9e, 0x73, 0xbe, 0xef, 0xbb, + 0x27, 0xf7, 0x7e, 0x29, 0x3e, 0xab, 0xe6, 0x14, 0x49, 0x36, 0x8c, 0x92, 0xaa, 0xc8, 0xb6, 0xaa, + 0x6b, 0x96, 0x54, 0xa0, 0x54, 0xaa, 0xcc, 0x49, 0x37, 0xcb, 0xd4, 0xac, 0x8a, 0x86, 0xa9, 0xdb, + 0x3a, 0x39, 0xad, 0xe6, 0x14, 0xd1, 0xbb, 0x48, 0x2c, 0x50, 0x2a, 0x56, 0xe6, 0xe2, 0x63, 0x45, + 0xbd, 0xa8, 0xb3, 0x35, 0x92, 0xf3, 0x89, 0x2f, 0x8f, 0x4f, 0x14, 0x75, 0xbd, 0x58, 0xa2, 0x92, + 0x6c, 0xa8, 0x92, 0xac, 0x69, 0xba, 0x0d, 0x49, 0x3c, 0x9a, 0x50, 0x74, 0x6b, 0x53, 0xb7, 0xa4, + 0x9c, 0x6c, 0x39, 0x8d, 0x72, 0xd4, 0x96, 0xe7, 0x24, 0x45, 0x57, 0x35, 0x88, 0xcf, 0x78, 0xe3, + 0x0c, 0x45, 0x63, 0x95, 0x21, 0x17, 0x55, 0x8d, 0x15, 0x83, 0xb5, 0x67, 0xc2, 0xd0, 0x3b, 0xf8, + 0xf8, 0x92, 0x73, 0x61, 0x4b, 0x8a, 0x54, 0xa3, 0x96, 0x6a, 0x79, 0x2b, 0x29, 0xba, 0x49, 0x25, + 0x65, 0x5d, 0xd6, 0x34, 0x5a, 0x72, 0x96, 0xc0, 0x47, 0xbe, 0x44, 0xf8, 0x0c, 0xe1, 0xe4, 0x9b, + 0x0e, 0x9e, 0x15, 0x4d, 0xa1, 0x9a, 0xad, 0x56, 0xd4, 0x5b, 0x34, 0xbf, 0x2a, 0x2b, 0x1b, 0xd4, + 0xb6, 0x32, 0xf4, 0x66, 0x99, 0x5a, 0x36, 0x59, 0xc2, 0xb8, 0x09, 0x32, 0x86, 0xa6, 0xd0, 0xf4, + 0x70, 0xea, 0x29, 0x91, 0x33, 0x12, 0x1d, 0x46, 0x22, 0xd7, 0x15, 0x18, 0x89, 0xab, 0x72, 0x91, + 0x42, 0x6e, 0xc6, 0x93, 0x49, 0xce, 0xe0, 0x11, 0xb6, 0x30, 0xbb, 0x4e, 0xd5, 0xe2, 0xba, 0x1d, + 0x8b, 0x4c, 0xa1, 0xe9, 0xfe, 0xcc, 0x30, 0x7b, 0xf7, 0x32, 0x7b, 0x25, 0xdc, 0x47, 0x78, 0x2a, + 0x1c, 0x8e, 0x65, 0xe8, 0x9a, 0x45, 0x49, 0x01, 0x8f, 0xa9, 0x9e, 0x70, 0xd6, 0xe0, 0xf1, 0x18, + 0x9a, 0x3a, 0x36, 0x3d, 0x9c, 0x9a, 0x15, 0x43, 0x36, 0x56, 0x5c, 0xc9, 0x3b, 0x39, 0x05, 0xd5, + 0xad, 0xb8, 0x44, 0xa9, 0x95, 0xee, 0xbf, 0x57, 0x4b, 0xf6, 0x65, 0x46, 0x55, 0x7f, 0x3f, 0xb2, + 0xdc, 0xc2, 0x3b, 0xc2, 0x78, 0x9f, 0x6f, 0xcb, 0x9b, 0x83, 0xf4, 0x12, 0x17, 0x76, 0x10, 0x4e, + 0x84, 0xb0, 0x72, 0x35, 0x7e, 0x11, 0x0f, 0x71, 0x1a, 0x59, 0x35, 0x0f, 0x12, 0x4f, 0x32, 0x22, + 0xce, 0xf6, 0x89, 0xee, 0x9e, 0x55, 0x9c, 0x26, 0xce, 0xaa, 0x95, 0x3c, 0x00, 0x1f, 0x34, 0xe0, + 0xb9, 0x13, 0x75, 0x3f, 0x0e, 0xdf, 0xec, 0x86, 0xb8, 0x79, 0x3c, 0x1a, 0x20, 0x2e, 0x40, 0x3a, + 0x94, 0xb6, 0xc4, 0xaf, 0xad, 0xf0, 0x23, 0xc2, 0x4f, 0x87, 0xed, 0xf3, 0x92, 0x6e, 0x2e, 0x72, + 0xbe, 0xbd, 0x1e, 0xc0, 0xd3, 0x78, 0xc0, 0xd0, 0x4d, 0x26, 0xb1, 0xa3, 0xce, 0x50, 0xe6, 0x84, + 0xf3, 0xb8, 0x92, 0x27, 0x93, 0x18, 0x83, 0xc4, 0x4e, 0xec, 0x18, 0x8b, 0x0d, 0xc1, 0x9b, 0x00, + 0x69, 0xfb, 0xfd, 0xd2, 0xfe, 0x84, 0xf0, 0x4c, 0x27, 0x84, 0x40, 0xe5, 0x1b, 0x3d, 0x1c, 0xe1, + 0x87, 0x3c, 0xbc, 0xef, 0xe2, 0x71, 0x46, 0x6c, 0x4d, 0xb7, 0xe5, 0x52, 0x86, 0x2a, 0x15, 0xd6, + 0xb3, 0x57, 0x63, 0x2b, 0x7c, 0x8d, 0x70, 0x3c, 0xa8, 0x3e, 0x08, 0x75, 0x1b, 0x0f, 0x99, 0x54, + 0xa9, 0x64, 0x0b, 0x94, 0xba, 0xea, 0x8c, 0xb7, 0xb0, 0x70, 0xf1, 0x2f, 0xea, 0xaa, 0x96, 0xbe, + 0xea, 0x14, 0xaf, 0xd7, 0x92, 0xa7, 0xaa, 0xf2, 0x66, 0xe9, 0x39, 0xa1, 0x91, 0x29, 0x7c, 0xf7, + 0x6b, 0x72, 0xba, 0xa8, 0xda, 0xeb, 0xe5, 0x9c, 0xa8, 0xe8, 0x9b, 0x12, 0x9c, 0xc6, 0xfc, 0xcf, + 0xac, 0x95, 0xdf, 0x90, 0xec, 0xaa, 0x41, 0x2d, 0x56, 0xc4, 0xca, 0x0c, 0x9a, 0x80, 0x42, 0x78, + 0x07, 0xc7, 0x9a, 0xd8, 0x16, 0x94, 0x8d, 0xde, 0x52, 0xff, 0x0a, 0x79, 0xa5, 0x6d, 0x94, 0x07, + 0xe6, 0x55, 0x3c, 0x28, 0x2b, 0x1b, 0x1d, 0x12, 0x5f, 0x04, 0xe2, 0x8f, 0x72, 0xe2, 0x6e, 0x62, + 0x77, 0xbc, 0x07, 0x64, 0x0e, 0x41, 0xb8, 0x81, 0x27, 0x9a, 0xb8, 0xd6, 0xd4, 0x4d, 0xaa, 0x97, + 0xed, 0xde, 0x52, 0xff, 0x06, 0xe1, 0xc9, 0x90, 0x16, 0x40, 0x7f, 0x07, 0xe1, 0x11, 0x9b, 0xbf, + 0xef, 0x50, 0x83, 0x65, 0xd0, 0x60, 0x94, 0x6b, 0xe0, 0x4d, 0xee, 0x4e, 0x87, 0x61, 0xbb, 0x89, + 0x47, 0x50, 0x70, 0x94, 0x01, 0x5d, 0x95, 0xab, 0xd4, 0x3d, 0x54, 0xc8, 0x33, 0x2d, 0xe7, 0x85, + 0xa3, 0xc0, 0x50, 0xfa, 0xb1, 0x7a, 0x2d, 0x19, 0xe5, 0xad, 0x9b, 0x31, 0xc1, 0x7b, 0x8c, 0xc4, + 0xf0, 0x80, 0x49, 0x4b, 0x72, 0x95, 0x9a, 0x70, 0xfc, 0xb8, 0x8f, 0xc2, 0x75, 0x4c, 0xbc, 0x4d, + 0x40, 0x82, 0xe7, 0xf1, 0x49, 0xc3, 0x79, 0x91, 0x95, 0xf3, 0x79, 0x93, 0x5a, 0x16, 0x34, 0x8a, + 0xd5, 0x6b, 0xc9, 0x31, 0xde, 0xa8, 0x25, 0x2c, 0x64, 0x46, 0xd8, 0xf3, 0x02, 0x3c, 0xea, 0x20, + 0xf1, 0xa2, 0x5e, 0xd6, 0x6c, 0x6a, 0x1a, 0xb2, 0x69, 0x3f, 0x5c, 0x16, 0x1a, 0xdc, 0x72, 0x01, + 0x0d, 0x81, 0xd1, 0x35, 0x4c, 0x14, 0x4f, 0x30, 0xcb, 0xf0, 0x42, 0xe7, 0xc9, 0x7a, 0x2d, 0x39, + 0x0e, 0x9d, 0x7d, 0x6b, 0x84, 0x4c, 0x54, 0x79, 0xb0, 0xaa, 0xf0, 0xa9, 0x7b, 0xad, 0x2e, 0x51, + 0xfa, 0x92, 0x26, 0xe7, 0x4a, 0x34, 0x0f, 0xe7, 0xec, 0xff, 0x61, 0x5d, 0xfe, 0x74, 0x2f, 0xd7, + 0x20, 0x34, 0xc0, 0xff, 0x0e, 0xc2, 0x63, 0x05, 0x4a, 0xb3, 0x94, 0xc7, 0xb3, 0xa0, 0xaa, 0x3b, + 0xdc, 0x33, 0xa1, 0xe7, 0xbe, 0xaf, 0x66, 0xfa, 0x2c, 0x4c, 0xfb, 0x13, 0x5c, 0xb2, 0xa0, 0xaa, + 0x42, 0x86, 0x14, 0x7c, 0x58, 0x7a, 0x77, 0x31, 0xdc, 0x75, 0xbf, 0xc3, 0x3e, 0x70, 0xae, 0xfa, + 0x17, 0x9a, 0xf7, 0x2d, 0xdf, 0x63, 0x52, 0xaf, 0x25, 0x1f, 0x81, 0xd1, 0xe5, 0x01, 0xa1, 0x71, + 0x07, 0xb7, 0x4e, 0x63, 0xa4, 0xb3, 0x69, 0x14, 0xde, 0x0a, 0x1b, 0x81, 0x86, 0xe6, 0xf3, 0x78, + 0xd8, 0x23, 0x0e, 0x03, 0x32, 0x98, 0x7e, 0xbc, 0x5e, 0x4b, 0x12, 0x9f, 0x72, 0x42, 0x06, 0x37, + 0x05, 0x4b, 0xfd, 0x11, 0xc5, 0xc7, 0x59, 0x6d, 0xf2, 0x03, 0xc2, 0xa3, 0x01, 0xf7, 0x3a, 0xb9, + 0x1c, 0xba, 0x5f, 0x6d, 0x2c, 0x75, 0xfc, 0xca, 0x21, 0x32, 0x39, 0x1f, 0x61, 0xf6, 0xee, 0xfd, + 0xdf, 0xbf, 0x8c, 0x9c, 0x27, 0xe7, 0x24, 0xf8, 0x11, 0xd0, 0x30, 0xff, 0x41, 0x8e, 0x82, 0x7c, + 0x1e, 0xc1, 0xc4, 0x5f, 0x8e, 0xcc, 0x77, 0x0b, 0xc0, 0x45, 0x7e, 0xb9, 0xfb, 0x44, 0x00, 0xbe, + 0x83, 0x18, 0xf2, 0x0f, 0xc8, 0xb6, 0x0f, 0xb9, 0x3b, 0xb1, 0xd2, 0x56, 0xe3, 0x5e, 0x11, 0x9b, + 0x1b, 0xbe, 0x2d, 0x39, 0x23, 0xd2, 0x12, 0x84, 0xe9, 0xd9, 0x96, 0x2c, 0x07, 0x96, 0xa6, 0xd0, + 0x96, 0xa8, 0xfb, 0x72, 0x3b, 0x48, 0x12, 0xf2, 0x0f, 0xc2, 0x93, 0x07, 0xba, 0x34, 0x92, 0xee, + 0x7a, 0x77, 0x7c, 0x9e, 0x35, 0xbe, 0xf8, 0x9f, 0x6a, 0x80, 0x64, 0xd7, 0x99, 0x62, 0xaf, 0x91, + 0x57, 0x0f, 0x50, 0x2c, 0x48, 0x27, 0x57, 0x9d, 0xc0, 0x89, 0xf8, 0x1b, 0xe1, 0x93, 0x2d, 0x66, + 0x8b, 0xa4, 0x0e, 0xc6, 0x1a, 0xe4, 0xfc, 0xe2, 0x17, 0xbb, 0xca, 0x01, 0x3e, 0x77, 0xf8, 0x08, + 0x6c, 0x91, 0xea, 0xd1, 0x8d, 0x80, 0xed, 0x20, 0xc9, 0x36, 0xac, 0x20, 0xf9, 0x0b, 0xe1, 0x11, + 0xaf, 0xe1, 0x22, 0x73, 0x1d, 0x30, 0x69, 0xf5, 0x7e, 0xf1, 0x54, 0x37, 0x29, 0xc0, 0xfd, 0x43, + 0xce, 0xfd, 0x16, 0x79, 0xef, 0xa8, 0xb9, 0xbb, 0x6e, 0x90, 0x7c, 0x12, 0xc1, 0xa7, 0x1e, 0x34, + 0x5c, 0xe4, 0x52, 0x07, 0x5c, 0xfc, 0x1e, 0x30, 0xfe, 0x6c, 0xb7, 0x69, 0x20, 0xc3, 0x47, 0x5c, + 0x86, 0xf7, 0xc9, 0xed, 0xa3, 0x96, 0xc1, 0x6b, 0x08, 0xc9, 0xb7, 0x08, 0x1f, 0x67, 0x2e, 0x82, + 0xcc, 0x1c, 0x4c, 0xc4, 0xeb, 0x98, 0xe2, 0x17, 0x3a, 0x5a, 0x0b, 0x4c, 0x97, 0x19, 0xd1, 0x05, + 0xf2, 0x42, 0x87, 0x5f, 0x5e, 0xb0, 0x51, 0x96, 0xb4, 0x05, 0x9f, 0xb6, 0x25, 0xe6, 0x7d, 0xc8, + 0x2f, 0x08, 0x47, 0x7d, 0x9e, 0x8a, 0xb4, 0xd9, 0x80, 0x30, 0xd7, 0x17, 0x9f, 0xef, 0x3a, 0x0f, + 0xf8, 0xac, 0x31, 0x3e, 0xaf, 0x93, 0x6b, 0x87, 0xe7, 0xe3, 0x37, 0x76, 0xe4, 0x7b, 0x84, 0x89, + 0xdf, 0x31, 0xb5, 0xbb, 0x9f, 0x42, 0x1d, 0x5f, 0xbb, 0xfb, 0x29, 0xdc, 0x9c, 0x09, 0x4f, 0x32, + 0x7e, 0x09, 0x32, 0xe1, 0xe3, 0xe7, 0xb1, 0x08, 0x64, 0x17, 0xe1, 0xa8, 0xaf, 0x48, 0xbb, 0xcd, + 0x08, 0x73, 0x48, 0xf1, 0xf9, 0xae, 0xf3, 0x00, 0xec, 0x2b, 0x0c, 0xec, 0x55, 0x92, 0x3e, 0xe4, + 0xcd, 0xe0, 0xa1, 0x94, 0x7e, 0xe3, 0xde, 0x5e, 0x02, 0xed, 0xee, 0x25, 0xd0, 0x6f, 0x7b, 0x09, + 0xf4, 0xc5, 0x7e, 0xa2, 0x6f, 0x77, 0x3f, 0xd1, 0xf7, 0xf3, 0x7e, 0xa2, 0xef, 0xed, 0x4b, 0xfe, + 0xdf, 0x4c, 0x6a, 0x4e, 0x99, 0x2d, 0xea, 0x52, 0x65, 0x5e, 0xda, 0xd4, 0xf3, 0xe5, 0x12, 0xb5, + 0x78, 0xf3, 0xd4, 0x95, 0x59, 0xa7, 0x3f, 0xfb, 0x19, 0x95, 0x3b, 0xc1, 0xfe, 0xb7, 0x78, 0xf1, + 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x1f, 0x67, 0x6b, 0x48, 0x88, 0x15, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -1640,6 +1668,18 @@ func (m *QueryIncentivizedPacketsResponse) MarshalToSizedBuffer(dAtA []byte) (in _ = i var l int _ = l + if m.Pagination != nil { + { + size, err := m.Pagination.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } if len(m.IncentivizedPackets) > 0 { for iNdEx := len(m.IncentivizedPackets) - 1; iNdEx >= 0; iNdEx-- { { @@ -1802,6 +1842,18 @@ func (m *QueryIncentivizedPacketsForChannelResponse) MarshalToSizedBuffer(dAtA [ _ = i var l int _ = l + if m.Pagination != nil { + { + size, err := m.Pagination.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } if len(m.IncentivizedPackets) > 0 { for iNdEx := len(m.IncentivizedPackets) - 1; iNdEx >= 0; iNdEx-- { { @@ -2223,6 +2275,18 @@ func (m *QueryFeeEnabledChannelsResponse) MarshalToSizedBuffer(dAtA []byte) (int _ = i var l int _ = l + if m.Pagination != nil { + { + size, err := m.Pagination.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } if len(m.FeeEnabledChannels) > 0 { for iNdEx := len(m.FeeEnabledChannels) - 1; iNdEx >= 0; iNdEx-- { { @@ -2349,6 +2413,10 @@ func (m *QueryIncentivizedPacketsResponse) Size() (n int) { n += 1 + l + sovQuery(uint64(l)) } } + if m.Pagination != nil { + l = m.Pagination.Size() + n += 1 + l + sovQuery(uint64(l)) + } return n } @@ -2413,6 +2481,10 @@ func (m *QueryIncentivizedPacketsForChannelResponse) Size() (n int) { n += 1 + l + sovQuery(uint64(l)) } } + if m.Pagination != nil { + l = m.Pagination.Size() + n += 1 + l + sovQuery(uint64(l)) + } return n } @@ -2582,6 +2654,10 @@ func (m *QueryFeeEnabledChannelsResponse) Size() (n int) { n += 1 + l + sovQuery(uint64(l)) } } + if m.Pagination != nil { + l = m.Pagination.Size() + n += 1 + l + sovQuery(uint64(l)) + } return n } @@ -2788,6 +2864,42 @@ func (m *QueryIncentivizedPacketsResponse) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pagination", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Pagination == nil { + m.Pagination = &query.PageResponse{} + } + if err := m.Pagination.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipQuery(dAtA[iNdEx:]) @@ -3226,6 +3338,42 @@ func (m *QueryIncentivizedPacketsForChannelResponse) Unmarshal(dAtA []byte) erro return err } iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pagination", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Pagination == nil { + m.Pagination = &query.PageResponse{} + } + if err := m.Pagination.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipQuery(dAtA[iNdEx:]) @@ -4308,6 +4456,42 @@ func (m *QueryFeeEnabledChannelsResponse) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pagination", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Pagination == nil { + m.Pagination = &query.PageResponse{} + } + if err := m.Pagination.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipQuery(dAtA[iNdEx:]) diff --git a/modules/apps/29-fee/types/query.pb.gw.go b/modules/apps/29-fee/types/query.pb.gw.go index d6e723f9bdd..46554122eb5 100644 --- a/modules/apps/29-fee/types/query.pb.gw.go +++ b/modules/apps/29-fee/types/query.pb.gw.go @@ -20,6 +20,7 @@ import ( "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/grpclog" + "google.golang.org/grpc/metadata" "google.golang.org/grpc/status" ) @@ -30,6 +31,7 @@ var _ status.Status var _ = runtime.String var _ = utilities.NewDoubleArray var _ = descriptor.ForMessage +var _ = metadata.Join var ( filter_Query_IncentivizedPackets_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} @@ -892,12 +894,14 @@ func local_request_Query_FeeEnabledChannel_0(ctx context.Context, marshaler runt // RegisterQueryHandlerServer registers the http handlers for service Query to "mux". // UnaryRPC :call QueryServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. -// Note that using this registration option will cause many gRPC library features (such as grpc.SendHeader, etc) to stop working. Consider using RegisterQueryHandlerFromEndpoint instead. +// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterQueryHandlerFromEndpoint instead. func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, server QueryServer) error { mux.Handle("GET", pattern_Query_IncentivizedPackets_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { @@ -905,6 +909,7 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv return } resp, md, err := local_request_Query_IncentivizedPackets_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) @@ -918,6 +923,8 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv mux.Handle("GET", pattern_Query_IncentivizedPacket_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { @@ -925,6 +932,7 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv return } resp, md, err := local_request_Query_IncentivizedPacket_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) @@ -938,6 +946,8 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv mux.Handle("GET", pattern_Query_IncentivizedPacketsForChannel_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { @@ -945,6 +955,7 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv return } resp, md, err := local_request_Query_IncentivizedPacketsForChannel_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) @@ -958,6 +969,8 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv mux.Handle("GET", pattern_Query_TotalRecvFees_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { @@ -965,6 +978,7 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv return } resp, md, err := local_request_Query_TotalRecvFees_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) @@ -978,6 +992,8 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv mux.Handle("GET", pattern_Query_TotalAckFees_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { @@ -985,6 +1001,7 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv return } resp, md, err := local_request_Query_TotalAckFees_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) @@ -998,6 +1015,8 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv mux.Handle("GET", pattern_Query_TotalTimeoutFees_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { @@ -1005,6 +1024,7 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv return } resp, md, err := local_request_Query_TotalTimeoutFees_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) @@ -1018,6 +1038,8 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv mux.Handle("GET", pattern_Query_Payee_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { @@ -1025,6 +1047,7 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv return } resp, md, err := local_request_Query_Payee_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) @@ -1038,6 +1061,8 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv mux.Handle("GET", pattern_Query_CounterpartyPayee_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { @@ -1045,6 +1070,7 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv return } resp, md, err := local_request_Query_CounterpartyPayee_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) @@ -1058,6 +1084,8 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv mux.Handle("GET", pattern_Query_FeeEnabledChannels_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { @@ -1065,6 +1093,7 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv return } resp, md, err := local_request_Query_FeeEnabledChannels_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) @@ -1078,6 +1107,8 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv mux.Handle("GET", pattern_Query_FeeEnabledChannel_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { @@ -1085,6 +1116,7 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv return } resp, md, err := local_request_Query_FeeEnabledChannel_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) @@ -1340,25 +1372,25 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie } var ( - pattern_Query_IncentivizedPackets_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"ibc", "apps", "fee", "v1", "incentivized_packets"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_IncentivizedPackets_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"ibc", "apps", "fee", "v1", "incentivized_packets"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_IncentivizedPacket_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 2, 8, 1, 0, 4, 1, 5, 9, 2, 10}, []string{"ibc", "apps", "fee", "v1", "channels", "packet_id.channel_id", "ports", "packet_id.port_id", "sequences", "packet_id.sequence", "incentivized_packet"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_IncentivizedPacket_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 2, 8, 1, 0, 4, 1, 5, 9, 2, 10}, []string{"ibc", "apps", "fee", "v1", "channels", "packet_id.channel_id", "ports", "packet_id.port_id", "sequences", "packet_id.sequence", "incentivized_packet"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_IncentivizedPacketsForChannel_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 2, 8}, []string{"ibc", "apps", "fee", "v1", "channels", "channel_id", "ports", "port_id", "incentivized_packets"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_IncentivizedPacketsForChannel_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 2, 8}, []string{"ibc", "apps", "fee", "v1", "channels", "channel_id", "ports", "port_id", "incentivized_packets"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_TotalRecvFees_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 2, 8, 1, 0, 4, 1, 5, 9, 2, 10}, []string{"ibc", "apps", "fee", "v1", "channels", "packet_id.channel_id", "ports", "packet_id.port_id", "sequences", "packet_id.sequence", "total_recv_fees"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_TotalRecvFees_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 2, 8, 1, 0, 4, 1, 5, 9, 2, 10}, []string{"ibc", "apps", "fee", "v1", "channels", "packet_id.channel_id", "ports", "packet_id.port_id", "sequences", "packet_id.sequence", "total_recv_fees"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_TotalAckFees_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 2, 8, 1, 0, 4, 1, 5, 9, 2, 10}, []string{"ibc", "apps", "fee", "v1", "channels", "packet_id.channel_id", "ports", "packet_id.port_id", "sequences", "packet_id.sequence", "total_ack_fees"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_TotalAckFees_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 2, 8, 1, 0, 4, 1, 5, 9, 2, 10}, []string{"ibc", "apps", "fee", "v1", "channels", "packet_id.channel_id", "ports", "packet_id.port_id", "sequences", "packet_id.sequence", "total_ack_fees"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_TotalTimeoutFees_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 2, 8, 1, 0, 4, 1, 5, 9, 2, 10}, []string{"ibc", "apps", "fee", "v1", "channels", "packet_id.channel_id", "ports", "packet_id.port_id", "sequences", "packet_id.sequence", "total_timeout_fees"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_TotalTimeoutFees_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 2, 8, 1, 0, 4, 1, 5, 9, 2, 10}, []string{"ibc", "apps", "fee", "v1", "channels", "packet_id.channel_id", "ports", "packet_id.port_id", "sequences", "packet_id.sequence", "total_timeout_fees"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_Payee_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 2, 8}, []string{"ibc", "apps", "fee", "v1", "channels", "channel_id", "relayers", "relayer", "payee"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_Payee_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 2, 8}, []string{"ibc", "apps", "fee", "v1", "channels", "channel_id", "relayers", "relayer", "payee"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_CounterpartyPayee_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 2, 8}, []string{"ibc", "apps", "fee", "v1", "channels", "channel_id", "relayers", "relayer", "counterparty_payee"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_CounterpartyPayee_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 2, 8}, []string{"ibc", "apps", "fee", "v1", "channels", "channel_id", "relayers", "relayer", "counterparty_payee"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_FeeEnabledChannels_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"ibc", "apps", "fee", "v1", "fee_enabled"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_FeeEnabledChannels_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"ibc", "apps", "fee", "v1", "fee_enabled"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_FeeEnabledChannel_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 2, 8}, []string{"ibc", "apps", "fee", "v1", "channels", "channel_id", "ports", "port_id", "fee_enabled"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_FeeEnabledChannel_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 2, 8}, []string{"ibc", "apps", "fee", "v1", "channels", "channel_id", "ports", "port_id", "fee_enabled"}, "", runtime.AssumeColonVerbOpt(false))) ) var ( diff --git a/modules/apps/29-fee/types/tx.pb.go b/modules/apps/29-fee/types/tx.pb.go index 4eec969d2c2..721250beacc 100644 --- a/modules/apps/29-fee/types/tx.pb.go +++ b/modules/apps/29-fee/types/tx.pb.go @@ -6,10 +6,10 @@ package types import ( context "context" fmt "fmt" - types "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" - _ "github.com/gogo/protobuf/gogoproto" - grpc1 "github.com/gogo/protobuf/grpc" - proto "github.com/gogo/protobuf/proto" + _ "github.com/cosmos/gogoproto/gogoproto" + grpc1 "github.com/cosmos/gogoproto/grpc" + proto "github.com/cosmos/gogoproto/proto" + types "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" @@ -372,51 +372,51 @@ func init() { func init() { proto.RegisterFile("ibc/applications/fee/v1/tx.proto", fileDescriptor_05c93128649f1b96) } var fileDescriptor_05c93128649f1b96 = []byte{ - // 698 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x55, 0xcf, 0x6e, 0xd3, 0x4c, - 0x10, 0x8f, 0x9b, 0xfe, 0xcb, 0xb4, 0x5f, 0xdb, 0xac, 0xda, 0xaf, 0x8e, 0xd5, 0xc6, 0xfd, 0xac, - 0x4f, 0xa8, 0x08, 0xd5, 0x26, 0xa1, 0x3d, 0x50, 0x09, 0x21, 0x5c, 0xa9, 0xa2, 0x12, 0x15, 0x91, - 0xc5, 0x09, 0x21, 0x55, 0x8e, 0xb3, 0x71, 0x0d, 0x89, 0xd7, 0xf2, 0x3a, 0x11, 0x7e, 0x03, 0x8e, - 0xe5, 0x0d, 0xfa, 0x06, 0xbc, 0x46, 0x8f, 0x3d, 0x70, 0xe0, 0x64, 0xa1, 0xf6, 0xc2, 0x0d, 0x29, - 0xbc, 0x00, 0x5a, 0x7b, 0x6d, 0x9c, 0x44, 0xa9, 0x02, 0x27, 0x6e, 0x3b, 0x33, 0xbf, 0xf9, 0xed, - 0xcc, 0xcf, 0x33, 0x5e, 0xd8, 0x71, 0x9a, 0x96, 0x66, 0x7a, 0x5e, 0xc7, 0xb1, 0xcc, 0xc0, 0x21, - 0x2e, 0xd5, 0xda, 0x18, 0x6b, 0xfd, 0x9a, 0x16, 0xbc, 0x57, 0x3d, 0x9f, 0x04, 0x04, 0x6d, 0x3a, - 0x4d, 0x4b, 0xcd, 0x23, 0xd4, 0x36, 0xc6, 0x6a, 0xbf, 0x26, 0xad, 0xdb, 0xc4, 0x26, 0x31, 0x46, - 0x63, 0xa7, 0x04, 0x2e, 0xfd, 0x37, 0x89, 0x90, 0x65, 0xe5, 0x20, 0x16, 0xf1, 0xb1, 0x66, 0x9d, - 0x9b, 0xae, 0x8b, 0x3b, 0x2c, 0xcc, 0x8f, 0x09, 0x44, 0xf9, 0x24, 0xc0, 0xda, 0x29, 0xb5, 0x0d, - 0x6c, 0x3b, 0x34, 0xc0, 0x7e, 0xc3, 0x0c, 0x31, 0x46, 0x0f, 0x60, 0xc1, 0x23, 0x7e, 0x70, 0xe6, - 0xb4, 0x44, 0x61, 0x47, 0xd8, 0x2d, 0xe9, 0x68, 0x10, 0xc9, 0x2b, 0xa1, 0xd9, 0xed, 0x1c, 0x2a, - 0x3c, 0xa0, 0x18, 0xf3, 0xec, 0x74, 0xd2, 0x42, 0xfb, 0x00, 0x9c, 0x92, 0xe1, 0x67, 0x62, 0xfc, - 0xc6, 0x20, 0x92, 0xcb, 0x09, 0xfe, 0x57, 0x4c, 0x31, 0x4a, 0xdc, 0x38, 0x69, 0x21, 0x11, 0x16, - 0x7c, 0xdc, 0x31, 0x43, 0xec, 0x8b, 0x45, 0x96, 0x62, 0xa4, 0x26, 0x5a, 0x87, 0x39, 0x8f, 0x55, - 0x21, 0xce, 0xc6, 0xfe, 0xc4, 0x38, 0x5c, 0xfc, 0x70, 0x29, 0x17, 0xbe, 0x5d, 0xca, 0x05, 0x45, - 0x02, 0x71, 0xb4, 0x60, 0x03, 0x53, 0x8f, 0xb8, 0x14, 0x2b, 0x3f, 0x04, 0xd8, 0xca, 0x05, 0x8f, - 0x48, 0xcf, 0x0d, 0xb0, 0xef, 0x99, 0x7e, 0x10, 0xfe, 0x05, 0x9d, 0xbd, 0x00, 0x64, 0xe5, 0x2a, - 0x3a, 0xcb, 0xb5, 0xa9, 0x6f, 0x0f, 0x22, 0xb9, 0xc2, 0x79, 0xc7, 0x30, 0x8a, 0x51, 0xb6, 0x46, - 0x5b, 0xc9, 0x29, 0x72, 0x0f, 0xfe, 0xbf, 0xab, 0xe9, 0x4c, 0x9d, 0x8b, 0x19, 0x58, 0x3d, 0xa5, - 0x76, 0xc3, 0x0c, 0x1b, 0xa6, 0xf5, 0x0e, 0x07, 0xc7, 0x18, 0xa3, 0x7d, 0x28, 0xb6, 0x31, 0x8e, - 0xc5, 0x58, 0xaa, 0x6f, 0xa9, 0x13, 0x46, 0x50, 0x3d, 0xc6, 0x58, 0x9f, 0xbd, 0x8a, 0xe4, 0x82, - 0xc1, 0xe0, 0xe8, 0x29, 0xac, 0x50, 0xd2, 0xf3, 0x2d, 0x7c, 0x96, 0xaa, 0x99, 0xa8, 0x53, 0x19, - 0x44, 0xf2, 0x46, 0xd2, 0xc5, 0x70, 0x5c, 0x31, 0x96, 0x13, 0x47, 0x23, 0x91, 0xf6, 0x39, 0x94, - 0x39, 0x20, 0xa7, 0x70, 0x2c, 0x97, 0xbe, 0x35, 0x88, 0x64, 0x71, 0x88, 0x23, 0x2f, 0xf4, 0x6a, - 0xe2, 0x3b, 0xca, 0xe4, 0xfe, 0x17, 0xe6, 0xa9, 0x63, 0xbb, 0xd8, 0xe7, 0xf3, 0xc2, 0x2d, 0x24, - 0xc1, 0x22, 0xd7, 0x9d, 0x8a, 0x73, 0x3b, 0xc5, 0xdd, 0x92, 0x91, 0xd9, 0x39, 0xe9, 0x2a, 0xb0, - 0x39, 0xa2, 0x48, 0xa6, 0xd6, 0x67, 0x01, 0xd6, 0x47, 0x62, 0xcf, 0x68, 0xe8, 0x5a, 0xe8, 0x15, - 0x94, 0xbc, 0xd8, 0x93, 0x4e, 0xd1, 0x52, 0x7d, 0x3b, 0x16, 0x8e, 0x6d, 0x9a, 0x9a, 0xae, 0x57, - 0xbf, 0xa6, 0x26, 0x79, 0x27, 0x2d, 0x5d, 0x64, 0xca, 0x0d, 0x22, 0x79, 0x8d, 0x0f, 0x5a, 0x9a, - 0xad, 0x18, 0x8b, 0x1e, 0xc7, 0xa0, 0x37, 0x00, 0xdc, 0xcf, 0xbe, 0xc7, 0x4c, 0x4c, 0xab, 0x4c, - 0xfc, 0x1e, 0x59, 0x49, 0x7a, 0x85, 0x73, 0x97, 0x87, 0xb8, 0xdb, 0x6c, 0x68, 0x78, 0x99, 0xc7, - 0x43, 0xc3, 0x52, 0x8d, 0x37, 0x64, 0xac, 0xab, 0xb4, 0xed, 0xfa, 0xf7, 0x22, 0x14, 0x4f, 0xa9, - 0x8d, 0xba, 0xf0, 0xcf, 0xf0, 0x4f, 0xe1, 0xfe, 0xc4, 0x62, 0x46, 0xd7, 0x51, 0xaa, 0x4d, 0x0d, - 0x4d, 0xaf, 0x45, 0x1f, 0x05, 0xa8, 0x4c, 0x5e, 0xdb, 0x83, 0x69, 0x08, 0xc7, 0xd2, 0xa4, 0x27, - 0x7f, 0x94, 0x96, 0xd5, 0xf4, 0x16, 0x96, 0x87, 0x76, 0x65, 0xf7, 0x2e, 0xba, 0x3c, 0x52, 0x7a, - 0x38, 0x2d, 0x32, 0xbb, 0x2b, 0x84, 0xf2, 0xf8, 0xa4, 0xed, 0x4d, 0x4b, 0x13, 0xc3, 0xa5, 0x83, - 0xdf, 0x82, 0xa7, 0x57, 0xeb, 0x2f, 0xaf, 0x6e, 0xaa, 0xc2, 0xf5, 0x4d, 0x55, 0xf8, 0x7a, 0x53, - 0x15, 0x2e, 0x6e, 0xab, 0x85, 0xeb, 0xdb, 0x6a, 0xe1, 0xcb, 0x6d, 0xb5, 0xf0, 0xfa, 0xc0, 0x76, - 0x82, 0xf3, 0x5e, 0x53, 0xb5, 0x48, 0x57, 0xb3, 0x08, 0xed, 0x12, 0xaa, 0x39, 0x4d, 0x6b, 0xcf, - 0x26, 0x5a, 0x7f, 0x5f, 0xeb, 0x92, 0x56, 0xaf, 0x83, 0x29, 0x7b, 0x82, 0xa8, 0x56, 0x7f, 0xbc, - 0xc7, 0x5e, 0x9f, 0x20, 0xf4, 0x30, 0x6d, 0xce, 0xc7, 0x4f, 0xcb, 0xa3, 0x9f, 0x01, 0x00, 0x00, - 0xff, 0xff, 0xe2, 0x94, 0x3a, 0xbf, 0xf3, 0x06, 0x00, 0x00, + // 699 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x55, 0x4f, 0x6b, 0xdb, 0x4a, + 0x10, 0xb7, 0xe2, 0xfc, 0xf3, 0x24, 0x2f, 0x89, 0x97, 0xe4, 0x45, 0x16, 0x89, 0x95, 0x27, 0x1e, + 0x8f, 0x3c, 0x4a, 0xa4, 0xda, 0x4d, 0x28, 0x0d, 0x94, 0x52, 0x05, 0x42, 0x03, 0x0d, 0x35, 0xa2, + 0xa7, 0x52, 0x08, 0xb2, 0xbc, 0x56, 0xd4, 0xda, 0x5a, 0xa1, 0x95, 0x4d, 0xf5, 0x0d, 0x7a, 0x4c, + 0xbf, 0x41, 0xbe, 0x41, 0xbf, 0x46, 0x8e, 0x39, 0xf4, 0xd0, 0x93, 0x28, 0xc9, 0xa5, 0xb7, 0x82, + 0xfb, 0x05, 0xca, 0x4a, 0x2b, 0x55, 0xb6, 0x71, 0x70, 0x7b, 0xea, 0x6d, 0x67, 0xe6, 0x37, 0xbf, + 0x9d, 0xf9, 0x69, 0x46, 0x0b, 0x3b, 0x4e, 0xd3, 0xd2, 0x4c, 0xcf, 0xeb, 0x38, 0x96, 0x19, 0x38, + 0xc4, 0xa5, 0x5a, 0x1b, 0x63, 0xad, 0x5f, 0xd3, 0x82, 0x77, 0xaa, 0xe7, 0x93, 0x80, 0xa0, 0x4d, + 0xa7, 0x69, 0xa9, 0x79, 0x84, 0xda, 0xc6, 0x58, 0xed, 0xd7, 0xa4, 0x75, 0x9b, 0xd8, 0x24, 0xc6, + 0x68, 0xec, 0x94, 0xc0, 0xa5, 0x7f, 0x26, 0x11, 0xb2, 0xac, 0x1c, 0xc4, 0x22, 0x3e, 0xd6, 0xac, + 0x73, 0xd3, 0x75, 0x71, 0x87, 0x85, 0xf9, 0x31, 0x81, 0x28, 0x1f, 0x05, 0x58, 0x3b, 0xa5, 0xb6, + 0x81, 0x6d, 0x87, 0x06, 0xd8, 0x6f, 0x98, 0x21, 0xc6, 0xe8, 0x1e, 0x2c, 0x78, 0xc4, 0x0f, 0xce, + 0x9c, 0x96, 0x28, 0xec, 0x08, 0xbb, 0x25, 0x1d, 0x0d, 0x22, 0x79, 0x25, 0x34, 0xbb, 0x9d, 0x43, + 0x85, 0x07, 0x14, 0x63, 0x9e, 0x9d, 0x4e, 0x5a, 0x68, 0x1f, 0x80, 0x53, 0x32, 0xfc, 0x4c, 0x8c, + 0xdf, 0x18, 0x44, 0x72, 0x39, 0xc1, 0xff, 0x8c, 0x29, 0x46, 0x89, 0x1b, 0x27, 0x2d, 0x24, 0xc2, + 0x82, 0x8f, 0x3b, 0x66, 0x88, 0x7d, 0xb1, 0xc8, 0x52, 0x8c, 0xd4, 0x44, 0xeb, 0x30, 0xe7, 0xb1, + 0x2a, 0xc4, 0xd9, 0xd8, 0x9f, 0x18, 0x87, 0x8b, 0xef, 0x2f, 0xe5, 0xc2, 0xd7, 0x4b, 0xb9, 0xa0, + 0x48, 0x20, 0x8e, 0x16, 0x6c, 0x60, 0xea, 0x11, 0x97, 0x62, 0xe5, 0xbb, 0x00, 0x5b, 0xb9, 0xe0, + 0x11, 0xe9, 0xb9, 0x01, 0xf6, 0x3d, 0xd3, 0x0f, 0xc2, 0x3f, 0xa0, 0xb3, 0xe7, 0x80, 0xac, 0x5c, + 0x45, 0x67, 0xb9, 0x36, 0xf5, 0xed, 0x41, 0x24, 0x57, 0x38, 0xef, 0x18, 0x46, 0x31, 0xca, 0xd6, + 0x68, 0x2b, 0x39, 0x45, 0xfe, 0x83, 0x7f, 0xef, 0x6a, 0x3a, 0x53, 0xe7, 0x62, 0x06, 0x56, 0x4f, + 0xa9, 0xdd, 0x30, 0xc3, 0x86, 0x69, 0xbd, 0xc5, 0xc1, 0x31, 0xc6, 0x68, 0x1f, 0x8a, 0x6d, 0x8c, + 0x63, 0x31, 0x96, 0xea, 0x5b, 0xea, 0x84, 0x11, 0x54, 0x8f, 0x31, 0xd6, 0x67, 0xaf, 0x22, 0xb9, + 0x60, 0x30, 0x38, 0x7a, 0x02, 0x2b, 0x94, 0xf4, 0x7c, 0x0b, 0x9f, 0xa5, 0x6a, 0x26, 0xea, 0x54, + 0x06, 0x91, 0xbc, 0x91, 0x74, 0x31, 0x1c, 0x57, 0x8c, 0xe5, 0xc4, 0xd1, 0x48, 0xa4, 0x7d, 0x06, + 0x65, 0x0e, 0xc8, 0x29, 0x1c, 0xcb, 0xa5, 0x6f, 0x0d, 0x22, 0x59, 0x1c, 0xe2, 0xc8, 0x0b, 0xbd, + 0x9a, 0xf8, 0x8e, 0x32, 0xb9, 0xff, 0x86, 0x79, 0xea, 0xd8, 0x2e, 0xf6, 0xf9, 0xbc, 0x70, 0x0b, + 0x49, 0xb0, 0xc8, 0x75, 0xa7, 0xe2, 0xdc, 0x4e, 0x71, 0xb7, 0x64, 0x64, 0x76, 0x4e, 0xba, 0x0a, + 0x6c, 0x8e, 0x28, 0x92, 0xa9, 0xf5, 0x49, 0x80, 0xf5, 0x91, 0xd8, 0x53, 0x1a, 0xba, 0x16, 0x7a, + 0x09, 0x25, 0x2f, 0xf6, 0xa4, 0x53, 0xb4, 0x54, 0xdf, 0x8e, 0x85, 0x63, 0x9b, 0xa6, 0xa6, 0xeb, + 0xd5, 0xaf, 0xa9, 0x49, 0xde, 0x49, 0x4b, 0x17, 0x99, 0x72, 0x83, 0x48, 0x5e, 0xe3, 0x83, 0x96, + 0x66, 0x2b, 0xc6, 0xa2, 0xc7, 0x31, 0xe8, 0x35, 0x00, 0xf7, 0xb3, 0xef, 0x31, 0x13, 0xd3, 0x2a, + 0x13, 0xbf, 0x47, 0x56, 0x92, 0x5e, 0xe1, 0xdc, 0xe5, 0x21, 0xee, 0x36, 0x1b, 0x1a, 0x5e, 0xe6, + 0xf1, 0xd0, 0xb0, 0x54, 0xe3, 0x0d, 0x19, 0xeb, 0x2a, 0x6d, 0xbb, 0xfe, 0xad, 0x08, 0xc5, 0x53, + 0x6a, 0xa3, 0x2e, 0xfc, 0x35, 0xfc, 0x53, 0xf8, 0x7f, 0x62, 0x31, 0xa3, 0xeb, 0x28, 0xd5, 0xa6, + 0x86, 0xa6, 0xd7, 0xa2, 0x0f, 0x02, 0x54, 0x26, 0xaf, 0xed, 0xc1, 0x34, 0x84, 0x63, 0x69, 0xd2, + 0xe3, 0xdf, 0x4a, 0xcb, 0x6a, 0x7a, 0x03, 0xcb, 0x43, 0xbb, 0xb2, 0x7b, 0x17, 0x5d, 0x1e, 0x29, + 0xdd, 0x9f, 0x16, 0x99, 0xdd, 0x15, 0x42, 0x79, 0x7c, 0xd2, 0xf6, 0xa6, 0xa5, 0x89, 0xe1, 0xd2, + 0xc1, 0x2f, 0xc1, 0xd3, 0xab, 0xf5, 0x17, 0x57, 0x37, 0x55, 0xe1, 0xfa, 0xa6, 0x2a, 0x7c, 0xb9, + 0xa9, 0x0a, 0x17, 0xb7, 0xd5, 0xc2, 0xf5, 0x6d, 0xb5, 0xf0, 0xf9, 0xb6, 0x5a, 0x78, 0x75, 0x60, + 0x3b, 0xc1, 0x79, 0xaf, 0xa9, 0x5a, 0xa4, 0xab, 0x59, 0x84, 0x76, 0x09, 0xd5, 0x9c, 0xa6, 0xb5, + 0x67, 0x13, 0xad, 0xff, 0x50, 0xeb, 0x92, 0x56, 0xaf, 0x83, 0x29, 0x7b, 0x82, 0xa8, 0x56, 0x7f, + 0xb4, 0xc7, 0x5e, 0x9f, 0x20, 0xf4, 0x30, 0x6d, 0xce, 0xc7, 0x4f, 0xcb, 0x83, 0x1f, 0x01, 0x00, + 0x00, 0xff, 0xff, 0xfb, 0xaa, 0xe9, 0x34, 0xf3, 0x06, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. diff --git a/modules/apps/transfer/client/cli/cli.go b/modules/apps/transfer/client/cli/cli.go index de83f4d4736..52102df81a1 100644 --- a/modules/apps/transfer/client/cli/cli.go +++ b/modules/apps/transfer/client/cli/cli.go @@ -1,9 +1,8 @@ package cli import ( - "github.com/spf13/cobra" - "github.com/cosmos/cosmos-sdk/client" + "github.com/spf13/cobra" ) // GetQueryCmd returns the query commands for IBC connections diff --git a/modules/apps/transfer/client/cli/query.go b/modules/apps/transfer/client/cli/query.go index 9f9ce97be71..e87998e5573 100644 --- a/modules/apps/transfer/client/cli/query.go +++ b/modules/apps/transfer/client/cli/query.go @@ -8,7 +8,7 @@ import ( "github.com/cosmos/cosmos-sdk/version" "github.com/spf13/cobra" - "github.com/cosmos/ibc-go/v4/modules/apps/transfer/types" + "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" ) // GetCmdQueryDenomTrace defines the command to query a a denomination trace from a given trace hash or ibc denom. diff --git a/modules/apps/transfer/client/cli/tx.go b/modules/apps/transfer/client/cli/tx.go index cddff4b4d9a..452aa214ae1 100644 --- a/modules/apps/transfer/client/cli/tx.go +++ b/modules/apps/transfer/client/cli/tx.go @@ -13,15 +13,16 @@ import ( "github.com/cosmos/cosmos-sdk/version" "github.com/spf13/cobra" - "github.com/cosmos/ibc-go/v4/modules/apps/transfer/types" - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - channelutils "github.com/cosmos/ibc-go/v4/modules/core/04-channel/client/utils" + "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + channelutils "github.com/cosmos/ibc-go/v7/modules/core/04-channel/client/utils" ) 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 { @@ -111,7 +117,7 @@ corresponding to the counterparty channel. Any timeout set to 0 is disabled.`), } msg := types.NewMsgTransfer( - srcPort, srcChannel, coin, sender, receiver, timeoutHeight, timeoutTimestamp, + srcPort, srcChannel, coin, sender, receiver, timeoutHeight, timeoutTimestamp, memo, ) return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) }, @@ -120,6 +126,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/doc.go b/modules/apps/transfer/doc.go new file mode 100644 index 00000000000..e1e1d79b424 --- /dev/null +++ b/modules/apps/transfer/doc.go @@ -0,0 +1,8 @@ +/* +Package transfer implements the packet data structure, state machine handling logic, +and encoding details for the transfer of fungible tokens over an IBC channel between +two modules on separate chains. +This implementation is based off the ICS 20 specification +(https://github.com/cosmos/ibc/blob/main/spec/app/ics-020-fungible-token-transfer) +*/ +package transfer diff --git a/modules/apps/transfer/ibc_module.go b/modules/apps/transfer/ibc_module.go index 96505e05ee6..3503f51da20 100644 --- a/modules/apps/transfer/ibc_module.go +++ b/modules/apps/transfer/ibc_module.go @@ -5,16 +5,17 @@ import ( "math" "strings" + errorsmod "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - "github.com/cosmos/ibc-go/v4/modules/apps/transfer/keeper" - "github.com/cosmos/ibc-go/v4/modules/apps/transfer/types" - channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" - porttypes "github.com/cosmos/ibc-go/v4/modules/core/05-port/types" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" - ibcexported "github.com/cosmos/ibc-go/v4/modules/core/exported" + ibcerrors "github.com/cosmos/ibc-go/v7/internal/errors" + "github.com/cosmos/ibc-go/v7/modules/apps/transfer/keeper" + "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + porttypes "github.com/cosmos/ibc-go/v7/modules/core/05-port/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + ibcexported "github.com/cosmos/ibc-go/v7/modules/core/exported" ) // IBCModule implements the ICS26 interface for transfer given the transfer keeper. @@ -46,16 +47,16 @@ func ValidateTransferChannelParams( return err } if channelSequence > uint64(math.MaxUint32) { - return sdkerrors.Wrapf(types.ErrMaxTransferChannels, "channel sequence %d is greater than max allowed transfer channels %d", channelSequence, uint64(math.MaxUint32)) + return errorsmod.Wrapf(types.ErrMaxTransferChannels, "channel sequence %d is greater than max allowed transfer channels %d", channelSequence, uint64(math.MaxUint32)) } if order != channeltypes.UNORDERED { - return sdkerrors.Wrapf(channeltypes.ErrInvalidChannelOrdering, "expected %s channel, got %s ", channeltypes.UNORDERED, order) + return errorsmod.Wrapf(channeltypes.ErrInvalidChannelOrdering, "expected %s channel, got %s ", channeltypes.UNORDERED, order) } // Require portID is the portID transfer module is bound to boundPort := keeper.GetPort(ctx) if boundPort != portID { - return sdkerrors.Wrapf(porttypes.ErrInvalidPort, "invalid port: %s, expected %s", portID, boundPort) + return errorsmod.Wrapf(porttypes.ErrInvalidPort, "invalid port: %s, expected %s", portID, boundPort) } return nil @@ -81,7 +82,7 @@ func (im IBCModule) OnChanOpenInit( } if version != types.Version { - return "", sdkerrors.Wrapf(types.ErrInvalidVersion, "got %s, expected %s", version, types.Version) + return "", errorsmod.Wrapf(types.ErrInvalidVersion, "got %s, expected %s", version, types.Version) } // Claim channel capability passed back by IBC module @@ -108,7 +109,7 @@ func (im IBCModule) OnChanOpenTry( } if counterpartyVersion != types.Version { - return "", sdkerrors.Wrapf(types.ErrInvalidVersion, "invalid counterparty version: got: %s, expected %s", counterpartyVersion, types.Version) + return "", errorsmod.Wrapf(types.ErrInvalidVersion, "invalid counterparty version: got: %s, expected %s", counterpartyVersion, types.Version) } // OpenTry must claim the channelCapability that IBC passes into the callback @@ -128,7 +129,7 @@ func (im IBCModule) OnChanOpenAck( counterpartyVersion string, ) error { if counterpartyVersion != types.Version { - return sdkerrors.Wrapf(types.ErrInvalidVersion, "invalid counterparty version: %s, expected %s", counterpartyVersion, types.Version) + return errorsmod.Wrapf(types.ErrInvalidVersion, "invalid counterparty version: %s, expected %s", counterpartyVersion, types.Version) } return nil } @@ -149,7 +150,7 @@ func (im IBCModule) OnChanCloseInit( channelID string, ) error { // Disallow user-initiated channel closing for transfer channels - return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "user cannot close channel") + return errorsmod.Wrap(ibcerrors.ErrInvalidRequest, "user cannot close channel") } // OnChanCloseConfirm implements the IBCModule interface @@ -169,12 +170,14 @@ func (im IBCModule) OnRecvPacket( packet channeltypes.Packet, relayer sdk.AccAddress, ) ibcexported.Acknowledgement { + logger := im.keeper.Logger(ctx) ack := channeltypes.NewResultAcknowledgement([]byte{byte(1)}) var data types.FungibleTokenPacketData var ackErr error if err := types.ModuleCdc.UnmarshalJSON(packet.GetData(), &data); err != nil { - ackErr = sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "cannot unmarshal ICS-20 transfer packet data") + ackErr = errorsmod.Wrapf(ibcerrors.ErrInvalidType, "cannot unmarshal ICS-20 transfer packet data") + logger.Error(fmt.Sprintf("%s sequence %d", ackErr.Error(), packet.Sequence)) ack = channeltypes.NewErrorAcknowledgement(ackErr) } @@ -185,6 +188,9 @@ func (im IBCModule) OnRecvPacket( if err != nil { ack = channeltypes.NewErrorAcknowledgement(err) ackErr = err + logger.Error(fmt.Sprintf("%s sequence %d", ackErr.Error(), packet.Sequence)) + } else { + logger.Info("successfully handled ICS-20 packet sequence: %d", packet.Sequence) } } @@ -194,6 +200,7 @@ func (im IBCModule) OnRecvPacket( 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())), } @@ -221,11 +228,11 @@ func (im IBCModule) OnAcknowledgementPacket( ) error { var ack channeltypes.Acknowledgement if err := types.ModuleCdc.UnmarshalJSON(acknowledgement, &ack); err != nil { - return sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "cannot unmarshal ICS-20 transfer packet acknowledgement: %v", err) + return errorsmod.Wrapf(ibcerrors.ErrUnknownRequest, "cannot unmarshal ICS-20 transfer packet acknowledgement: %v", err) } var data types.FungibleTokenPacketData if err := types.ModuleCdc.UnmarshalJSON(packet.GetData(), &data); err != nil { - return sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "cannot unmarshal ICS-20 transfer packet data: %s", err.Error()) + return errorsmod.Wrapf(ibcerrors.ErrUnknownRequest, "cannot unmarshal ICS-20 transfer packet data: %s", err.Error()) } if err := im.keeper.OnAcknowledgementPacket(ctx, packet, data, ack); err != nil { @@ -240,6 +247,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()), ), ) @@ -272,7 +280,7 @@ func (im IBCModule) OnTimeoutPacket( ) error { var data types.FungibleTokenPacketData if err := types.ModuleCdc.UnmarshalJSON(packet.GetData(), &data); err != nil { - return sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "cannot unmarshal ICS-20 transfer packet data: %s", err.Error()) + return errorsmod.Wrapf(ibcerrors.ErrUnknownRequest, "cannot unmarshal ICS-20 transfer packet data: %s", err.Error()) } // refund tokens if err := im.keeper.OnTimeoutPacket(ctx, packet, data); err != nil { @@ -286,6 +294,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 3ff7b25679e..5e43397596c 100644 --- a/modules/apps/transfer/ibc_module_test.go +++ b/modules/apps/transfer/ibc_module_test.go @@ -5,11 +5,11 @@ import ( capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - "github.com/cosmos/ibc-go/v4/modules/apps/transfer" - "github.com/cosmos/ibc-go/v4/modules/apps/transfer/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" + "github.com/cosmos/ibc-go/v7/modules/apps/transfer" + "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + ibctesting "github.com/cosmos/ibc-go/v7/testing" ) func (suite *TransferTestSuite) TestOnChanOpenInit() { @@ -50,7 +50,7 @@ func (suite *TransferTestSuite) TestOnChanOpenInit() { }, { "invalid version", func() { - channel.Version = "version" + channel.Version = "version" //nolint:goconst }, false, }, { diff --git a/modules/apps/transfer/keeper/MBT_README.md b/modules/apps/transfer/keeper/MBT_README.md index c2a62599f36..564d87bc6af 100644 --- a/modules/apps/transfer/keeper/MBT_README.md +++ b/modules/apps/transfer/keeper/MBT_README.md @@ -1,31 +1,31 @@ -## Token Transfer Model-based Testing Guide +# Token Transfer Model-based Testing Guide -In the process of IBC Audit performed by Informal Systems, we have implemented +In the process of IBC Audit performed by Informal Systems, we have implemented a preliminary set of model-based tests for the ICS-20 Token Transfer implementation. Model-based tests are based on the formal `TLA+` model of the Token transfer relay functions: see [relay.tla](relay_model/relay.tla). -The tests themselves are simple `TLA+` assertions, that describe the desired shape of execution that send or receive tokens; -see [relay_tests.tla](relay_model/relay_tests.tla) for some examples. -To be able to specify test assertions the TLA+ model contains the `history` variable, -which records the whole execution history. +The tests themselves are simple `TLA+` assertions, that describe the desired shape of execution that send or receive tokens; +see [relay_tests.tla](relay_model/relay_tests.tla) for some examples. +To be able to specify test assertions the TLA+ model contains the `history` variable, +which records the whole execution history. So, by way of referring to `history` you simply specify declaratively what execution history you want to see. After you have specified your `TLA+` test, you can run it using [Apalache model checker](https://github.com/informalsystems/apalache). -E.g. for the test `TestUnescrowTokens` run +E.g. for the test `TestUnescrowTokens` run ```bash apalache-mc check --inv=TestUnescrowTokensInv relay_tests.tla ``` - -In case there are no error in the TLA+ model or in the test assertions, this will produce a couple of so-called _counterexamples_. + +In case there are no error in the TLA+ model or in the test assertions, this will produce a couple of so-called _counterexamples_. This is a terminology from the model-checking community; for the testing purposes they can be considered simply as model executions. See the files `counterexample.tla` for human-readable representation, and `counterexample.json` for machine-readable one. -In order to execute the produced test, you need to translate it into another format. -For that translation you need the tool [Jsonatr (JSON Arrifact Translator)](https://github.com/informalsystems/jsonatr). +In order to execute the produced test, you need to translate it into another format. +For that translation you need the tool [Jsonatr (JSON Arrifact Translator)](https://github.com/informalsystems/jsonatr). It performs the translation using this [transformation spec](relay_model/apalache-to-relay-test2.json); -To transform a counterexample into a test, run +To transform a counterexample into a test, run ```bash jsonatr --use apalache-to-relay-test2.json --in counterexample.json --out model_based_tests/YourTestName.json @@ -34,18 +34,16 @@ jsonatr --use apalache-to-relay-test2.json --in counterexample.json --out model_ Now, if you run `go test` in this directory, the file you have produced above should be picked up by the [model-based test driver](mbt_relay_test.go), and executed automatically. - -The easiest way to run Apalache is by -[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`. +The easiest way to run Apalache is by +[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`. To wrap Apalache docker image into an executable you might create the following executable bash script `apalache-mc`: ```bash #!/bin/bash docker run --rm -v $(pwd):/var/apalache apalache/mc $@ -``` - +``` In case of any questions please don't hesitate to contact Andrey Kuprianov (andrey@informal.systems). diff --git a/modules/apps/transfer/keeper/encoding.go b/modules/apps/transfer/keeper/encoding.go index 0dfa979cb91..32f52bd651a 100644 --- a/modules/apps/transfer/keeper/encoding.go +++ b/modules/apps/transfer/keeper/encoding.go @@ -1,7 +1,7 @@ package keeper import ( - "github.com/cosmos/ibc-go/v4/modules/apps/transfer/types" + "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" ) // UnmarshalDenomTrace attempts to decode and return an DenomTrace object from diff --git a/modules/apps/transfer/keeper/genesis.go b/modules/apps/transfer/keeper/genesis.go index a3506e4e22d..ce0a45155e1 100644 --- a/modules/apps/transfer/keeper/genesis.go +++ b/modules/apps/transfer/keeper/genesis.go @@ -5,7 +5,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/ibc-go/v4/modules/apps/transfer/types" + "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" ) // InitGenesis initializes the ibc-transfer state and binds to PortID. diff --git a/modules/apps/transfer/keeper/genesis_test.go b/modules/apps/transfer/keeper/genesis_test.go index e734967863c..3bc35144943 100644 --- a/modules/apps/transfer/keeper/genesis_test.go +++ b/modules/apps/transfer/keeper/genesis_test.go @@ -3,7 +3,7 @@ package keeper_test import ( "fmt" - "github.com/cosmos/ibc-go/v4/modules/apps/transfer/types" + "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" ) func (suite *KeeperTestSuite) TestGenesis() { diff --git a/modules/apps/transfer/keeper/grpc_query.go b/modules/apps/transfer/keeper/grpc_query.go index 5564ef04614..d6a1eaefdaa 100644 --- a/modules/apps/transfer/keeper/grpc_query.go +++ b/modules/apps/transfer/keeper/grpc_query.go @@ -5,14 +5,14 @@ import ( "fmt" "strings" + errorsmod "cosmossdk.io/errors" "github.com/cosmos/cosmos-sdk/store/prefix" sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/types/query" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" - "github.com/cosmos/ibc-go/v4/modules/apps/transfer/types" + "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" ) var _ types.QueryServer = Keeper{} @@ -33,7 +33,7 @@ func (q Keeper) DenomTrace(c context.Context, req *types.QueryDenomTraceRequest) if !found { return nil, status.Error( codes.NotFound, - sdkerrors.Wrap(types.ErrTraceNotFound, req.Hash).Error(), + errorsmod.Wrap(types.ErrTraceNotFound, req.Hash).Error(), ) } @@ -100,7 +100,7 @@ func (q Keeper) DenomHash(c context.Context, req *types.QueryDenomHashRequest) ( if !found { return nil, status.Error( codes.NotFound, - sdkerrors.Wrap(types.ErrTraceNotFound, req.Trace).Error(), + errorsmod.Wrap(types.ErrTraceNotFound, req.Trace).Error(), ) } diff --git a/modules/apps/transfer/keeper/grpc_query_test.go b/modules/apps/transfer/keeper/grpc_query_test.go index 92dfbe24370..ffb4833d25e 100644 --- a/modules/apps/transfer/keeper/grpc_query_test.go +++ b/modules/apps/transfer/keeper/grpc_query_test.go @@ -6,8 +6,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/query" - "github.com/cosmos/ibc-go/v4/modules/apps/transfer/types" - ibctesting "github.com/cosmos/ibc-go/v4/testing" + "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" + ibctesting "github.com/cosmos/ibc-go/v7/testing" ) func (suite *KeeperTestSuite) TestQueryDenomTrace() { @@ -24,8 +24,8 @@ func (suite *KeeperTestSuite) TestQueryDenomTrace() { { "success: correct ibc denom", func() { - expTrace.Path = "transfer/channelToA/transfer/channelToB" - expTrace.BaseDenom = "uatom" + expTrace.Path = "transfer/channelToA/transfer/channelToB" //nolint:goconst + expTrace.BaseDenom = "uatom" //nolint:goconst suite.chainA.GetSimApp().TransferKeeper.SetDenomTrace(suite.chainA.GetContext(), expTrace) req = &types.QueryDenomTraceRequest{ @@ -223,9 +223,7 @@ func (suite *KeeperTestSuite) TestQueryDenomHash() { } func (suite *KeeperTestSuite) TestEscrowAddress() { - var ( - req *types.QueryEscrowAddressRequest - ) + var req *types.QueryEscrowAddressRequest testCases := []struct { msg string diff --git a/modules/apps/transfer/keeper/keeper.go b/modules/apps/transfer/keeper/keeper.go index 2a9c5c16db0..c478b5a6eea 100644 --- a/modules/apps/transfer/keeper/keeper.go +++ b/modules/apps/transfer/keeper/keeper.go @@ -3,36 +3,38 @@ package keeper import ( "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/store/prefix" + storetypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" - capabilitykeeper "github.com/cosmos/cosmos-sdk/x/capability/keeper" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" tmbytes "github.com/tendermint/tendermint/libs/bytes" "github.com/tendermint/tendermint/libs/log" - "github.com/cosmos/ibc-go/v4/modules/apps/transfer/types" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" + "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" + porttypes "github.com/cosmos/ibc-go/v7/modules/core/05-port/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + "github.com/cosmos/ibc-go/v7/modules/core/exported" ) // Keeper defines the IBC fungible transfer keeper type Keeper struct { - storeKey sdk.StoreKey + storeKey storetypes.StoreKey cdc codec.BinaryCodec paramSpace paramtypes.Subspace - ics4Wrapper types.ICS4Wrapper + ics4Wrapper porttypes.ICS4Wrapper channelKeeper types.ChannelKeeper portKeeper types.PortKeeper authKeeper types.AccountKeeper bankKeeper types.BankKeeper - scopedKeeper capabilitykeeper.ScopedKeeper + scopedKeeper exported.ScopedKeeper } // NewKeeper creates a new IBC transfer Keeper instance func NewKeeper( - cdc codec.BinaryCodec, key sdk.StoreKey, paramSpace paramtypes.Subspace, - ics4Wrapper types.ICS4Wrapper, channelKeeper types.ChannelKeeper, portKeeper types.PortKeeper, - authKeeper types.AccountKeeper, bankKeeper types.BankKeeper, scopedKeeper capabilitykeeper.ScopedKeeper, + cdc codec.BinaryCodec, key storetypes.StoreKey, paramSpace paramtypes.Subspace, + ics4Wrapper porttypes.ICS4Wrapper, channelKeeper types.ChannelKeeper, portKeeper types.PortKeeper, + authKeeper types.AccountKeeper, bankKeeper types.BankKeeper, scopedKeeper exported.ScopedKeeper, ) Keeper { // ensure ibc transfer module account is set if addr := authKeeper.GetModuleAddress(types.ModuleName); addr == nil { @@ -59,7 +61,7 @@ func NewKeeper( // Logger returns a module-specific logger. func (k Keeper) Logger(ctx sdk.Context) log.Logger { - return ctx.Logger().With("module", "x/"+host.ModuleName+"-"+types.ModuleName) + return ctx.Logger().With("module", "x/"+exported.ModuleName+"-"+types.ModuleName) } // IsBound checks if the transfer module is already bound to the desired port @@ -91,7 +93,7 @@ func (k Keeper) SetPort(ctx sdk.Context, portID string) { func (k Keeper) GetDenomTrace(ctx sdk.Context, denomTraceHash tmbytes.HexBytes) (types.DenomTrace, bool) { store := prefix.NewStore(ctx.KVStore(k.storeKey), types.DenomTraceKey) bz := store.Get(denomTraceHash) - if bz == nil { + if len(bz) == 0 { return types.DenomTrace{}, false } @@ -129,7 +131,7 @@ func (k Keeper) IterateDenomTraces(ctx sdk.Context, cb func(denomTrace types.Den store := ctx.KVStore(k.storeKey) iterator := sdk.KVStorePrefixIterator(store, types.DenomTraceKey) - defer iterator.Close() + defer sdk.LogDeferred(ctx.Logger(), func() error { return iterator.Close() }) for ; iterator.Valid(); iterator.Next() { denomTrace := k.MustUnmarshalDenomTrace(iterator.Value()) diff --git a/modules/apps/transfer/keeper/keeper_test.go b/modules/apps/transfer/keeper/keeper_test.go index 051f3952234..6e56dbbd4bc 100644 --- a/modules/apps/transfer/keeper/keeper_test.go +++ b/modules/apps/transfer/keeper/keeper_test.go @@ -6,8 +6,8 @@ import ( "github.com/cosmos/cosmos-sdk/baseapp" "github.com/stretchr/testify/suite" - "github.com/cosmos/ibc-go/v4/modules/apps/transfer/types" - ibctesting "github.com/cosmos/ibc-go/v4/testing" + "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" + ibctesting "github.com/cosmos/ibc-go/v7/testing" ) type KeeperTestSuite struct { diff --git a/modules/apps/transfer/keeper/mbt_relay_test.go b/modules/apps/transfer/keeper/mbt_relay_test.go index 16aa831b37d..ef016dff3c3 100644 --- a/modules/apps/transfer/keeper/mbt_relay_test.go +++ b/modules/apps/transfer/keeper/mbt_relay_test.go @@ -7,18 +7,20 @@ package keeper_test import ( "encoding/json" "fmt" - "io/ioutil" + "os" "strconv" "strings" + errorsmod "cosmossdk.io/errors" + "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/tendermint/tendermint/crypto" - "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" + ibcerrors "github.com/cosmos/ibc-go/v7/internal/errors" + "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + ibctesting "github.com/cosmos/ibc-go/v7/testing" ) type TlaBalance struct { @@ -83,10 +85,10 @@ type OwnedCoin struct { } type Balance struct { - Id string + ID string Address string Denom string - Amount sdk.Int + Amount math.Int } func AddressFromString(address string) string { @@ -98,7 +100,7 @@ func AddressFromTla(addr []string) string { panic("failed to convert from TLA+ address: wrong number of address components") } s := "" - if len(addr[0]) == 0 && len(addr[1]) == 0 { + if len(addr[0]) == 0 && len(addr[1]) == 0 { //nolint:gocritic // simple address: id s = addr[2] } else if len(addr[2]) == 0 { @@ -120,7 +122,7 @@ func DenomFromTla(denom []string) string { func BalanceFromTla(balance TlaBalance) Balance { return Balance{ - Id: AddressFromTla(balance.Address), + ID: AddressFromTla(balance.Address), Address: AddressFromString(AddressFromTla(balance.Address)), Denom: DenomFromTla(balance.Denom), Amount: sdk.NewInt(balance.Amount), @@ -145,7 +147,8 @@ func FungibleTokenPacketFromTla(packet TlaFungibleTokenPacket) FungibleTokenPack DenomFromTla(packet.Data.Denom), packet.Data.Amount, AddressFromString(packet.Data.Sender), - AddressFromString(packet.Data.Receiver)), + AddressFromString(packet.Data.Receiver), + ""), } } @@ -163,12 +166,12 @@ func OnRecvPacketTestCaseFromTla(tc TlaOnRecvPacketTestCase) OnRecvPacketTestCas var addressMap = make(map[string]string) type Bank struct { - balances map[OwnedCoin]sdk.Int + balances map[OwnedCoin]math.Int } // Make an empty bank func MakeBank() Bank { - return Bank{balances: make(map[OwnedCoin]sdk.Int)} + return Bank{balances: make(map[OwnedCoin]math.Int)} } // Subtract other bank from this bank @@ -191,7 +194,7 @@ func (bank *Bank) Sub(other *Bank) Bank { } // Set specific bank balance -func (bank *Bank) SetBalance(address string, denom string, amount sdk.Int) { +func (bank *Bank) SetBalance(address string, denom string, amount math.Int) { bank.balances[OwnedCoin{address, denom}] = amount } @@ -199,7 +202,7 @@ func (bank *Bank) SetBalance(address string, denom string, amount sdk.Int) { func (bank *Bank) SetBalances(balances []Balance) { for _, balance := range balances { bank.balances[OwnedCoin{balance.Address, balance.Denom}] = balance.Amount - addressMap[balance.Address] = balance.Id + addressMap[balance.Address] = balance.ID } } @@ -217,7 +220,7 @@ func BankFromBalances(balances []Balance) Bank { coin := OwnedCoin{balance.Address, balance.Denom} if coin != NullCoin() { // ignore null coin bank.balances[coin] = balance.Amount - addressMap[balance.Address] = balance.Id + addressMap[balance.Address] = balance.ID } } return bank @@ -268,27 +271,27 @@ func (suite *KeeperTestSuite) CheckBankBalances(chain *ibctesting.TestChain, ban diff := bankChange.Sub(expectedBankChange) NonZeroString := diff.NonZeroString() if len(NonZeroString) != 0 { - return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, "Unexpected changes in the bank: \n"+NonZeroString) + return errorsmod.Wrap(ibcerrors.ErrInvalidCoins, "Unexpected changes in the bank: \n"+NonZeroString) } return nil } func (suite *KeeperTestSuite) TestModelBasedRelay() { dirname := "model_based_tests/" - files, err := ioutil.ReadDir(dirname) + files, err := os.ReadDir(dirname) if err != nil { panic(fmt.Errorf("Failed to read model-based test files: %w", err)) } - for _, file_info := range files { + for _, fileInfo := range files { tlaTestCases := []TlaOnRecvPacketTestCase{} - if !strings.HasSuffix(file_info.Name(), ".json") { + if !strings.HasSuffix(fileInfo.Name(), ".json") { continue } - jsonBlob, err := ioutil.ReadFile(dirname + file_info.Name()) + jsonBlob, err := os.ReadFile(dirname + fileInfo.Name()) if err != nil { panic(fmt.Errorf("Failed to read JSON test fixture: %w", err)) } - err = json.Unmarshal([]byte(jsonBlob), &tlaTestCases) + err = json.Unmarshal(jsonBlob, &tlaTestCases) if err != nil { panic(fmt.Errorf("Failed to parse JSON test fixture: %w", err)) } @@ -301,7 +304,7 @@ func (suite *KeeperTestSuite) TestModelBasedRelay() { for i, tlaTc := range tlaTestCases { tc := OnRecvPacketTestCaseFromTla(tlaTc) - registerDenom := func() { + registerDenomFn := func() { denomTrace := types.ParseDenomTrace(tc.packet.Data.Denom) traceHash := denomTrace.Hash() if !suite.chainB.GetSimApp().TransferKeeper.HasDenomTrace(suite.chainB.GetContext(), traceHash) { @@ -309,10 +312,10 @@ func (suite *KeeperTestSuite) TestModelBasedRelay() { } } - description := file_info.Name() + " # " + strconv.Itoa(i+1) + description := fileInfo.Name() + " # " + strconv.Itoa(i+1) suite.Run(fmt.Sprintf("Case %s", description), func() { seq := uint64(1) - packet := channeltypes.NewPacket(tc.packet.Data.GetBytes(), seq, tc.packet.SourcePort, tc.packet.SourceChannel, tc.packet.DestPort, tc.packet.DestChannel, clienttypes.NewHeight(0, 100), 0) + packet := channeltypes.NewPacket(tc.packet.Data.GetBytes(), seq, tc.packet.SourcePort, tc.packet.SourceChannel, tc.packet.DestPort, tc.packet.DestChannel, clienttypes.NewHeight(1, 100), 0) bankBefore := BankFromBalances(tc.bankBefore) realBankBefore := BankOfChain(suite.chainB) // First validate the packet itself (mimics what happens when the packet is being sent and/or received) @@ -328,7 +331,7 @@ func (suite *KeeperTestSuite) TestModelBasedRelay() { if err != nil { panic("MBT failed to convert sender address") } - registerDenom() + registerDenomFn() denomTrace := types.ParseDenomTrace(tc.packet.Data.Denom) denom := denomTrace.IBCDenom() err = sdk.ValidateDenom(denom) @@ -337,27 +340,30 @@ func (suite *KeeperTestSuite) TestModelBasedRelay() { if !ok { panic("MBT failed to parse amount from string") } - err = suite.chainB.GetSimApp().TransferKeeper.SendTransfer( - suite.chainB.GetContext(), + msg := types.NewMsgTransfer( tc.packet.SourcePort, tc.packet.SourceChannel, sdk.NewCoin(denom, amount), - sender, + sender.String(), tc.packet.Data.Receiver, - clienttypes.NewHeight(0, 110), - 0) + suite.chainA.GetTimeoutHeight(), 0, // only use timeout height + "", + ) + + _, err = suite.chainB.GetSimApp().TransferKeeper.Transfer(sdk.WrapSDKContext(suite.chainB.GetContext()), msg) + } case "OnRecvPacket": err = suite.chainB.GetSimApp().TransferKeeper.OnRecvPacket(suite.chainB.GetContext(), packet, tc.packet.Data) case "OnTimeoutPacket": - registerDenom() + registerDenomFn() err = suite.chainB.GetSimApp().TransferKeeper.OnTimeoutPacket(suite.chainB.GetContext(), packet, tc.packet.Data) case "OnRecvAcknowledgementResult": err = suite.chainB.GetSimApp().TransferKeeper.OnAcknowledgementPacket( suite.chainB.GetContext(), packet, tc.packet.Data, channeltypes.NewResultAcknowledgement(nil)) case "OnRecvAcknowledgementError": - registerDenom() + registerDenomFn() err = suite.chainB.GetSimApp().TransferKeeper.OnAcknowledgementPacket( suite.chainB.GetContext(), packet, tc.packet.Data, channeltypes.NewErrorAcknowledgement(fmt.Errorf("MBT Error Acknowledgement"))) diff --git a/modules/apps/transfer/keeper/migrations.go b/modules/apps/transfer/keeper/migrations.go index a1f3654f170..c0399a2ad34 100644 --- a/modules/apps/transfer/keeper/migrations.go +++ b/modules/apps/transfer/keeper/migrations.go @@ -5,7 +5,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/ibc-go/v4/modules/apps/transfer/types" + "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" ) // Migrator is a struct for handling in-place store migrations. @@ -20,7 +20,6 @@ func NewMigrator(keeper Keeper) Migrator { // 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, diff --git a/modules/apps/transfer/keeper/migrations_test.go b/modules/apps/transfer/keeper/migrations_test.go index ff7da8ca089..ac0097e617a 100644 --- a/modules/apps/transfer/keeper/migrations_test.go +++ b/modules/apps/transfer/keeper/migrations_test.go @@ -3,18 +3,16 @@ 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" + transferkeeper "github.com/cosmos/ibc-go/v7/modules/apps/transfer/keeper" + transfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" ) func (suite *KeeperTestSuite) TestMigratorMigrateTraces() { - testCases := []struct { msg string malleate func() expectedTraces transfertypes.Traces }{ - { "success: two slashes in base denom", func() { @@ -118,6 +116,6 @@ func (suite *KeeperTestSuite) TestMigratorMigrateTracesCorruptionDetection() { migrator := transferkeeper.NewMigrator(suite.chainA.GetSimApp().TransferKeeper) suite.Panics(func() { - migrator.MigrateTraces(suite.chainA.GetContext()) + migrator.MigrateTraces(suite.chainA.GetContext()) //nolint:errcheck // we shouldn't check the error here because we want to ensure that a panic occurs. }) } diff --git a/modules/apps/transfer/keeper/msg_server.go b/modules/apps/transfer/keeper/msg_server.go index eab992e958f..8eb436ba6ec 100644 --- a/modules/apps/transfer/keeper/msg_server.go +++ b/modules/apps/transfer/keeper/msg_server.go @@ -3,26 +3,40 @@ package keeper import ( "context" + errorsmod "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/ibc-go/v4/modules/apps/transfer/types" + ibcerrors "github.com/cosmos/ibc-go/v7/internal/errors" + "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" ) 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. +// Transfer defines an rpc handler method for MsgTransfer. func (k Keeper) Transfer(goCtx context.Context, msg *types.MsgTransfer) (*types.MsgTransferResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) + if !k.GetSendEnabled(ctx) { + return nil, types.ErrSendDisabled + } + sender, err := sdk.AccAddressFromBech32(msg.Sender) if err != nil { return nil, err } - if err := k.SendTransfer( + + if !k.bankKeeper.IsSendEnabledCoin(ctx, msg.Token) { + return nil, errorsmod.Wrapf(types.ErrSendDisabled, "%s transfers are currently disabled", msg.Token.Denom) + } + + if k.bankKeeper.BlockedAddr(sender) { + return nil, errorsmod.Wrapf(ibcerrors.ErrUnauthorized, "%s is not allowed to send funds", sender) + } + + 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 } @@ -33,6 +47,9 @@ func (k Keeper) Transfer(goCtx context.Context, msg *types.MsgTransfer) (*types. types.EventTypeTransfer, sdk.NewAttribute(sdk.AttributeKeySender, msg.Sender), sdk.NewAttribute(types.AttributeKeyReceiver, msg.Receiver), + sdk.NewAttribute(types.AttributeKeyAmount, msg.Token.Amount.String()), + sdk.NewAttribute(types.AttributeKeyDenom, msg.Token.Denom), + sdk.NewAttribute(types.AttributeKeyMemo, msg.Memo), ), sdk.NewEvent( sdk.EventTypeMessage, @@ -40,5 +57,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..61f5ec9395b --- /dev/null +++ b/modules/apps/transfer/keeper/msg_server_test.go @@ -0,0 +1,127 @@ +package keeper_test + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + ibctesting "github.com/cosmos/ibc-go/v7/testing" + + "github.com/cosmos/ibc-go/v7/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() { + err := suite.chainA.GetSimApp().BankKeeper.SetParams(suite.chainA.GetContext(), + banktypes.Params{ + SendEnabled: []*banktypes.SendEnabled{{Denom: sdk.DefaultBondDenom, Enabled: true}}, + }, + ) + suite.Require().NoError(err) + }, + true, + }, + { + "send transfers disabled", + func() { + suite.chainA.GetSimApp().TransferKeeper.SetParams(suite.chainA.GetContext(), + types.Params{ + SendEnabled: false, + }, + ) + }, + false, + }, + { + "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() { + err := suite.chainA.GetSimApp().BankKeeper.SetParams(suite.chainA.GetContext(), + banktypes.Params{ + SendEnabled: []*banktypes.SendEnabled{{Denom: sdk.DefaultBondDenom, Enabled: false}}, + }, + ) + suite.Require().NoError(err) + }, + false, + }, + { + "channel does not exist", + func() { + msg.SourceChannel = "channel-100" + }, + false, + }, + } + + for _, tc := range testCases { + suite.Run(tc.name, func() { + 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 + "memo", + ) + + tc.malleate() + + ctx := suite.chainA.GetContext() + res, err := suite.chainA.GetSimApp().TransferKeeper.Transfer(sdk.WrapSDKContext(ctx), msg) + + // Verify events + events := ctx.EventManager().Events() + expEvents := ibctesting.EventsMap{ + "ibc_transfer": { + "sender": suite.chainA.SenderAccount.GetAddress().String(), + "receiver": suite.chainB.SenderAccount.GetAddress().String(), + "amount": coin.Amount.String(), + "denom": coin.Denom, + "memo": "memo", + }, + } + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().NotNil(res) + suite.Require().NotEqual(res.Sequence, uint64(0)) + ibctesting.AssertEvents(&suite.Suite, expEvents, events) + } else { + suite.Require().Error(err) + suite.Require().Nil(res) + suite.Require().Len(events, 0) + } + }) + } +} diff --git a/modules/apps/transfer/keeper/params.go b/modules/apps/transfer/keeper/params.go index 9589ae04ec9..c7e69c9311d 100644 --- a/modules/apps/transfer/keeper/params.go +++ b/modules/apps/transfer/keeper/params.go @@ -3,7 +3,7 @@ package keeper import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/ibc-go/v4/modules/apps/transfer/types" + "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" ) // GetSendEnabled retrieves the send enabled boolean from the paramstore diff --git a/modules/apps/transfer/keeper/params_test.go b/modules/apps/transfer/keeper/params_test.go index e52f15cb247..921e857d319 100644 --- a/modules/apps/transfer/keeper/params_test.go +++ b/modules/apps/transfer/keeper/params_test.go @@ -1,6 +1,6 @@ package keeper_test -import "github.com/cosmos/ibc-go/v4/modules/apps/transfer/types" +import "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" func (suite *KeeperTestSuite) TestParams() { expParams := types.DefaultParams() diff --git a/modules/apps/transfer/keeper/relay.go b/modules/apps/transfer/keeper/relay.go index 8c023ebac5a..62d4135e072 100644 --- a/modules/apps/transfer/keeper/relay.go +++ b/modules/apps/transfer/keeper/relay.go @@ -4,19 +4,20 @@ import ( "fmt" "strings" - "github.com/armon/go-metrics" + errorsmod "cosmossdk.io/errors" + metrics "github.com/armon/go-metrics" "github.com/cosmos/cosmos-sdk/telemetry" sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "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" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" - coretypes "github.com/cosmos/ibc-go/v4/modules/core/types" + ibcerrors "github.com/cosmos/ibc-go/v7/internal/errors" + "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + coretypes "github.com/cosmos/ibc-go/v7/modules/core/types" ) -// SendTransfer handles transfer sending logic. There are 2 possible cases: +// sendTransfer handles transfer sending logic. There are 2 possible cases: // // 1. Sender chain is acting as the source zone. The coins are transferred // to an escrow address (i.e locked) on the sender chain and then transferred @@ -48,7 +49,7 @@ 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' -func (k Keeper) SendTransfer( +func (k Keeper) sendTransfer( ctx sdk.Context, sourcePort, sourceChannel string, @@ -57,33 +58,21 @@ func (k Keeper) SendTransfer( receiver string, timeoutHeight clienttypes.Height, timeoutTimestamp uint64, -) error { - if !k.GetSendEnabled(ctx) { - return types.ErrSendDisabled - } - - sourceChannelEnd, found := k.channelKeeper.GetChannel(ctx, sourcePort, sourceChannel) + memo string, +) (uint64, error) { + channel, found := k.channelKeeper.GetChannel(ctx, sourcePort, sourceChannel) if !found { - return sdkerrors.Wrapf(channeltypes.ErrChannelNotFound, "port ID (%s) channel ID (%s)", sourcePort, sourceChannel) + return 0, errorsmod.Wrapf(channeltypes.ErrChannelNotFound, "port ID (%s) channel ID (%s)", sourcePort, sourceChannel) } - destinationPort := sourceChannelEnd.GetCounterparty().GetPortID() - destinationChannel := sourceChannelEnd.GetCounterparty().GetChannelID() - - // get the next sequence - sequence, found := k.channelKeeper.GetNextSequenceSend(ctx, sourcePort, sourceChannel) - if !found { - return sdkerrors.Wrapf( - channeltypes.ErrSequenceSendNotFound, - "source port: %s, source channel: %s", sourcePort, sourceChannel, - ) - } + destinationPort := channel.GetCounterparty().GetPortID() + destinationChannel := channel.GetCounterparty().GetChannelID() // begin createOutgoingPacket logic // 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, errorsmod.Wrap(channeltypes.ErrChannelCapabilityNotFound, "module does not own channel capability") } // NOTE: denomination and hex hash correctness checked during msg.ValidateBasic @@ -96,7 +85,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,9 +108,8 @@ func (k Keeper) SendTransfer( if err := k.bankKeeper.SendCoins( ctx, sender, escrowAddress, sdk.NewCoins(token), ); err != nil { - return err + return 0, err } - } else { labels = append(labels, telemetry.NewLabel(coretypes.LabelSource, "false")) @@ -129,7 +117,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( @@ -143,22 +131,12 @@ func (k Keeper) SendTransfer( } packetData := types.NewFungibleTokenPacketData( - fullDenomPath, token.Amount.String(), sender.String(), receiver, - ) - - packet := channeltypes.NewPacket( - packetData.GetBytes(), - sequence, - sourcePort, - sourceChannel, - destinationPort, - destinationChannel, - timeoutHeight, - timeoutTimestamp, + fullDenomPath, token.Amount.String(), sender.String(), receiver, memo, ) - if err := k.ics4Wrapper.SendPacket(ctx, channelCap, packet); err != nil { - return err + sequence, err := k.ics4Wrapper.SendPacket(ctx, channelCap, sourcePort, sourceChannel, timeoutHeight, timeoutTimestamp, packetData.GetBytes()) + if err != nil { + return 0, err } defer func() { @@ -177,7 +155,7 @@ func (k Keeper) SendTransfer( ) }() - return nil + return sequence, nil } // OnRecvPacket processes a cross chain fungible token transfer. If the @@ -188,7 +166,7 @@ func (k Keeper) SendTransfer( func (k Keeper) OnRecvPacket(ctx sdk.Context, packet channeltypes.Packet, data types.FungibleTokenPacketData) error { // validate packet data upon receiving if err := data.ValidateBasic(); err != nil { - return err + return errorsmod.Wrapf(err, "error validating ICS-20 transfer packet data") } if !k.GetReceiveEnabled(ctx) { @@ -198,13 +176,13 @@ func (k Keeper) OnRecvPacket(ctx sdk.Context, packet channeltypes.Packet, data t // decode the receiver address receiver, err := sdk.AccAddressFromBech32(data.Receiver) if err != nil { - return err + return errorsmod.Wrapf(err, "failed to decode receiver address: %s", data.Receiver) } // parse the transfer amount transferAmount, ok := sdk.NewIntFromString(data.Amount) if !ok { - return sdkerrors.Wrapf(types.ErrInvalidAmount, "unable to parse transfer amount (%s) into sdk.Int", data.Amount) + return errorsmod.Wrapf(types.ErrInvalidAmount, "unable to parse transfer amount: %s", data.Amount) } labels := []metrics.Label{ @@ -239,7 +217,7 @@ func (k Keeper) OnRecvPacket(ctx sdk.Context, packet channeltypes.Packet, data t token := sdk.NewCoin(denom, transferAmount) if k.bankKeeper.BlockedAddr(receiver) { - return sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "%s is not allowed to receive funds", receiver) + return errorsmod.Wrapf(ibcerrors.ErrUnauthorized, "%s is not allowed to receive funds", receiver) } // unescrow tokens @@ -249,7 +227,7 @@ func (k Keeper) OnRecvPacket(ctx sdk.Context, packet channeltypes.Packet, data t // counterparty module. The bug may occur in bank or any part of the code that allows // the escrow address to be drained. A malicious counterparty module could drain the // escrow address by allowing more tokens to be sent back then were escrowed. - return sdkerrors.Wrap(err, "unable to unescrow tokens, this may be caused by a malicious counterparty module or a bug: please open an issue on counterparty module") + return errorsmod.Wrap(err, "unable to unescrow tokens, this may be caused by a malicious counterparty module or a bug: please open an issue on counterparty module") } defer func() { @@ -302,14 +280,14 @@ func (k Keeper) OnRecvPacket(ctx sdk.Context, packet channeltypes.Packet, data t if err := k.bankKeeper.MintCoins( ctx, types.ModuleName, sdk.NewCoins(voucher), ); err != nil { - return err + return errorsmod.Wrap(err, "failed to mint IBC tokens") } // send to receiver if err := k.bankKeeper.SendCoinsFromModuleToAccount( ctx, types.ModuleName, receiver, sdk.NewCoins(voucher), ); err != nil { - return err + return errorsmod.Wrapf(err, "failed to send coins to receiver %s", receiver.String()) } defer func() { @@ -367,7 +345,7 @@ func (k Keeper) refundPacketToken(ctx sdk.Context, packet channeltypes.Packet, d // parse the transfer amount transferAmount, ok := sdk.NewIntFromString(data.Amount) if !ok { - return sdkerrors.Wrapf(types.ErrInvalidAmount, "unable to parse transfer amount (%s) into sdk.Int", data.Amount) + return errorsmod.Wrapf(types.ErrInvalidAmount, "unable to parse transfer amount (%s) into math.Int", data.Amount) } token := sdk.NewCoin(trace.IBCDenom(), transferAmount) @@ -385,7 +363,7 @@ func (k Keeper) refundPacketToken(ctx sdk.Context, packet channeltypes.Packet, d // counterparty module. The bug may occur in bank or any part of the code that allows // the escrow address to be drained. A malicious counterparty module could drain the // escrow address by allowing more tokens to be sent back then were escrowed. - return sdkerrors.Wrap(err, "unable to unescrow tokens, this may be caused by a malicious counterparty module or a bug: please open an issue on counterparty module") + return errorsmod.Wrap(err, "unable to unescrow tokens, this may be caused by a malicious counterparty module or a bug: please open an issue on counterparty module") } return nil @@ -413,12 +391,12 @@ func (k Keeper) DenomPathFromHash(ctx sdk.Context, denom string) (string, error) hash, err := types.ParseHexHash(hexHash) if err != nil { - return "", sdkerrors.Wrap(types.ErrInvalidDenomForTransfer, err.Error()) + return "", errorsmod.Wrap(types.ErrInvalidDenomForTransfer, err.Error()) } denomTrace, found := k.GetDenomTrace(ctx, hash) if !found { - return "", sdkerrors.Wrap(types.ErrTraceNotFound, hexHash) + return "", errorsmod.Wrap(types.ErrTraceNotFound, hexHash) } fullDenomPath := denomTrace.GetFullDenomPath() diff --git a/modules/apps/transfer/keeper/relay_test.go b/modules/apps/transfer/keeper/relay_test.go index ac60ed3964b..a09a73037b1 100644 --- a/modules/apps/transfer/keeper/relay_test.go +++ b/modules/apps/transfer/keeper/relay_test.go @@ -3,142 +3,147 @@ package keeper_test import ( "fmt" + "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" + banktestutil "github.com/cosmos/cosmos-sdk/x/bank/testutil" - "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" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" - ibctesting "github.com/cosmos/ibc-go/v4/testing" - "github.com/cosmos/ibc-go/v4/testing/simapp" + "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + ibctesting "github.com/cosmos/ibc-go/v7/testing" ) // test sending from chainA to chainB using both coin that orignate on // chainA and coin that orignate on chainB func (suite *KeeperTestSuite) TestSendTransfer() { var ( - amount sdk.Coin - path *ibctesting.Path - err error + coin sdk.Coin + path *ibctesting.Path + sender sdk.AccAddress + timeoutHeight clienttypes.Height + memo string ) testCases := []struct { - msg string - malleate func() - sendFromSource bool - expPass bool + name string + malleate func() + expPass bool }{ { - "successful transfer from source chain", + "successful transfer with native token", + func() {}, true, + }, + { + "successful transfer from source chain with memo", func() { - suite.coordinator.CreateTransferChannels(path) - amount = sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100)) - }, true, true, + memo = "memo" //nolint:goconst + }, true, }, { - "successful transfer with coin from counterparty chain", + "successful transfer with IBC token", + func() { - // send coin from chainA back to chainB - suite.coordinator.CreateTransferChannels(path) - amount = types.GetTransferCoin(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, sdk.DefaultBondDenom, sdk.NewInt(100)) - }, false, true, + // send IBC token back to chainB + coin = types.GetTransferCoin(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, coin.Denom, coin.Amount) + }, true, + }, + { + "successful transfer with IBC token and memo", + func() { + // send IBC token back to chainB + coin = types.GetTransferCoin(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, coin.Denom, coin.Amount) + memo = "memo" + }, true, }, { "source channel not found", func() { // channel references wrong ID - suite.coordinator.CreateTransferChannels(path) path.EndpointA.ChannelID = ibctesting.InvalidID - amount = sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100)) - }, true, false, + }, false, }, { - "next seq send not found", + "transfer failed - sender account is blocked", func() { - path.EndpointA.ChannelID = "channel-0" - path.EndpointB.ChannelID = "channel-0" - // manually create channel so next seq send is never set - suite.chainA.App.GetIBCKeeper().ChannelKeeper.SetChannel( - suite.chainA.GetContext(), - path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, - channeltypes.NewChannel(channeltypes.OPEN, channeltypes.ORDERED, channeltypes.NewCounterparty(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID), []string{path.EndpointA.ConnectionID}, ibctesting.DefaultChannelVersion), - ) - suite.chainA.CreateChannelCapability(suite.chainA.GetSimApp().ScopedIBCMockKeeper, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) - amount = sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100)) - }, true, false, + sender = suite.chainA.GetSimApp().AccountKeeper.GetModuleAddress(types.ModuleName) + }, false, }, - - // createOutgoingPacket tests - // - source chain { "send coin failed", func() { - suite.coordinator.CreateTransferChannels(path) - amount = sdk.NewCoin("randomdenom", sdk.NewInt(100)) - }, true, false, + coin = sdk.NewCoin("randomdenom", sdk.NewInt(100)) + }, false, }, - // - receiving chain { - "send from module account failed", + "failed to parse coin denom", func() { - suite.coordinator.CreateTransferChannels(path) - amount = types.GetTransferCoin(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, " randomdenom", sdk.NewInt(100)) - }, false, false, + coin = types.GetTransferCoin(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, "randomdenom", coin.Amount) + }, false, + }, + { + "send from module account failed, insufficient balance", + func() { + coin = types.GetTransferCoin(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, coin.Denom, coin.Amount.Add(sdk.NewInt(1))) + }, false, }, { "channel capability not found", func() { - suite.coordinator.CreateTransferChannels(path) cap := suite.chainA.GetChannelCapability(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) // Release channel capability - suite.chainA.GetSimApp().ScopedTransferKeeper.ReleaseCapability(suite.chainA.GetContext(), cap) - amount = sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100)) - }, true, false, + suite.chainA.GetSimApp().ScopedTransferKeeper.ReleaseCapability(suite.chainA.GetContext(), cap) //nolint:errcheck // ignore error for testing + }, false, + }, + { + "SendPacket fails, timeout height and timeout timestamp are zero", + func() { + timeoutHeight = clienttypes.ZeroHeight() + }, false, }, } for _, tc := range testCases { - tc := tc - - suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + suite.Run(tc.name, func() { suite.SetupTest() // reset + path = NewTransferPath(suite.chainA, suite.chainB) - suite.coordinator.SetupConnections(path) + suite.coordinator.Setup(path) - tc.malleate() + coin = sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100)) + sender = suite.chainA.SenderAccount.GetAddress() + memo = "" + timeoutHeight = suite.chainB.GetTimeoutHeight() - if !tc.sendFromSource { - // send coin from chainB to chainA - coinFromBToA := sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100)) - transferMsg := types.NewMsgTransfer(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, coinFromBToA, suite.chainB.SenderAccount.GetAddress().String(), suite.chainA.SenderAccount.GetAddress().String(), clienttypes.NewHeight(0, 110), 0) - _, err = suite.chainB.SendMsgs(transferMsg) - suite.Require().NoError(err) // message committed + // create IBC token on chainA + transferMsg := types.NewMsgTransfer(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, coin, suite.chainB.SenderAccount.GetAddress().String(), suite.chainA.SenderAccount.GetAddress().String(), suite.chainA.GetTimeoutHeight(), 0, "") + result, err := suite.chainB.SendMsgs(transferMsg) + suite.Require().NoError(err) // message committed - // receive coin on chainA from chainB - fungibleTokenPacket := types.NewFungibleTokenPacketData(coinFromBToA.Denom, coinFromBToA.Amount.String(), suite.chainB.SenderAccount.GetAddress().String(), suite.chainA.SenderAccount.GetAddress().String()) - packet := channeltypes.NewPacket(fungibleTokenPacket.GetBytes(), 1, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, clienttypes.NewHeight(0, 110), 0) + packet, err := ibctesting.ParsePacketFromEvents(result.GetEvents()) + suite.Require().NoError(err) - // get proof of packet commitment from chainB - err = path.EndpointA.UpdateClient() - suite.Require().NoError(err) - packetKey := host.PacketCommitmentKey(packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) - proof, proofHeight := path.EndpointB.QueryProof(packetKey) + err = path.RelayPacket(packet) + suite.Require().NoError(err) - recvMsg := channeltypes.NewMsgRecvPacket(packet, proof, proofHeight, suite.chainA.SenderAccount.GetAddress().String()) - _, err = suite.chainA.SendMsgs(recvMsg) - suite.Require().NoError(err) // message committed - } + tc.malleate() - 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, + msg := types.NewMsgTransfer( + path.EndpointA.ChannelConfig.PortID, + path.EndpointA.ChannelID, + coin, sender.String(), suite.chainB.SenderAccount.GetAddress().String(), + timeoutHeight, 0, // only use timeout height + memo, ) + res, err := suite.chainA.GetSimApp().TransferKeeper.Transfer(sdk.WrapSDKContext(suite.chainA.GetContext()), msg) + if tc.expPass { suite.Require().NoError(err) + suite.Require().NotNil(res) } else { suite.Require().Error(err) + suite.Require().Nil(res) } }) } @@ -151,8 +156,9 @@ func (suite *KeeperTestSuite) TestSendTransfer() { func (suite *KeeperTestSuite) TestOnRecvPacket() { var ( trace types.DenomTrace - amount sdk.Int + amount math.Int receiver string + memo string ) testCases := []struct { @@ -162,7 +168,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,13 +215,14 @@ 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) if tc.recvIsSource { // send coin from chainB to chainA, receive them, acknowledge them, and send back to chainB coinFromBToA := sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100)) - transferMsg := types.NewMsgTransfer(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, coinFromBToA, suite.chainB.SenderAccount.GetAddress().String(), suite.chainA.SenderAccount.GetAddress().String(), clienttypes.NewHeight(0, 110), 0) + transferMsg := types.NewMsgTransfer(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, coinFromBToA, suite.chainB.SenderAccount.GetAddress().String(), suite.chainA.SenderAccount.GetAddress().String(), clienttypes.NewHeight(1, 110), 0, memo) res, err := suite.chainB.SendMsgs(transferMsg) suite.Require().NoError(err) // message committed @@ -228,14 +241,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 := types.NewMsgTransfer(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, sdk.NewCoin(trace.IBCDenom(), amount), suite.chainA.SenderAccount.GetAddress().String(), receiver, clienttypes.NewHeight(1, 110), 0, 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) - 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) + data := types.NewFungibleTokenPacketData(trace.GetFullDenomPath(), amount.String(), suite.chainA.SenderAccount.GetAddress().String(), receiver, memo) + packet := channeltypes.NewPacket(data.GetBytes(), seq, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, clienttypes.NewHeight(1, 100), 0) err = suite.chainB.GetSimApp().TransferKeeper.OnRecvPacket(suite.chainB.GetContext(), packet, data) @@ -257,7 +270,7 @@ func (suite *KeeperTestSuite) TestOnAcknowledgementPacket() { successAck = channeltypes.NewResultAcknowledgement([]byte{byte(1)}) failedAck = channeltypes.NewErrorAcknowledgement(fmt.Errorf("failed packet transfer")) trace types.DenomTrace - amount sdk.Int + amount math.Int path *ibctesting.Path ) @@ -276,7 +289,7 @@ func (suite *KeeperTestSuite) TestOnAcknowledgementPacket() { trace = types.ParseDenomTrace(sdk.DefaultBondDenom) coin := sdk.NewCoin(sdk.DefaultBondDenom, amount) - suite.Require().NoError(simapp.FundAccount(suite.chainA.GetSimApp(), suite.chainA.GetContext(), escrow, sdk.NewCoins(coin))) + suite.Require().NoError(banktestutil.FundAccount(suite.chainA.GetSimApp().BankKeeper, suite.chainA.GetContext(), escrow, sdk.NewCoins(coin))) }, false, true}, { "unsuccessful refund from source", failedAck, @@ -291,7 +304,7 @@ func (suite *KeeperTestSuite) TestOnAcknowledgementPacket() { trace = types.ParseDenomTrace(types.GetPrefixedDenom(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, sdk.DefaultBondDenom)) coin := sdk.NewCoin(trace.IBCDenom(), amount) - suite.Require().NoError(simapp.FundAccount(suite.chainA.GetSimApp(), suite.chainA.GetContext(), escrow, sdk.NewCoins(coin))) + suite.Require().NoError(banktestutil.FundAccount(suite.chainA.GetSimApp().BankKeeper, suite.chainA.GetContext(), escrow, sdk.NewCoins(coin))) }, false, true, }, } @@ -307,8 +320,8 @@ func (suite *KeeperTestSuite) TestOnAcknowledgementPacket() { tc.malleate() - data := types.NewFungibleTokenPacketData(trace.GetFullDenomPath(), amount.String(), suite.chainA.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String()) - packet := channeltypes.NewPacket(data.GetBytes(), 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, clienttypes.NewHeight(0, 100), 0) + data := types.NewFungibleTokenPacketData(trace.GetFullDenomPath(), amount.String(), suite.chainA.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(), "") + packet := channeltypes.NewPacket(data.GetBytes(), 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, clienttypes.NewHeight(1, 100), 0) preCoin := suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), suite.chainA.SenderAccount.GetAddress(), trace.IBCDenom()) @@ -339,7 +352,7 @@ func (suite *KeeperTestSuite) TestOnTimeoutPacket() { var ( trace types.DenomTrace path *ibctesting.Path - amount sdk.Int + amount math.Int sender string ) @@ -355,7 +368,7 @@ func (suite *KeeperTestSuite) TestOnTimeoutPacket() { trace = types.ParseDenomTrace(sdk.DefaultBondDenom) coin := sdk.NewCoin(trace.IBCDenom(), amount) - suite.Require().NoError(simapp.FundAccount(suite.chainA.GetSimApp(), suite.chainA.GetContext(), escrow, sdk.NewCoins(coin))) + suite.Require().NoError(banktestutil.FundAccount(suite.chainA.GetSimApp().BankKeeper, suite.chainA.GetContext(), escrow, sdk.NewCoins(coin))) }, true, }, { @@ -365,7 +378,7 @@ func (suite *KeeperTestSuite) TestOnTimeoutPacket() { trace = types.ParseDenomTrace(types.GetPrefixedDenom(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, sdk.DefaultBondDenom)) coin := sdk.NewCoin(trace.IBCDenom(), amount) - suite.Require().NoError(simapp.FundAccount(suite.chainA.GetSimApp(), suite.chainA.GetContext(), escrow, sdk.NewCoins(coin))) + suite.Require().NoError(banktestutil.FundAccount(suite.chainA.GetSimApp().BankKeeper, suite.chainA.GetContext(), escrow, sdk.NewCoins(coin))) }, true, }, { @@ -403,8 +416,8 @@ func (suite *KeeperTestSuite) TestOnTimeoutPacket() { tc.malleate() - data := types.NewFungibleTokenPacketData(trace.GetFullDenomPath(), amount.String(), sender, suite.chainB.SenderAccount.GetAddress().String()) - packet := channeltypes.NewPacket(data.GetBytes(), 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, clienttypes.NewHeight(0, 100), 0) + data := types.NewFungibleTokenPacketData(trace.GetFullDenomPath(), amount.String(), sender, suite.chainB.SenderAccount.GetAddress().String(), "") + packet := channeltypes.NewPacket(data.GetBytes(), 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, clienttypes.NewHeight(1, 100), 0) preCoin := suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), suite.chainA.SenderAccount.GetAddress(), trace.IBCDenom()) diff --git a/modules/apps/transfer/module.go b/modules/apps/transfer/module.go index 9503a06d926..fcf5ac7267f 100644 --- a/modules/apps/transfer/module.go +++ b/modules/apps/transfer/module.go @@ -4,7 +4,6 @@ import ( "context" "encoding/json" "fmt" - "math/rand" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/codec" @@ -12,16 +11,15 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" simtypes "github.com/cosmos/cosmos-sdk/types/simulation" - "github.com/gorilla/mux" "github.com/grpc-ecosystem/grpc-gateway/runtime" "github.com/spf13/cobra" abci "github.com/tendermint/tendermint/abci/types" - "github.com/cosmos/ibc-go/v4/modules/apps/transfer/client/cli" - "github.com/cosmos/ibc-go/v4/modules/apps/transfer/keeper" - "github.com/cosmos/ibc-go/v4/modules/apps/transfer/simulation" - "github.com/cosmos/ibc-go/v4/modules/apps/transfer/types" - porttypes "github.com/cosmos/ibc-go/v4/modules/core/05-port/types" + "github.com/cosmos/ibc-go/v7/modules/apps/transfer/client/cli" + "github.com/cosmos/ibc-go/v7/modules/apps/transfer/keeper" + "github.com/cosmos/ibc-go/v7/modules/apps/transfer/simulation" + "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" + porttypes "github.com/cosmos/ibc-go/v7/modules/core/05-port/types" ) var ( @@ -64,13 +62,12 @@ func (AppModuleBasic) ValidateGenesis(cdc codec.JSONCodec, config client.TxEncod return gs.Validate() } -// RegisterRESTRoutes implements AppModuleBasic interface -func (AppModuleBasic) RegisterRESTRoutes(clientCtx client.Context, rtr *mux.Router) { -} - // RegisterGRPCGatewayRoutes registers the gRPC Gateway routes for the ibc-transfer module. func (AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *runtime.ServeMux) { - types.RegisterQueryHandlerClient(context.Background(), mux, types.NewQueryClient(clientCtx)) + err := types.RegisterQueryHandlerClient(context.Background(), mux, types.NewQueryClient(clientCtx)) + if err != nil { + panic(err) + } } // GetTxCmd implements AppModuleBasic interface @@ -101,21 +98,6 @@ func (AppModule) RegisterInvariants(ir sdk.InvariantRegistry) { // TODO } -// Route implements the AppModule interface -func (am AppModule) Route() sdk.Route { - return sdk.Route{} -} - -// QuerierRoute implements the AppModule interface -func (AppModule) QuerierRoute() string { - return types.QuerierRoute -} - -// LegacyQuerierHandler implements the AppModule interface -func (am AppModule) LegacyQuerierHandler(*codec.LegacyAmino) sdk.Querier { - return nil -} - // RegisterServices registers module services. func (am AppModule) RegisterServices(cfg module.Configurator) { types.RegisterMsgServer(cfg.MsgServer(), am.keeper) @@ -162,16 +144,6 @@ func (AppModule) GenerateGenesisState(simState *module.SimulationState) { simulation.RandomizedGenState(simState) } -// ProposalContents doesn't return any content functions for governance proposals. -func (AppModule) ProposalContents(_ module.SimulationState) []simtypes.WeightedProposalContent { - return nil -} - -// RandomizedParams creates randomized ibc-transfer param changes for the simulator. -func (AppModule) RandomizedParams(r *rand.Rand) []simtypes.ParamChange { - return simulation.ParamChanges(r) -} - // RegisterStoreDecoder registers a decoder for transfer module's types func (am AppModule) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) { sdr[types.StoreKey] = simulation.NewDecodeStore(am.keeper) diff --git a/modules/apps/transfer/simulation/decoder.go b/modules/apps/transfer/simulation/decoder.go index 6e1fe37feeb..ea67c509e3d 100644 --- a/modules/apps/transfer/simulation/decoder.go +++ b/modules/apps/transfer/simulation/decoder.go @@ -6,7 +6,7 @@ import ( "github.com/cosmos/cosmos-sdk/types/kv" - "github.com/cosmos/ibc-go/v4/modules/apps/transfer/types" + "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" ) // TransferUnmarshaler defines the expected encoding store functions. diff --git a/modules/apps/transfer/simulation/decoder_test.go b/modules/apps/transfer/simulation/decoder_test.go index 13afd462be7..26d7dbf4b94 100644 --- a/modules/apps/transfer/simulation/decoder_test.go +++ b/modules/apps/transfer/simulation/decoder_test.go @@ -7,9 +7,9 @@ import ( "github.com/cosmos/cosmos-sdk/types/kv" "github.com/stretchr/testify/require" - "github.com/cosmos/ibc-go/v4/modules/apps/transfer/simulation" - "github.com/cosmos/ibc-go/v4/modules/apps/transfer/types" - "github.com/cosmos/ibc-go/v4/testing/simapp" + "github.com/cosmos/ibc-go/v7/modules/apps/transfer/simulation" + "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" + "github.com/cosmos/ibc-go/v7/testing/simapp" ) func TestDecodeStore(t *testing.T) { diff --git a/modules/apps/transfer/simulation/genesis.go b/modules/apps/transfer/simulation/genesis.go index 693864ce12b..220cdddd470 100644 --- a/modules/apps/transfer/simulation/genesis.go +++ b/modules/apps/transfer/simulation/genesis.go @@ -9,7 +9,7 @@ import ( "github.com/cosmos/cosmos-sdk/types/module" simtypes "github.com/cosmos/cosmos-sdk/types/simulation" - "github.com/cosmos/ibc-go/v4/modules/apps/transfer/types" + "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" ) // Simulation parameter constants diff --git a/modules/apps/transfer/simulation/genesis_test.go b/modules/apps/transfer/simulation/genesis_test.go index 5b46c88f9d8..01c933c17a5 100644 --- a/modules/apps/transfer/simulation/genesis_test.go +++ b/modules/apps/transfer/simulation/genesis_test.go @@ -5,20 +5,23 @@ import ( "math/rand" "testing" + "cosmossdk.io/math" "github.com/cosmos/cosmos-sdk/codec" codectypes "github.com/cosmos/cosmos-sdk/codec/types" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" "github.com/cosmos/cosmos-sdk/types/module" simtypes "github.com/cosmos/cosmos-sdk/types/simulation" "github.com/stretchr/testify/require" - "github.com/cosmos/ibc-go/v4/modules/apps/transfer/simulation" - "github.com/cosmos/ibc-go/v4/modules/apps/transfer/types" + "github.com/cosmos/ibc-go/v7/modules/apps/transfer/simulation" + "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" ) // TestRandomizedGenState tests the normal scenario of applying RandomizedGenState. // Abonormal scenarios are not tested here. func TestRandomizedGenState(t *testing.T) { interfaceRegistry := codectypes.NewInterfaceRegistry() + cryptocodec.RegisterInterfaces(interfaceRegistry) cdc := codec.NewProtoCodec(interfaceRegistry) s := rand.NewSource(1) @@ -30,7 +33,7 @@ func TestRandomizedGenState(t *testing.T) { Rand: r, NumBonded: 3, Accounts: simtypes.RandomAccounts(r, 3), - InitialStake: 1000, + InitialStake: math.NewInt(1000), GenState: make(map[string]json.RawMessage), } diff --git a/modules/apps/transfer/simulation/params.go b/modules/apps/transfer/simulation/params.go deleted file mode 100644 index 838015ae498..00000000000 --- a/modules/apps/transfer/simulation/params.go +++ /dev/null @@ -1,31 +0,0 @@ -package simulation - -import ( - "fmt" - "math/rand" - - simtypes "github.com/cosmos/cosmos-sdk/types/simulation" - "github.com/cosmos/cosmos-sdk/x/simulation" - gogotypes "github.com/gogo/protobuf/types" - - "github.com/cosmos/ibc-go/v4/modules/apps/transfer/types" -) - -// ParamChanges defines the parameters that can be modified by param change proposals -// on the simulation -func ParamChanges(r *rand.Rand) []simtypes.ParamChange { - return []simtypes.ParamChange{ - simulation.NewSimParamChange(types.ModuleName, string(types.KeySendEnabled), - func(r *rand.Rand) string { - sendEnabled := RadomEnabled(r) - return fmt.Sprintf("%s", types.ModuleCdc.MustMarshalJSON(&gogotypes.BoolValue{Value: sendEnabled})) - }, - ), - simulation.NewSimParamChange(types.ModuleName, string(types.KeyReceiveEnabled), - func(r *rand.Rand) string { - receiveEnabled := RadomEnabled(r) - return fmt.Sprintf("%s", types.ModuleCdc.MustMarshalJSON(&gogotypes.BoolValue{Value: receiveEnabled})) - }, - ), - } -} diff --git a/modules/apps/transfer/simulation/params_test.go b/modules/apps/transfer/simulation/params_test.go deleted file mode 100644 index 86c16c67c5c..00000000000 --- a/modules/apps/transfer/simulation/params_test.go +++ /dev/null @@ -1,36 +0,0 @@ -package simulation_test - -import ( - "math/rand" - "testing" - - "github.com/stretchr/testify/require" - - "github.com/cosmos/ibc-go/v4/modules/apps/transfer/simulation" -) - -func TestParamChanges(t *testing.T) { - s := rand.NewSource(1) - r := rand.New(s) - - expected := []struct { - composedKey string - key string - simValue string - subspace string - }{ - {"transfer/SendEnabled", "SendEnabled", "false", "transfer"}, - {"transfer/ReceiveEnabled", "ReceiveEnabled", "true", "transfer"}, - } - - paramChanges := simulation.ParamChanges(r) - - require.Len(t, paramChanges, 2) - - for i, p := range paramChanges { - require.Equal(t, expected[i].composedKey, p.ComposedKey()) - require.Equal(t, expected[i].key, p.Key()) - require.Equal(t, expected[i].simValue, p.SimValue()(r), p.Key()) - require.Equal(t, expected[i].subspace, p.Subspace()) - } -} diff --git a/modules/apps/transfer/transfer_test.go b/modules/apps/transfer/transfer_test.go index d1089f0ee82..0d50d3cd0fd 100644 --- a/modules/apps/transfer/transfer_test.go +++ b/modules/apps/transfer/transfer_test.go @@ -6,9 +6,9 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/suite" - "github.com/cosmos/ibc-go/v4/modules/apps/transfer/types" - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - ibctesting "github.com/cosmos/ibc-go/v4/testing" + "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + ibctesting "github.com/cosmos/ibc-go/v7/testing" ) type TransferTestSuite struct { @@ -47,14 +47,14 @@ func (suite *TransferTestSuite) TestHandleMsgTransfer() { suite.coordinator.Setup(path) // originalBalance := suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), suite.chainA.SenderAccount.GetAddress(), sdk.DefaultBondDenom) - timeoutHeight := clienttypes.NewHeight(0, 110) + timeoutHeight := clienttypes.NewHeight(1, 110) amount, ok := sdk.NewIntFromString("9223372036854775808") // 2^63 (one above int64) suite.Require().True(ok) coinToSendToB := sdk.NewCoin(sdk.DefaultBondDenom, amount) // send from chainA to chainB - msg := types.NewMsgTransfer(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, coinToSendToB, suite.chainA.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(), timeoutHeight, 0) + msg := types.NewMsgTransfer(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, coinToSendToB, suite.chainA.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(), timeoutHeight, 0, "") res, err := suite.chainA.SendMsgs(msg) suite.Require().NoError(err) // message committed @@ -80,7 +80,7 @@ func (suite *TransferTestSuite) TestHandleMsgTransfer() { suite.coordinator.Setup(pathBtoC) // send from chainB to chainC - msg = types.NewMsgTransfer(pathBtoC.EndpointA.ChannelConfig.PortID, pathBtoC.EndpointA.ChannelID, coinSentFromAToB, suite.chainB.SenderAccount.GetAddress().String(), suite.chainC.SenderAccount.GetAddress().String(), timeoutHeight, 0) + msg = types.NewMsgTransfer(pathBtoC.EndpointA.ChannelConfig.PortID, pathBtoC.EndpointA.ChannelID, coinSentFromAToB, suite.chainB.SenderAccount.GetAddress().String(), suite.chainC.SenderAccount.GetAddress().String(), timeoutHeight, 0, "") res, err = suite.chainB.SendMsgs(msg) suite.Require().NoError(err) // message committed @@ -104,7 +104,7 @@ func (suite *TransferTestSuite) TestHandleMsgTransfer() { suite.Require().Zero(balance.Amount.Int64()) // send from chainC back to chainB - msg = types.NewMsgTransfer(pathBtoC.EndpointB.ChannelConfig.PortID, pathBtoC.EndpointB.ChannelID, coinSentFromBToC, suite.chainC.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(), timeoutHeight, 0) + msg = types.NewMsgTransfer(pathBtoC.EndpointB.ChannelConfig.PortID, pathBtoC.EndpointB.ChannelID, coinSentFromBToC, suite.chainC.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(), timeoutHeight, 0, "") res, err = suite.chainC.SendMsgs(msg) suite.Require().NoError(err) // message committed diff --git a/modules/apps/transfer/types/authz.pb.go b/modules/apps/transfer/types/authz.pb.go new file mode 100644 index 00000000000..95f24eca332 --- /dev/null +++ b/modules/apps/transfer/types/authz.pb.go @@ -0,0 +1,695 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: ibc/applications/transfer/v1/authz.proto + +package types + +import ( + fmt "fmt" + _ "github.com/cosmos/cosmos-proto" + github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" + types "github.com/cosmos/cosmos-sdk/types" + _ "github.com/cosmos/gogoproto/gogoproto" + proto "github.com/cosmos/gogoproto/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// Allocation defines the spend limit for a particular port and channel +type Allocation struct { + // the port on which the packet will be sent + SourcePort string `protobuf:"bytes,1,opt,name=source_port,json=sourcePort,proto3" json:"source_port,omitempty" yaml:"source_port"` + // the channel by which the packet will be sent + SourceChannel string `protobuf:"bytes,2,opt,name=source_channel,json=sourceChannel,proto3" json:"source_channel,omitempty" yaml:"source_channel"` + // spend limitation on the channel + SpendLimit github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,3,rep,name=spend_limit,json=spendLimit,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"spend_limit"` + // allow list of receivers, an empty allow list permits any receiver address + AllowList []string `protobuf:"bytes,4,rep,name=allow_list,json=allowList,proto3" json:"allow_list,omitempty"` +} + +func (m *Allocation) Reset() { *m = Allocation{} } +func (m *Allocation) String() string { return proto.CompactTextString(m) } +func (*Allocation) ProtoMessage() {} +func (*Allocation) Descriptor() ([]byte, []int) { + return fileDescriptor_b1a28b55d17325aa, []int{0} +} +func (m *Allocation) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Allocation) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Allocation.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 *Allocation) XXX_Merge(src proto.Message) { + xxx_messageInfo_Allocation.Merge(m, src) +} +func (m *Allocation) XXX_Size() int { + return m.Size() +} +func (m *Allocation) XXX_DiscardUnknown() { + xxx_messageInfo_Allocation.DiscardUnknown(m) +} + +var xxx_messageInfo_Allocation proto.InternalMessageInfo + +func (m *Allocation) GetSourcePort() string { + if m != nil { + return m.SourcePort + } + return "" +} + +func (m *Allocation) GetSourceChannel() string { + if m != nil { + return m.SourceChannel + } + return "" +} + +func (m *Allocation) GetSpendLimit() github_com_cosmos_cosmos_sdk_types.Coins { + if m != nil { + return m.SpendLimit + } + return nil +} + +func (m *Allocation) GetAllowList() []string { + if m != nil { + return m.AllowList + } + return nil +} + +// TransferAuthorization allows the grantee to spend up to spend_limit coins from +// the granter's account for ibc transfer on a specific channel +type TransferAuthorization struct { + // port and channel amounts + Allocations []Allocation `protobuf:"bytes,1,rep,name=allocations,proto3" json:"allocations"` +} + +func (m *TransferAuthorization) Reset() { *m = TransferAuthorization{} } +func (m *TransferAuthorization) String() string { return proto.CompactTextString(m) } +func (*TransferAuthorization) ProtoMessage() {} +func (*TransferAuthorization) Descriptor() ([]byte, []int) { + return fileDescriptor_b1a28b55d17325aa, []int{1} +} +func (m *TransferAuthorization) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TransferAuthorization) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_TransferAuthorization.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 *TransferAuthorization) XXX_Merge(src proto.Message) { + xxx_messageInfo_TransferAuthorization.Merge(m, src) +} +func (m *TransferAuthorization) XXX_Size() int { + return m.Size() +} +func (m *TransferAuthorization) XXX_DiscardUnknown() { + xxx_messageInfo_TransferAuthorization.DiscardUnknown(m) +} + +var xxx_messageInfo_TransferAuthorization proto.InternalMessageInfo + +func (m *TransferAuthorization) GetAllocations() []Allocation { + if m != nil { + return m.Allocations + } + return nil +} + +func init() { + proto.RegisterType((*Allocation)(nil), "ibc.applications.transfer.v1.Allocation") + proto.RegisterType((*TransferAuthorization)(nil), "ibc.applications.transfer.v1.TransferAuthorization") +} + +func init() { + proto.RegisterFile("ibc/applications/transfer/v1/authz.proto", fileDescriptor_b1a28b55d17325aa) +} + +var fileDescriptor_b1a28b55d17325aa = []byte{ + // 435 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x52, 0x4d, 0x6e, 0xd3, 0x40, + 0x14, 0x8e, 0x9b, 0x0a, 0x29, 0x13, 0xc1, 0xc2, 0xa2, 0xc8, 0xa9, 0xc0, 0x89, 0xbc, 0x40, 0xde, + 0x64, 0x86, 0xc0, 0x22, 0x52, 0x57, 0x34, 0xdd, 0x76, 0x51, 0x2c, 0x56, 0x6c, 0xa2, 0xf1, 0x64, + 0xb0, 0x47, 0x8c, 0xfd, 0x2c, 0xcf, 0xd8, 0xa8, 0x3d, 0x05, 0x48, 0x9c, 0x82, 0x35, 0x87, 0xa8, + 0x58, 0x75, 0xc9, 0x2a, 0xa0, 0xe4, 0x06, 0x3d, 0x01, 0xf2, 0xcc, 0x14, 0x5c, 0x21, 0xb1, 0xb2, + 0xdf, 0xcf, 0xf7, 0xde, 0xf7, 0xbe, 0xf9, 0x50, 0x2c, 0x52, 0x46, 0x68, 0x55, 0x49, 0xc1, 0xa8, + 0x16, 0x50, 0x2a, 0xa2, 0x6b, 0x5a, 0xaa, 0xf7, 0xbc, 0x26, 0xed, 0x82, 0xd0, 0x46, 0xe7, 0x57, + 0xb8, 0xaa, 0x41, 0x83, 0xff, 0x54, 0xa4, 0x0c, 0xf7, 0x3b, 0xf1, 0x5d, 0x27, 0x6e, 0x17, 0xc7, + 0x13, 0x06, 0xaa, 0x00, 0xb5, 0x36, 0xbd, 0xc4, 0x06, 0x16, 0x78, 0xfc, 0x38, 0x83, 0x0c, 0x6c, + 0xbe, 0xfb, 0x73, 0xd9, 0xd0, 0xf6, 0x90, 0x94, 0x2a, 0x4e, 0xda, 0x45, 0xca, 0x35, 0x5d, 0x10, + 0x06, 0xa2, 0xb4, 0xf5, 0xe8, 0xcb, 0x01, 0x42, 0xa7, 0x52, 0x82, 0x5d, 0xe6, 0x2f, 0xd1, 0x58, + 0x41, 0x53, 0x33, 0xbe, 0xae, 0xa0, 0xd6, 0x81, 0x37, 0xf3, 0xe2, 0xd1, 0xea, 0xc9, 0xed, 0x76, + 0xea, 0x5f, 0xd2, 0x42, 0x9e, 0x44, 0xbd, 0x62, 0x94, 0x20, 0x1b, 0x5d, 0x40, 0xad, 0xfd, 0xd7, + 0xe8, 0x91, 0xab, 0xb1, 0x9c, 0x96, 0x25, 0x97, 0xc1, 0x81, 0xc1, 0x4e, 0x6e, 0xb7, 0xd3, 0xa3, + 0x7b, 0x58, 0x57, 0x8f, 0x92, 0x87, 0x36, 0x71, 0x66, 0x63, 0x5f, 0xa2, 0xb1, 0xaa, 0x78, 0xb9, + 0x59, 0x4b, 0x51, 0x08, 0x1d, 0x0c, 0x67, 0xc3, 0x78, 0xfc, 0x72, 0x82, 0xdd, 0x8d, 0x1d, 0x7f, + 0xec, 0xf8, 0xe3, 0x33, 0x10, 0xe5, 0xea, 0xc5, 0xf5, 0x76, 0x3a, 0xf8, 0xfa, 0x73, 0x1a, 0x67, + 0x42, 0xe7, 0x4d, 0x8a, 0x19, 0x14, 0x4e, 0x10, 0xf7, 0x99, 0xab, 0xcd, 0x07, 0xa2, 0x2f, 0x2b, + 0xae, 0x0c, 0x40, 0x25, 0xc8, 0xcc, 0x3f, 0xef, 0xc6, 0xfb, 0xcf, 0x10, 0xa2, 0x52, 0xc2, 0xc7, + 0xb5, 0x14, 0x4a, 0x07, 0x87, 0xb3, 0x61, 0x3c, 0x4a, 0x46, 0x26, 0x73, 0x2e, 0x94, 0x8e, 0x3e, + 0x7b, 0xe8, 0xe8, 0xad, 0xd3, 0xfd, 0xb4, 0xd1, 0x39, 0xd4, 0xe2, 0xca, 0x2a, 0x74, 0x81, 0xc6, + 0xf4, 0x8f, 0x5e, 0x2a, 0xf0, 0x0c, 0xcd, 0x18, 0xff, 0xef, 0xd5, 0xf0, 0x5f, 0x81, 0x57, 0x87, + 0x1d, 0xeb, 0xa4, 0x3f, 0xe2, 0xe4, 0xf9, 0xf7, 0x6f, 0xf3, 0xc8, 0x9d, 0x69, 0x9d, 0x70, 0x77, + 0xe7, 0xbd, 0xcd, 0xab, 0x37, 0xd7, 0xbb, 0xd0, 0xbb, 0xd9, 0x85, 0xde, 0xaf, 0x5d, 0xe8, 0x7d, + 0xda, 0x87, 0x83, 0x9b, 0x7d, 0x38, 0xf8, 0xb1, 0x0f, 0x07, 0xef, 0x96, 0xff, 0x4a, 0x20, 0x52, + 0x36, 0xcf, 0x80, 0xb4, 0x4b, 0x52, 0xc0, 0xa6, 0x91, 0x5c, 0x75, 0xee, 0xeb, 0xb9, 0xce, 0xe8, + 0x92, 0x3e, 0x30, 0x26, 0x78, 0xf5, 0x3b, 0x00, 0x00, 0xff, 0xff, 0x61, 0xe8, 0x65, 0x9c, 0x9f, + 0x02, 0x00, 0x00, +} + +func (m *Allocation) 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 *Allocation) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Allocation) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.AllowList) > 0 { + for iNdEx := len(m.AllowList) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.AllowList[iNdEx]) + copy(dAtA[i:], m.AllowList[iNdEx]) + i = encodeVarintAuthz(dAtA, i, uint64(len(m.AllowList[iNdEx]))) + i-- + dAtA[i] = 0x22 + } + } + if len(m.SpendLimit) > 0 { + for iNdEx := len(m.SpendLimit) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.SpendLimit[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintAuthz(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + } + if len(m.SourceChannel) > 0 { + i -= len(m.SourceChannel) + copy(dAtA[i:], m.SourceChannel) + i = encodeVarintAuthz(dAtA, i, uint64(len(m.SourceChannel))) + i-- + dAtA[i] = 0x12 + } + if len(m.SourcePort) > 0 { + i -= len(m.SourcePort) + copy(dAtA[i:], m.SourcePort) + i = encodeVarintAuthz(dAtA, i, uint64(len(m.SourcePort))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *TransferAuthorization) 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 *TransferAuthorization) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TransferAuthorization) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Allocations) > 0 { + for iNdEx := len(m.Allocations) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Allocations[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintAuthz(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func encodeVarintAuthz(dAtA []byte, offset int, v uint64) int { + offset -= sovAuthz(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *Allocation) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.SourcePort) + if l > 0 { + n += 1 + l + sovAuthz(uint64(l)) + } + l = len(m.SourceChannel) + if l > 0 { + n += 1 + l + sovAuthz(uint64(l)) + } + if len(m.SpendLimit) > 0 { + for _, e := range m.SpendLimit { + l = e.Size() + n += 1 + l + sovAuthz(uint64(l)) + } + } + if len(m.AllowList) > 0 { + for _, s := range m.AllowList { + l = len(s) + n += 1 + l + sovAuthz(uint64(l)) + } + } + return n +} + +func (m *TransferAuthorization) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Allocations) > 0 { + for _, e := range m.Allocations { + l = e.Size() + n += 1 + l + sovAuthz(uint64(l)) + } + } + return n +} + +func sovAuthz(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozAuthz(x uint64) (n int) { + return sovAuthz(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *Allocation) 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 ErrIntOverflowAuthz + } + 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: Allocation: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Allocation: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SourcePort", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuthz + } + 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 ErrInvalidLengthAuthz + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAuthz + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SourcePort = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SourceChannel", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuthz + } + 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 ErrInvalidLengthAuthz + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAuthz + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SourceChannel = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SpendLimit", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuthz + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthAuthz + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthAuthz + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SpendLimit = append(m.SpendLimit, types.Coin{}) + if err := m.SpendLimit[len(m.SpendLimit)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AllowList", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuthz + } + 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 ErrInvalidLengthAuthz + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAuthz + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.AllowList = append(m.AllowList, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipAuthz(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthAuthz + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TransferAuthorization) 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 ErrIntOverflowAuthz + } + 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: TransferAuthorization: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TransferAuthorization: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Allocations", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuthz + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthAuthz + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthAuthz + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Allocations = append(m.Allocations, Allocation{}) + if err := m.Allocations[len(m.Allocations)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipAuthz(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthAuthz + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipAuthz(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowAuthz + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowAuthz + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowAuthz + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthAuthz + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupAuthz + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthAuthz + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthAuthz = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowAuthz = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupAuthz = fmt.Errorf("proto: unexpected end of group") +) diff --git a/modules/apps/transfer/types/codec.go b/modules/apps/transfer/types/codec.go index 24ad7e5a902..92ed91ef58a 100644 --- a/modules/apps/transfer/types/codec.go +++ b/modules/apps/transfer/types/codec.go @@ -1,6 +1,12 @@ package types import ( + "bytes" + + "github.com/cosmos/cosmos-sdk/x/authz" + "github.com/cosmos/gogoproto/jsonpb" + "github.com/cosmos/gogoproto/proto" + "github.com/cosmos/cosmos-sdk/codec" codectypes "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" @@ -18,6 +24,11 @@ func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { func RegisterInterfaces(registry codectypes.InterfaceRegistry) { registry.RegisterImplementations((*sdk.Msg)(nil), &MsgTransfer{}) + registry.RegisterImplementations( + (*authz.Authorization)(nil), + &TransferAuthorization{}, + ) + msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) } @@ -39,3 +50,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..05620ede27a --- /dev/null +++ b/modules/apps/transfer/types/codec_test.go @@ -0,0 +1,25 @@ +package types_test + +import ( + "strings" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/cosmos/ibc-go/v7/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(), 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/coin.go b/modules/apps/transfer/types/coin.go index d6329541103..6abc5367cbe 100644 --- a/modules/apps/transfer/types/coin.go +++ b/modules/apps/transfer/types/coin.go @@ -4,6 +4,7 @@ import ( "fmt" "strings" + "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -41,7 +42,7 @@ func GetPrefixedDenom(portID, channelID, baseDenom string) string { // GetTransferCoin creates a transfer coin with the port ID and channel ID // prefixed to the base denom. -func GetTransferCoin(portID, channelID, baseDenom string, amount sdk.Int) sdk.Coin { +func GetTransferCoin(portID, channelID, baseDenom string, amount math.Int) sdk.Coin { denomTrace := ParseDenomTrace(GetPrefixedDenom(portID, channelID, baseDenom)) return sdk.NewCoin(denomTrace.IBCDenom(), amount) } diff --git a/modules/apps/transfer/types/errors.go b/modules/apps/transfer/types/errors.go index 0f0cb7c42a4..d62134b27cd 100644 --- a/modules/apps/transfer/types/errors.go +++ b/modules/apps/transfer/types/errors.go @@ -1,17 +1,18 @@ package types import ( - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + errorsmod "cosmossdk.io/errors" ) // IBC transfer sentinel errors var ( - ErrInvalidPacketTimeout = sdkerrors.Register(ModuleName, 2, "invalid packet timeout") - ErrInvalidDenomForTransfer = sdkerrors.Register(ModuleName, 3, "invalid denomination for cross-chain transfer") - ErrInvalidVersion = sdkerrors.Register(ModuleName, 4, "invalid ICS20 version") - ErrInvalidAmount = sdkerrors.Register(ModuleName, 5, "invalid token amount") - ErrTraceNotFound = sdkerrors.Register(ModuleName, 6, "denomination trace not found") - ErrSendDisabled = sdkerrors.Register(ModuleName, 7, "fungible token transfers from this chain are disabled") - ErrReceiveDisabled = sdkerrors.Register(ModuleName, 8, "fungible token transfers to this chain are disabled") - ErrMaxTransferChannels = sdkerrors.Register(ModuleName, 9, "max transfer channels") + ErrInvalidPacketTimeout = errorsmod.Register(ModuleName, 2, "invalid packet timeout") + ErrInvalidDenomForTransfer = errorsmod.Register(ModuleName, 3, "invalid denomination for cross-chain transfer") + ErrInvalidVersion = errorsmod.Register(ModuleName, 4, "invalid ICS20 version") + ErrInvalidAmount = errorsmod.Register(ModuleName, 5, "invalid token amount") + ErrTraceNotFound = errorsmod.Register(ModuleName, 6, "denomination trace not found") + ErrSendDisabled = errorsmod.Register(ModuleName, 7, "fungible token transfers from this chain are disabled") + ErrReceiveDisabled = errorsmod.Register(ModuleName, 8, "fungible token transfers to this chain are disabled") + ErrMaxTransferChannels = errorsmod.Register(ModuleName, 9, "max transfer channels") + ErrInvalidAuthorization = errorsmod.Register(ModuleName, 10, "invalid transfer authorization") ) 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 c696a2d75d8..2d53fc58f8f 100644 --- a/modules/apps/transfer/types/expected_keepers.go +++ b/modules/apps/transfer/types/expected_keepers.go @@ -5,9 +5,9 @@ import ( "github.com/cosmos/cosmos-sdk/x/auth/types" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - connectiontypes "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types" - channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" - ibcexported "github.com/cosmos/ibc-go/v4/modules/core/exported" + connectiontypes "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + ibcexported "github.com/cosmos/ibc-go/v7/modules/core/exported" ) // AccountKeeper defines the contract required for account APIs. @@ -24,11 +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 -} - -// ICS4Wrapper defines the expected ICS4Wrapper for middleware -type ICS4Wrapper interface { - SendPacket(ctx sdk.Context, channelCap *capabilitytypes.Capability, packet ibcexported.PacketI) error + IsSendEnabledCoin(ctx sdk.Context, coin sdk.Coin) bool } // ChannelKeeper defines the expected IBC channel keeper diff --git a/modules/apps/transfer/types/genesis.go b/modules/apps/transfer/types/genesis.go index db1f5489558..241a6cff005 100644 --- a/modules/apps/transfer/types/genesis.go +++ b/modules/apps/transfer/types/genesis.go @@ -1,7 +1,7 @@ package types import ( - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" ) // NewGenesisState creates a new ibc-transfer GenesisState instance. diff --git a/modules/apps/transfer/types/genesis.pb.go b/modules/apps/transfer/types/genesis.pb.go index bac7416d405..d7cd38dd7a9 100644 --- a/modules/apps/transfer/types/genesis.pb.go +++ b/modules/apps/transfer/types/genesis.pb.go @@ -5,8 +5,8 @@ package types import ( fmt "fmt" - _ "github.com/gogo/protobuf/gogoproto" - proto "github.com/gogo/protobuf/proto" + _ "github.com/cosmos/gogoproto/gogoproto" + proto "github.com/cosmos/gogoproto/proto" io "io" math "math" math_bits "math/bits" @@ -96,24 +96,24 @@ var fileDescriptor_a4f788affd5bea89 = []byte{ // 324 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x90, 0xc1, 0x4a, 0xc3, 0x30, 0x1c, 0xc6, 0x1b, 0x27, 0x15, 0xbb, 0xe1, 0xa1, 0x7a, 0x18, 0x43, 0xda, 0x51, 0x14, 0x8a, 0xc3, - 0x84, 0x4d, 0x41, 0xf0, 0x58, 0x04, 0xf1, 0xa6, 0xd3, 0x93, 0x97, 0x91, 0xa6, 0xb1, 0x06, 0xd6, - 0xa6, 0xe4, 0x9f, 0x0d, 0xf6, 0x16, 0x3e, 0x87, 0x4f, 0xb2, 0xe3, 0x8e, 0x9e, 0xa6, 0x6c, 0x6f, - 0x30, 0x5f, 0x40, 0xd2, 0xcd, 0xb1, 0x53, 0x6f, 0x1f, 0xc9, 0xef, 0xfb, 0xbe, 0x3f, 0x9f, 0x73, - 0x21, 0x62, 0x46, 0x68, 0x51, 0x0c, 0x05, 0xa3, 0x5a, 0xc8, 0x1c, 0x88, 0x56, 0x34, 0x87, 0x37, - 0xae, 0xc8, 0xb8, 0x4b, 0x52, 0x9e, 0x73, 0x10, 0x80, 0x0b, 0x25, 0xb5, 0x74, 0x4f, 0x45, 0xcc, - 0xf0, 0x2e, 0x8b, 0xff, 0x59, 0x3c, 0xee, 0xb6, 0x3a, 0x95, 0x49, 0x5b, 0xb2, 0x8c, 0x6a, 0x9d, - 0xa4, 0x32, 0x95, 0xa5, 0x24, 0x46, 0xad, 0x5f, 0x83, 0x5f, 0xe4, 0x34, 0xee, 0xd7, 0x95, 0xcf, - 0x9a, 0x6a, 0xee, 0x76, 0x9c, 0x83, 0x42, 0x2a, 0x3d, 0x10, 0x49, 0x13, 0xb5, 0x51, 0x78, 0x18, - 0xb9, 0xab, 0xb9, 0x7f, 0x34, 0xa1, 0xd9, 0xf0, 0x36, 0xd8, 0x7c, 0x04, 0x7d, 0xdb, 0xa8, 0x87, - 0xc4, 0x55, 0x4e, 0x23, 0xe1, 0xb9, 0xcc, 0x06, 0x5a, 0x51, 0xc6, 0xa1, 0xb9, 0xd7, 0xae, 0x85, - 0xf5, 0x5e, 0x88, 0xab, 0xae, 0xc6, 0x77, 0xc6, 0xf1, 0x62, 0x0c, 0xd1, 0xf9, 0x74, 0xee, 0x5b, - 0xab, 0xb9, 0x7f, 0xbc, 0xce, 0xdf, 0xcd, 0x0a, 0x3e, 0xbf, 0x7d, 0xbb, 0xa4, 0xa0, 0x5f, 0x4f, - 0xb6, 0x16, 0x70, 0x23, 0xc7, 0x2e, 0xa8, 0xa2, 0x19, 0x34, 0x6b, 0x6d, 0x14, 0xd6, 0x7b, 0x67, - 0xd5, 0x6d, 0x8f, 0x25, 0x1b, 0xed, 0x9b, 0xa6, 0xfe, 0xc6, 0x19, 0x3d, 0x4d, 0x17, 0x1e, 0x9a, - 0x2d, 0x3c, 0xf4, 0xb3, 0xf0, 0xd0, 0xc7, 0xd2, 0xb3, 0x66, 0x4b, 0xcf, 0xfa, 0x5a, 0x7a, 0xd6, - 0xeb, 0x4d, 0x2a, 0xf4, 0xfb, 0x28, 0xc6, 0x4c, 0x66, 0x84, 0x49, 0xc8, 0x24, 0x10, 0x11, 0xb3, - 0xcb, 0x54, 0x92, 0xf1, 0x35, 0xc9, 0x64, 0x32, 0x1a, 0x72, 0x30, 0x93, 0xef, 0x4c, 0xad, 0x27, - 0x05, 0x87, 0xd8, 0x2e, 0xf7, 0xbc, 0xfa, 0x0b, 0x00, 0x00, 0xff, 0xff, 0x96, 0x72, 0x43, 0x93, + 0x84, 0xcd, 0xc3, 0xc0, 0x63, 0x11, 0xc4, 0x9b, 0x4e, 0x4f, 0x5e, 0x46, 0x9a, 0xc6, 0x1a, 0x58, + 0x9b, 0x92, 0x7f, 0x36, 0xd8, 0x5b, 0xf8, 0x1c, 0x3e, 0xc9, 0x8e, 0x3b, 0x7a, 0x9a, 0xb2, 0xbd, + 0xc1, 0x7c, 0x01, 0x49, 0x37, 0xc7, 0x4e, 0xbd, 0x7d, 0x24, 0xbf, 0xef, 0xfb, 0xfe, 0x7c, 0xce, + 0x95, 0x88, 0x19, 0xa1, 0x45, 0x31, 0x12, 0x8c, 0x6a, 0x21, 0x73, 0x20, 0x5a, 0xd1, 0x1c, 0xde, + 0xb8, 0x22, 0x93, 0x2e, 0x49, 0x79, 0xce, 0x41, 0x00, 0x2e, 0x94, 0xd4, 0xd2, 0x3d, 0x17, 0x31, + 0xc3, 0xfb, 0x2c, 0xfe, 0x67, 0xf1, 0xa4, 0xdb, 0xea, 0x54, 0x26, 0xed, 0xc8, 0x32, 0xaa, 0x75, + 0x96, 0xca, 0x54, 0x96, 0x92, 0x18, 0xb5, 0x79, 0x0d, 0x7e, 0x91, 0xd3, 0xb8, 0xdf, 0x54, 0x3e, + 0x6b, 0xaa, 0xb9, 0xdb, 0x71, 0x8e, 0x0a, 0xa9, 0xf4, 0x50, 0x24, 0x4d, 0xd4, 0x46, 0xe1, 0x71, + 0xe4, 0xae, 0x17, 0xfe, 0xc9, 0x94, 0x66, 0xa3, 0xdb, 0x60, 0xfb, 0x11, 0x0c, 0x6c, 0xa3, 0x1e, + 0x12, 0x57, 0x39, 0x8d, 0x84, 0xe7, 0x32, 0x1b, 0x6a, 0x45, 0x19, 0x87, 0xe6, 0x41, 0xbb, 0x16, + 0xd6, 0x7b, 0x21, 0xae, 0xba, 0x1a, 0xdf, 0x19, 0xc7, 0x8b, 0x31, 0x44, 0x97, 0xb3, 0x85, 0x6f, + 0xad, 0x17, 0xfe, 0xe9, 0x26, 0x7f, 0x3f, 0x2b, 0xf8, 0xfc, 0xf6, 0xed, 0x92, 0x82, 0x41, 0x3d, + 0xd9, 0x59, 0xc0, 0x8d, 0x1c, 0xbb, 0xa0, 0x8a, 0x66, 0xd0, 0xac, 0xb5, 0x51, 0x58, 0xef, 0x5d, + 0x54, 0xb7, 0x3d, 0x96, 0x6c, 0x74, 0x68, 0x9a, 0x06, 0x5b, 0x67, 0xf4, 0x34, 0x5b, 0x7a, 0x68, + 0xbe, 0xf4, 0xd0, 0xcf, 0xd2, 0x43, 0x1f, 0x2b, 0xcf, 0x9a, 0xaf, 0x3c, 0xeb, 0x6b, 0xe5, 0x59, + 0xaf, 0xfd, 0x54, 0xe8, 0xf7, 0x71, 0x8c, 0x99, 0xcc, 0x08, 0x93, 0x90, 0x49, 0x20, 0x22, 0x66, + 0xd7, 0xa9, 0x24, 0x93, 0x3e, 0xc9, 0x64, 0x32, 0x1e, 0x71, 0x30, 0x93, 0xef, 0x4d, 0xad, 0xa7, + 0x05, 0x87, 0xd8, 0x2e, 0xf7, 0xbc, 0xf9, 0x0b, 0x00, 0x00, 0xff, 0xff, 0xf6, 0x4d, 0x22, 0xc9, 0xde, 0x01, 0x00, 0x00, } diff --git a/modules/apps/transfer/types/genesis_test.go b/modules/apps/transfer/types/genesis_test.go index fb63fbede64..415ba2b9b27 100644 --- a/modules/apps/transfer/types/genesis_test.go +++ b/modules/apps/transfer/types/genesis_test.go @@ -5,7 +5,7 @@ import ( "github.com/stretchr/testify/require" - "github.com/cosmos/ibc-go/v4/modules/apps/transfer/types" + "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" ) func TestValidateGenesis(t *testing.T) { diff --git a/modules/apps/transfer/types/keys_test.go b/modules/apps/transfer/types/keys_test.go index d69dc6e30c6..e19765ed57c 100644 --- a/modules/apps/transfer/types/keys_test.go +++ b/modules/apps/transfer/types/keys_test.go @@ -5,7 +5,7 @@ import ( "github.com/stretchr/testify/require" - "github.com/cosmos/ibc-go/v4/modules/apps/transfer/types" + "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" ) // Test that there is domain separation between the port id and the channel id otherwise an diff --git a/modules/apps/transfer/types/msgs.go b/modules/apps/transfer/types/msgs.go index 00ef9fe5386..de573544257 100644 --- a/modules/apps/transfer/types/msgs.go +++ b/modules/apps/transfer/types/msgs.go @@ -3,24 +3,22 @@ package types import ( "strings" + errorsmod "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" -) - -// msg types -const ( - TypeMsgTransfer = "transfer" + ibcerrors "github.com/cosmos/ibc-go/v7/internal/errors" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" ) // NewMsgTransfer creates a new MsgTransfer instance +// //nolint:interfacer func NewMsgTransfer( sourcePort, sourceChannel string, token sdk.Coin, sender, receiver string, timeoutHeight clienttypes.Height, timeoutTimestamp uint64, + memo string, ) *MsgTransfer { return &MsgTransfer{ SourcePort: sourcePort, @@ -30,6 +28,7 @@ func NewMsgTransfer( Receiver: receiver, TimeoutHeight: timeoutHeight, TimeoutTimestamp: timeoutTimestamp, + Memo: memo, } } @@ -38,35 +37,30 @@ func (MsgTransfer) Route() string { return RouterKey } -// Type implements sdk.Msg -func (MsgTransfer) Type() string { - return TypeMsgTransfer -} - // ValidateBasic performs a basic check of the MsgTransfer fields. // NOTE: timeout height or timestamp values can be 0 to disable the timeout. // NOTE: The recipient addresses format is not validated as the format defined by // the chain is not known to IBC. func (msg MsgTransfer) ValidateBasic() error { if err := host.PortIdentifierValidator(msg.SourcePort); err != nil { - return sdkerrors.Wrap(err, "invalid source port ID") + return errorsmod.Wrap(err, "invalid source port ID") } if err := host.ChannelIdentifierValidator(msg.SourceChannel); err != nil { - return sdkerrors.Wrap(err, "invalid source channel ID") + return errorsmod.Wrap(err, "invalid source channel ID") } if !msg.Token.IsValid() { - return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, msg.Token.String()) + return errorsmod.Wrap(ibcerrors.ErrInvalidCoins, msg.Token.String()) } if !msg.Token.IsPositive() { - return sdkerrors.Wrap(sdkerrors.ErrInsufficientFunds, msg.Token.String()) + return errorsmod.Wrap(ibcerrors.ErrInsufficientFunds, msg.Token.String()) } // NOTE: sender format must be validated as it is required by the GetSigners function. _, err := sdk.AccAddressFromBech32(msg.Sender) if err != nil { - return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) + return errorsmod.Wrapf(ibcerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) } if strings.TrimSpace(msg.Receiver) == "" { - return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "missing recipient address") + return errorsmod.Wrap(ibcerrors.ErrInvalidAddress, "missing recipient address") } return ValidateIBCDenom(msg.Token.Denom) } diff --git a/modules/apps/transfer/types/msgs_test.go b/modules/apps/transfer/types/msgs_test.go index 929de3a4d6f..e32d0f4a6c7 100644 --- a/modules/apps/transfer/types/msgs_test.go +++ b/modules/apps/transfer/types/msgs_test.go @@ -1,4 +1,4 @@ -package types +package types_test import ( "fmt" @@ -8,7 +8,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" ) // define constants used for testing @@ -41,20 +42,13 @@ var ( // TestMsgTransferRoute tests Route for MsgTransfer func TestMsgTransferRoute(t *testing.T) { - msg := NewMsgTransfer(validPort, validChannel, coin, addr1, addr2, timeoutHeight, 0) + msg := types.NewMsgTransfer(validPort, validChannel, coin, addr1, addr2, timeoutHeight, 0, "") - require.Equal(t, RouterKey, msg.Route()) -} - -// TestMsgTransferType tests Type for MsgTransfer -func TestMsgTransferType(t *testing.T) { - msg := NewMsgTransfer(validPort, validChannel, coin, addr1, addr2, timeoutHeight, 0) - - require.Equal(t, "transfer", msg.Type()) + require.Equal(t, types.RouterKey, msg.Route()) } func TestMsgTransferGetSignBytes(t *testing.T) { - msg := NewMsgTransfer(validPort, validChannel, coin, addr1, addr2, timeoutHeight, 0) + msg := types.NewMsgTransfer(validPort, validChannel, coin, addr1, addr2, timeoutHeight, 0, "") expected := fmt.Sprintf(`{"type":"cosmos-sdk/MsgTransfer","value":{"receiver":"%s","sender":"%s","source_channel":"testchannel","source_port":"testportid","timeout_height":{"revision_height":"10"},"token":{"amount":"100","denom":"atom"}}}`, addr2, addr1) require.NotPanics(t, func() { res := msg.GetSignBytes() @@ -66,23 +60,23 @@ func TestMsgTransferGetSignBytes(t *testing.T) { func TestMsgTransferValidation(t *testing.T) { testCases := []struct { name string - msg *MsgTransfer + msg *types.MsgTransfer expPass bool }{ - {"valid msg with base denom", NewMsgTransfer(validPort, validChannel, coin, addr1, addr2, timeoutHeight, 0), true}, - {"valid msg with trace hash", NewMsgTransfer(validPort, validChannel, ibcCoin, addr1, addr2, timeoutHeight, 0), true}, - {"invalid ibc denom", NewMsgTransfer(validPort, validChannel, invalidIBCCoin, addr1, addr2, timeoutHeight, 0), false}, - {"too short port id", NewMsgTransfer(invalidShortPort, validChannel, coin, addr1, addr2, timeoutHeight, 0), false}, - {"too long port id", NewMsgTransfer(invalidLongPort, validChannel, coin, addr1, addr2, timeoutHeight, 0), false}, - {"port id contains non-alpha", NewMsgTransfer(invalidPort, validChannel, coin, addr1, addr2, timeoutHeight, 0), false}, - {"too short channel id", NewMsgTransfer(validPort, invalidShortChannel, coin, addr1, addr2, timeoutHeight, 0), false}, - {"too long channel id", NewMsgTransfer(validPort, invalidLongChannel, coin, addr1, addr2, timeoutHeight, 0), false}, - {"channel id contains non-alpha", NewMsgTransfer(validPort, invalidChannel, coin, addr1, addr2, timeoutHeight, 0), false}, - {"invalid denom", NewMsgTransfer(validPort, validChannel, invalidDenomCoin, addr1, addr2, timeoutHeight, 0), false}, - {"zero coin", NewMsgTransfer(validPort, validChannel, zeroCoin, addr1, addr2, timeoutHeight, 0), false}, - {"missing sender address", NewMsgTransfer(validPort, validChannel, coin, emptyAddr, addr2, timeoutHeight, 0), false}, - {"missing recipient address", NewMsgTransfer(validPort, validChannel, coin, addr1, "", timeoutHeight, 0), false}, - {"empty coin", NewMsgTransfer(validPort, validChannel, sdk.Coin{}, addr1, addr2, timeoutHeight, 0), false}, + {"valid msg with base denom", types.NewMsgTransfer(validPort, validChannel, coin, addr1, addr2, timeoutHeight, 0, ""), true}, + {"valid msg with trace hash", types.NewMsgTransfer(validPort, validChannel, ibcCoin, addr1, addr2, timeoutHeight, 0, ""), true}, + {"invalid ibc denom", types.NewMsgTransfer(validPort, validChannel, invalidIBCCoin, addr1, addr2, timeoutHeight, 0, ""), false}, + {"too short port id", types.NewMsgTransfer(invalidShortPort, validChannel, coin, addr1, addr2, timeoutHeight, 0, ""), false}, + {"too long port id", types.NewMsgTransfer(invalidLongPort, validChannel, coin, addr1, addr2, timeoutHeight, 0, ""), false}, + {"port id contains non-alpha", types.NewMsgTransfer(invalidPort, validChannel, coin, addr1, addr2, timeoutHeight, 0, ""), false}, + {"too short channel id", types.NewMsgTransfer(validPort, invalidShortChannel, coin, addr1, addr2, timeoutHeight, 0, ""), false}, + {"too long channel id", types.NewMsgTransfer(validPort, invalidLongChannel, coin, addr1, addr2, timeoutHeight, 0, ""), false}, + {"channel id contains non-alpha", types.NewMsgTransfer(validPort, invalidChannel, coin, addr1, addr2, timeoutHeight, 0, ""), false}, + {"invalid denom", types.NewMsgTransfer(validPort, validChannel, invalidDenomCoin, addr1, addr2, timeoutHeight, 0, ""), false}, + {"zero coin", types.NewMsgTransfer(validPort, validChannel, zeroCoin, addr1, addr2, timeoutHeight, 0, ""), false}, + {"missing sender address", types.NewMsgTransfer(validPort, validChannel, coin, emptyAddr, addr2, timeoutHeight, 0, ""), false}, + {"missing recipient address", types.NewMsgTransfer(validPort, validChannel, coin, addr1, "", timeoutHeight, 0, ""), false}, + {"empty coin", types.NewMsgTransfer(validPort, validChannel, sdk.Coin{}, addr1, addr2, timeoutHeight, 0, ""), false}, } for i, tc := range testCases { @@ -99,7 +93,7 @@ func TestMsgTransferValidation(t *testing.T) { func TestMsgTransferGetSigners(t *testing.T) { addr := sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()) - msg := NewMsgTransfer(validPort, validChannel, coin, addr.String(), addr2, timeoutHeight, 0) + msg := types.NewMsgTransfer(validPort, validChannel, coin, addr.String(), addr2, timeoutHeight, 0, "") res := msg.GetSigners() require.Equal(t, []sdk.AccAddress{addr}, res) diff --git a/modules/apps/transfer/types/packet.go b/modules/apps/transfer/types/packet.go index a7384c47486..ec795fa2482 100644 --- a/modules/apps/transfer/types/packet.go +++ b/modules/apps/transfer/types/packet.go @@ -4,8 +4,10 @@ import ( "strings" "time" + errorsmod "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + + ibcerrors "github.com/cosmos/ibc-go/v7/internal/errors" ) var ( @@ -25,12 +27,14 @@ var ( func NewFungibleTokenPacketData( denom string, amount string, sender, receiver string, + memo string, ) FungibleTokenPacketData { return FungibleTokenPacketData{ Denom: denom, Amount: amount, Sender: sender, Receiver: receiver, + Memo: memo, } } @@ -40,21 +44,21 @@ func NewFungibleTokenPacketData( func (ftpd FungibleTokenPacketData) ValidateBasic() error { amount, ok := sdk.NewIntFromString(ftpd.Amount) if !ok { - return sdkerrors.Wrapf(ErrInvalidAmount, "unable to parse transfer amount (%s) into sdk.Int", ftpd.Amount) + return errorsmod.Wrapf(ErrInvalidAmount, "unable to parse transfer amount (%s) into math.Int", ftpd.Amount) } if !amount.IsPositive() { - return sdkerrors.Wrapf(ErrInvalidAmount, "amount must be strictly positive: got %d", amount) + return errorsmod.Wrapf(ErrInvalidAmount, "amount must be strictly positive: got %d", amount) } if strings.TrimSpace(ftpd.Sender) == "" { - return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "sender address cannot be blank") + return errorsmod.Wrap(ibcerrors.ErrInvalidAddress, "sender address cannot be blank") } if strings.TrimSpace(ftpd.Receiver) == "" { - return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "receiver address cannot be blank") + return errorsmod.Wrap(ibcerrors.ErrInvalidAddress, "receiver address cannot be blank") } return ValidatePrefixedDenom(ftpd.Denom) } // 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..574e4880453 100644 --- a/modules/apps/transfer/types/packet.pb.go +++ b/modules/apps/transfer/types/packet.pb.go @@ -5,7 +5,7 @@ package types import ( fmt "fmt" - proto "github.com/gogo/protobuf/proto" + proto "github.com/cosmos/gogoproto/proto" io "io" math "math" math_bits "math/bits" @@ -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, 0xc5, 0xf6, 0x22, 0xd6, 0x2a, 0x56, 0x76, 0x49, 0xe6, 0x3a, 0x86, 0x9d, 0xe4, 0x86, 0x24, + 0x33, 0xe0, 0x53, 0xe8, 0x63, 0x59, 0x6e, 0x69, 0x29, 0x33, 0x2f, 0x22, 0x9b, 0x51, 0xd9, 0x2e, + 0xe7, 0xe4, 0xbb, 0xcd, 0xa1, 0x17, 0x46, 0x69, 0x21, 0xbd, 0x6f, 0x8d, 0x96, 0xc9, 0xa0, 0x8b, + 0x22, 0x05, 0xe9, 0xe2, 0x33, 0x04, 0xd1, 0x2f, 0x85, 0x97, 0x7a, 0x0d, 0xa9, 0xf2, 0x01, 0x13, + 0xb2, 0x13, 0xa3, 0x74, 0xb5, 0x3b, 0xad, 0x7e, 0xa7, 0x55, 0xbf, 0x3c, 0x7b, 0x23, 0xf4, 0xe8, + 0xb6, 0x73, 0x8d, 0x51, 0x2d, 0x3c, 0xe2, 0x1a, 0xdc, 0x5d, 0xbe, 0xbd, 0x91, 0x49, 0xb2, 0x03, + 0x3a, 0xaf, 0xc1, 0xa1, 0x2d, 0xc9, 0x29, 0x39, 0xdf, 0x7f, 0x98, 0x80, 0x1d, 0xd2, 0x85, 0xb4, + 0xd8, 0xb9, 0x54, 0xfe, 0xcb, 0xfa, 0x87, 0xb6, 0x3e, 0x82, 0xab, 0x21, 0x94, 0xff, 0x27, 0x3f, + 0x11, 0x3b, 0xa6, 0x7b, 0x01, 0x34, 0x98, 0x1e, 0x42, 0x39, 0xcb, 0x3f, 0x7f, 0xcc, 0x18, 0x9d, + 0x59, 0xb0, 0x58, 0xce, 0xb3, 0xcf, 0xef, 0xeb, 0xfb, 0x8f, 0x81, 0x93, 0xcd, 0xc0, 0xc9, 0xd7, + 0xc0, 0xc9, 0xfb, 0xc8, 0x8b, 0xcd, 0xc8, 0x8b, 0xcf, 0x91, 0x17, 0x4f, 0xab, 0xc6, 0xa4, 0x97, + 0x4e, 0x55, 0x1a, 0xad, 0xd0, 0x18, 0x2d, 0x46, 0x61, 0x94, 0xbe, 0x6c, 0x50, 0xf4, 0x2b, 0x61, + 0xb1, 0xee, 0x5a, 0x88, 0xdb, 0x28, 0x3b, 0x31, 0xd2, 0xab, 0x87, 0xa8, 0x16, 0xb9, 0xc4, 0xd5, + 0x77, 0x00, 0x00, 0x00, 0xff, 0xff, 0x32, 0xf9, 0x52, 0xde, 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/packet_test.go b/modules/apps/transfer/types/packet_test.go index e5d21d648d2..067169a4ba2 100644 --- a/modules/apps/transfer/types/packet_test.go +++ b/modules/apps/transfer/types/packet_test.go @@ -1,9 +1,11 @@ -package types +package types_test import ( "testing" "github.com/stretchr/testify/require" + + "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" ) const ( @@ -17,18 +19,19 @@ const ( func TestFungibleTokenPacketDataValidateBasic(t *testing.T) { testCases := []struct { name string - packetData FungibleTokenPacketData + packetData types.FungibleTokenPacketData expPass bool }{ - {"valid packet", NewFungibleTokenPacketData(denom, amount, addr1, addr2), true}, - {"valid packet with large amount", NewFungibleTokenPacketData(denom, largeAmount, addr1, addr2), true}, - {"invalid denom", NewFungibleTokenPacketData("", amount, addr1, addr2), false}, - {"invalid empty amount", NewFungibleTokenPacketData(denom, "", addr1, addr2), false}, - {"invalid zero amount", NewFungibleTokenPacketData(denom, "0", addr1, addr2), false}, - {"invalid negative amount", NewFungibleTokenPacketData(denom, "-1", addr1, addr2), false}, - {"invalid large amount", NewFungibleTokenPacketData(denom, invalidLargeAmount, addr1, addr2), false}, - {"missing sender address", NewFungibleTokenPacketData(denom, amount, emptyAddr, addr2), false}, - {"missing recipient address", NewFungibleTokenPacketData(denom, amount, addr1, emptyAddr), false}, + {"valid packet", types.NewFungibleTokenPacketData(denom, amount, addr1, addr2, ""), true}, + {"valid packet with memo", types.NewFungibleTokenPacketData(denom, amount, addr1, addr2, "memo"), true}, + {"valid packet with large amount", types.NewFungibleTokenPacketData(denom, largeAmount, addr1, addr2, ""), true}, + {"invalid denom", types.NewFungibleTokenPacketData("", amount, addr1, addr2, ""), false}, + {"invalid empty amount", types.NewFungibleTokenPacketData(denom, "", addr1, addr2, ""), false}, + {"invalid zero amount", types.NewFungibleTokenPacketData(denom, "0", addr1, addr2, ""), false}, + {"invalid negative amount", types.NewFungibleTokenPacketData(denom, "-1", addr1, addr2, ""), false}, + {"invalid large amount", types.NewFungibleTokenPacketData(denom, invalidLargeAmount, addr1, addr2, ""), false}, + {"missing sender address", types.NewFungibleTokenPacketData(denom, amount, emptyAddr, addr2, ""), false}, + {"missing recipient address", types.NewFungibleTokenPacketData(denom, amount, addr1, emptyAddr, ""), false}, } for i, tc := range testCases { diff --git a/modules/apps/transfer/types/params.go b/modules/apps/transfer/types/params.go index 4ecdfab77e7..5b5fded0b4d 100644 --- a/modules/apps/transfer/types/params.go +++ b/modules/apps/transfer/types/params.go @@ -40,22 +40,22 @@ func DefaultParams() Params { // Validate all ibc-transfer module parameters func (p Params) Validate() error { - if err := validateEnabled(p.SendEnabled); err != nil { + if err := validateEnabledType(p.SendEnabled); err != nil { return err } - return validateEnabled(p.ReceiveEnabled) + return validateEnabledType(p.ReceiveEnabled) } // ParamSetPairs implements params.ParamSet func (p *Params) ParamSetPairs() paramtypes.ParamSetPairs { return paramtypes.ParamSetPairs{ - paramtypes.NewParamSetPair(KeySendEnabled, p.SendEnabled, validateEnabled), - paramtypes.NewParamSetPair(KeyReceiveEnabled, p.ReceiveEnabled, validateEnabled), + paramtypes.NewParamSetPair(KeySendEnabled, p.SendEnabled, validateEnabledType), + paramtypes.NewParamSetPair(KeyReceiveEnabled, p.ReceiveEnabled, validateEnabledType), } } -func validateEnabled(i interface{}) error { +func validateEnabledType(i interface{}) error { _, ok := i.(bool) if !ok { return fmt.Errorf("invalid parameter type: %T", i) diff --git a/modules/apps/transfer/types/params_test.go b/modules/apps/transfer/types/params_test.go index 825efb825c1..836cf973b01 100644 --- a/modules/apps/transfer/types/params_test.go +++ b/modules/apps/transfer/types/params_test.go @@ -1,12 +1,14 @@ -package types +package types_test import ( "testing" "github.com/stretchr/testify/require" + + "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" ) func TestValidateParams(t *testing.T) { - require.NoError(t, DefaultParams().Validate()) - require.NoError(t, NewParams(true, false).Validate()) + require.NoError(t, types.DefaultParams().Validate()) + require.NoError(t, types.NewParams(true, false).Validate()) } diff --git a/modules/apps/transfer/types/query.pb.go b/modules/apps/transfer/types/query.pb.go index d654e4b30a5..f8c4543eee3 100644 --- a/modules/apps/transfer/types/query.pb.go +++ b/modules/apps/transfer/types/query.pb.go @@ -7,9 +7,9 @@ import ( context "context" fmt "fmt" query "github.com/cosmos/cosmos-sdk/types/query" - _ "github.com/gogo/protobuf/gogoproto" - grpc1 "github.com/gogo/protobuf/grpc" - proto "github.com/gogo/protobuf/proto" + _ "github.com/cosmos/gogoproto/gogoproto" + grpc1 "github.com/cosmos/gogoproto/grpc" + proto "github.com/cosmos/gogoproto/proto" _ "google.golang.org/genproto/googleapis/api/annotations" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" @@ -523,52 +523,52 @@ func init() { } var fileDescriptor_a638e2800a01538c = []byte{ - // 715 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x55, 0xcd, 0x6e, 0xd3, 0x4c, - 0x14, 0x8d, 0xfb, 0x7d, 0x0d, 0xca, 0x0d, 0xed, 0x62, 0x28, 0xb4, 0x58, 0xc5, 0xad, 0xac, 0x02, - 0xa5, 0x3f, 0x1e, 0xd2, 0x16, 0xca, 0x82, 0x0d, 0xe5, 0xb7, 0x88, 0x45, 0x9b, 0xb2, 0x82, 0x45, - 0x35, 0xb6, 0x07, 0xc7, 0x52, 0xe2, 0x71, 0x3d, 0x4e, 0x50, 0x55, 0x65, 0xc3, 0x13, 0x20, 0xf5, - 0x25, 0x50, 0xc5, 0x43, 0xb0, 0xec, 0xb2, 0x12, 0x12, 0x62, 0x05, 0xa8, 0xe5, 0x35, 0x90, 0x90, - 0x67, 0x26, 0x89, 0x4d, 0xa3, 0x34, 0xde, 0x8d, 0x67, 0xee, 0xb9, 0xf7, 0x9c, 0x73, 0xef, 0x95, - 0x61, 0xde, 0xb7, 0x1d, 0x4c, 0xc2, 0xb0, 0xee, 0x3b, 0x24, 0xf6, 0x59, 0xc0, 0x71, 0x1c, 0x91, - 0x80, 0xbf, 0xa3, 0x11, 0x6e, 0x55, 0xf0, 0x5e, 0x93, 0x46, 0xfb, 0x56, 0x18, 0xb1, 0x98, 0xa1, - 0x69, 0xdf, 0x76, 0xac, 0x74, 0xa4, 0xd5, 0x89, 0xb4, 0x5a, 0x15, 0x7d, 0xc2, 0x63, 0x1e, 0x13, - 0x81, 0x38, 0x39, 0x49, 0x8c, 0xbe, 0xe0, 0x30, 0xde, 0x60, 0x1c, 0xdb, 0x84, 0x53, 0x99, 0x0c, - 0xb7, 0x2a, 0x36, 0x8d, 0x49, 0x05, 0x87, 0xc4, 0xf3, 0x03, 0x91, 0x48, 0xc5, 0x2e, 0x0e, 0x64, - 0xd2, 0xad, 0x25, 0x83, 0xa7, 0x3d, 0xc6, 0xbc, 0x3a, 0xc5, 0x24, 0xf4, 0x31, 0x09, 0x02, 0x16, - 0x2b, 0x4a, 0xe2, 0xd5, 0x5c, 0x82, 0x6b, 0xdb, 0x49, 0xb1, 0x27, 0x34, 0x60, 0x8d, 0xd7, 0x11, - 0x71, 0x68, 0x95, 0xee, 0x35, 0x29, 0x8f, 0x11, 0x82, 0xff, 0x6b, 0x84, 0xd7, 0xa6, 0xb4, 0x59, - 0x6d, 0xbe, 0x54, 0x15, 0x67, 0xd3, 0x85, 0xc9, 0x73, 0xd1, 0x3c, 0x64, 0x01, 0xa7, 0x68, 0x13, - 0xca, 0x6e, 0x72, 0xbb, 0x1b, 0x27, 0xd7, 0x02, 0x55, 0x5e, 0x99, 0xb7, 0x06, 0x39, 0x61, 0xa5, - 0xd2, 0x80, 0xdb, 0x3d, 0x9b, 0xe4, 0x5c, 0x15, 0xde, 0x21, 0xf5, 0x0c, 0xa0, 0xe7, 0x86, 0x2a, - 0x72, 0xcb, 0x92, 0xd6, 0x59, 0x89, 0x75, 0x96, 0xec, 0x83, 0xb2, 0xce, 0xda, 0x22, 0x5e, 0x47, - 0x50, 0x35, 0x85, 0x34, 0xbf, 0x68, 0x30, 0x75, 0xbe, 0x86, 0x92, 0xf2, 0x16, 0x2e, 0xa7, 0xa4, - 0xf0, 0x29, 0x6d, 0xf6, 0xbf, 0x3c, 0x5a, 0x36, 0xc6, 0x8f, 0x7f, 0xcc, 0x14, 0x8e, 0x7e, 0xce, - 0x14, 0x55, 0xde, 0x72, 0x4f, 0x1b, 0x47, 0xcf, 0x33, 0x0a, 0x46, 0x84, 0x82, 0xdb, 0x17, 0x2a, - 0x90, 0xcc, 0x32, 0x12, 0x26, 0x00, 0x09, 0x05, 0x5b, 0x24, 0x22, 0x8d, 0x8e, 0x41, 0xe6, 0x0e, - 0x5c, 0xc9, 0xdc, 0x2a, 0x49, 0x0f, 0xa1, 0x18, 0x8a, 0x1b, 0xe5, 0xd9, 0xdc, 0x60, 0x31, 0x0a, - 0xad, 0x30, 0xe6, 0x32, 0x5c, 0xed, 0x99, 0xf5, 0x82, 0xf0, 0x5a, 0xa7, 0x1d, 0x13, 0x30, 0xda, - 0x6b, 0x77, 0xa9, 0x2a, 0x3f, 0xb2, 0x33, 0x25, 0xc3, 0x15, 0x8d, 0x7e, 0x33, 0xb5, 0x03, 0xd7, - 0x45, 0xf4, 0x53, 0xee, 0x44, 0xec, 0xfd, 0x23, 0xd7, 0x8d, 0x28, 0xef, 0xf6, 0x7b, 0x12, 0x2e, - 0x85, 0x2c, 0x8a, 0x77, 0x7d, 0x57, 0x61, 0x8a, 0xc9, 0xe7, 0xa6, 0x8b, 0x6e, 0x00, 0x38, 0x35, - 0x12, 0x04, 0xb4, 0x9e, 0xbc, 0x8d, 0x88, 0xb7, 0x92, 0xba, 0xd9, 0x74, 0xcd, 0xc7, 0xa0, 0xf7, - 0x4b, 0xaa, 0x68, 0xdc, 0x84, 0x71, 0x2a, 0x1e, 0x76, 0x89, 0x7c, 0x51, 0xc9, 0xc7, 0x68, 0x3a, - 0x7c, 0xe5, 0x4f, 0x11, 0x46, 0x45, 0x16, 0xf4, 0x59, 0x03, 0xe8, 0x35, 0x18, 0xad, 0x0d, 0x76, - 0xaf, 0xff, 0x42, 0xe9, 0xf7, 0x72, 0xa2, 0x24, 0x59, 0xb3, 0xf2, 0xe1, 0xeb, 0xef, 0xc3, 0x91, - 0x45, 0x74, 0x07, 0xab, 0xad, 0xcf, 0x6e, 0x7b, 0x7a, 0x52, 0xf1, 0x41, 0xe2, 0x68, 0x1b, 0x7d, - 0xd2, 0xa0, 0x9c, 0x1a, 0x6c, 0x94, 0xaf, 0x72, 0xc7, 0x7c, 0xfd, 0x7e, 0x5e, 0x98, 0x62, 0xbc, - 0x20, 0x18, 0xcf, 0x21, 0xf3, 0x62, 0xc6, 0xe8, 0x50, 0x83, 0xa2, 0x9c, 0x36, 0x74, 0x77, 0x88, - 0x72, 0x99, 0x61, 0xd7, 0x2b, 0x39, 0x10, 0x8a, 0xdb, 0x9c, 0xe0, 0x66, 0xa0, 0xe9, 0xfe, 0xdc, - 0xe4, 0xc0, 0xa3, 0x23, 0x0d, 0x4a, 0xdd, 0xe9, 0x45, 0xab, 0xc3, 0xfa, 0x90, 0x5a, 0x0d, 0x7d, - 0x2d, 0x1f, 0x48, 0xd1, 0x5b, 0x11, 0xf4, 0x96, 0xd0, 0xc2, 0x20, 0xeb, 0x92, 0x26, 0x27, 0xcd, - 0x16, 0x16, 0xb6, 0xd1, 0x37, 0x0d, 0xc6, 0x32, 0x73, 0x8e, 0xd6, 0x87, 0xa8, 0xdd, 0x6f, 0xdd, - 0xf4, 0x07, 0xf9, 0x81, 0x8a, 0x78, 0x55, 0x10, 0x7f, 0x85, 0x5e, 0xf6, 0x27, 0xae, 0x36, 0x93, - 0xe3, 0x83, 0xde, 0xd6, 0xb6, 0x71, 0xb2, 0xcb, 0x1c, 0x1f, 0xa8, 0x0d, 0x6f, 0xe3, 0xec, 0x52, - 0x6e, 0x6c, 0x1f, 0x9f, 0x1a, 0xda, 0xc9, 0xa9, 0xa1, 0xfd, 0x3a, 0x35, 0xb4, 0x8f, 0x67, 0x46, - 0xe1, 0xe4, 0xcc, 0x28, 0x7c, 0x3f, 0x33, 0x0a, 0x6f, 0xd6, 0x3d, 0x3f, 0xae, 0x35, 0x6d, 0xcb, - 0x61, 0x0d, 0xac, 0xfe, 0x9b, 0xbe, 0xed, 0x2c, 0x7b, 0x0c, 0xb7, 0xd6, 0x70, 0x83, 0xb9, 0xcd, - 0x3a, 0xe5, 0xff, 0x90, 0x88, 0xf7, 0x43, 0xca, 0xed, 0xa2, 0xf8, 0xeb, 0xad, 0xfe, 0x0d, 0x00, - 0x00, 0xff, 0xff, 0xed, 0xd2, 0x70, 0xd6, 0xcc, 0x07, 0x00, 0x00, + // 718 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x55, 0x4f, 0x4f, 0xd4, 0x40, + 0x14, 0xdf, 0xa2, 0x2c, 0xd9, 0xb7, 0xc2, 0x61, 0x44, 0xc1, 0x06, 0x0b, 0x69, 0x50, 0xc9, 0x0a, + 0x1d, 0x97, 0x3f, 0xe2, 0x41, 0x0f, 0xe2, 0x5f, 0x8c, 0x07, 0x58, 0x3c, 0xe9, 0x81, 0x4c, 0xdb, + 0xb1, 0xdb, 0x64, 0xb7, 0x53, 0x3a, 0xdd, 0x35, 0x84, 0x70, 0xd1, 0x2f, 0x60, 0xc2, 0x97, 0x30, + 0x26, 0xc6, 0xaf, 0xe0, 0x91, 0x23, 0x89, 0x89, 0xf1, 0xa4, 0x06, 0xfc, 0x20, 0xa6, 0x33, 0xb3, + 0xbb, 0xad, 0x6c, 0x16, 0x7a, 0x9b, 0xce, 0xbc, 0xdf, 0x7b, 0xbf, 0xdf, 0xef, 0xbd, 0x97, 0xc2, + 0x9c, 0x6f, 0x3b, 0x98, 0x84, 0x61, 0xc3, 0x77, 0x48, 0xec, 0xb3, 0x80, 0xe3, 0x38, 0x22, 0x01, + 0x7f, 0x4b, 0x23, 0xdc, 0xae, 0xe2, 0x9d, 0x16, 0x8d, 0x76, 0xad, 0x30, 0x62, 0x31, 0x43, 0x53, + 0xbe, 0xed, 0x58, 0xe9, 0x48, 0xab, 0x13, 0x69, 0xb5, 0xab, 0xfa, 0xb8, 0xc7, 0x3c, 0x26, 0x02, + 0x71, 0x72, 0x92, 0x18, 0xbd, 0xe2, 0x30, 0xde, 0x64, 0x1c, 0xdb, 0x84, 0x53, 0x99, 0x0c, 0xb7, + 0xab, 0x36, 0x8d, 0x49, 0x15, 0x87, 0xc4, 0xf3, 0x03, 0x91, 0x48, 0xc5, 0xde, 0x1e, 0xc8, 0xa4, + 0x5b, 0x4b, 0x06, 0x4f, 0x79, 0x8c, 0x79, 0x0d, 0x8a, 0x49, 0xe8, 0x63, 0x12, 0x04, 0x2c, 0x56, + 0x94, 0xc4, 0xab, 0x39, 0x0f, 0x57, 0x37, 0x93, 0x62, 0x8f, 0x69, 0xc0, 0x9a, 0xaf, 0x22, 0xe2, + 0xd0, 0x1a, 0xdd, 0x69, 0x51, 0x1e, 0x23, 0x04, 0x17, 0xeb, 0x84, 0xd7, 0x27, 0xb5, 0x19, 0x6d, + 0xae, 0x54, 0x13, 0x67, 0xd3, 0x85, 0x89, 0x53, 0xd1, 0x3c, 0x64, 0x01, 0xa7, 0x68, 0x1d, 0xca, + 0x6e, 0x72, 0xbb, 0x1d, 0x27, 0xd7, 0x02, 0x55, 0x5e, 0x9c, 0xb3, 0x06, 0x39, 0x61, 0xa5, 0xd2, + 0x80, 0xdb, 0x3d, 0x9b, 0xe4, 0x54, 0x15, 0xde, 0x21, 0xf5, 0x14, 0xa0, 0xe7, 0x86, 0x2a, 0x72, + 0xd3, 0x92, 0xd6, 0x59, 0x89, 0x75, 0x96, 0xec, 0x83, 0xb2, 0xce, 0xda, 0x20, 0x5e, 0x47, 0x50, + 0x2d, 0x85, 0x34, 0xbf, 0x69, 0x30, 0x79, 0xba, 0x86, 0x92, 0xf2, 0x06, 0x2e, 0xa5, 0xa4, 0xf0, + 0x49, 0x6d, 0xe6, 0x42, 0x1e, 0x2d, 0x6b, 0x63, 0x87, 0xbf, 0xa6, 0x0b, 0x9f, 0x7f, 0x4f, 0x17, + 0x55, 0xde, 0x72, 0x4f, 0x1b, 0x47, 0xcf, 0x32, 0x0a, 0x86, 0x84, 0x82, 0x5b, 0x67, 0x2a, 0x90, + 0xcc, 0x32, 0x12, 0xc6, 0x01, 0x09, 0x05, 0x1b, 0x24, 0x22, 0xcd, 0x8e, 0x41, 0xe6, 0x16, 0x5c, + 0xce, 0xdc, 0x2a, 0x49, 0xf7, 0xa1, 0x18, 0x8a, 0x1b, 0xe5, 0xd9, 0xec, 0x60, 0x31, 0x0a, 0xad, + 0x30, 0xe6, 0x02, 0x5c, 0xe9, 0x99, 0xf5, 0x9c, 0xf0, 0x7a, 0xa7, 0x1d, 0xe3, 0x30, 0xdc, 0x6b, + 0x77, 0xa9, 0x26, 0x3f, 0xb2, 0x33, 0x25, 0xc3, 0x15, 0x8d, 0x7e, 0x33, 0xb5, 0x05, 0xd7, 0x44, + 0xf4, 0x13, 0xee, 0x44, 0xec, 0xdd, 0x43, 0xd7, 0x8d, 0x28, 0xef, 0xf6, 0x7b, 0x02, 0x46, 0x42, + 0x16, 0xc5, 0xdb, 0xbe, 0xab, 0x30, 0xc5, 0xe4, 0x73, 0xdd, 0x45, 0xd7, 0x01, 0x9c, 0x3a, 0x09, + 0x02, 0xda, 0x48, 0xde, 0x86, 0xc4, 0x5b, 0x49, 0xdd, 0xac, 0xbb, 0xe6, 0x23, 0xd0, 0xfb, 0x25, + 0x55, 0x34, 0x6e, 0xc0, 0x18, 0x15, 0x0f, 0xdb, 0x44, 0xbe, 0xa8, 0xe4, 0xa3, 0x34, 0x1d, 0xbe, + 0xf8, 0x61, 0x04, 0x86, 0x45, 0x16, 0xf4, 0x55, 0x03, 0xe8, 0x35, 0x18, 0x2d, 0x0f, 0x76, 0xaf, + 0xff, 0x42, 0xe9, 0x2b, 0x39, 0x51, 0x92, 0xac, 0xb9, 0xfc, 0xfe, 0xfb, 0xdf, 0x83, 0x21, 0x0b, + 0xcd, 0x63, 0xb5, 0xf5, 0xd9, 0x6d, 0x4f, 0x4f, 0x2a, 0xde, 0x4b, 0x1c, 0x7d, 0x50, 0xa9, 0xec, + 0xa3, 0x4f, 0x1a, 0x94, 0x53, 0xb3, 0x8d, 0xf2, 0x15, 0xef, 0xf8, 0xaf, 0xdf, 0xcd, 0x0b, 0x53, + 0xa4, 0x2b, 0x82, 0xf4, 0x2c, 0x32, 0xcf, 0x26, 0x8d, 0x0e, 0x34, 0x28, 0xca, 0x81, 0x43, 0x77, + 0xce, 0x51, 0x2e, 0x33, 0xef, 0x7a, 0x35, 0x07, 0x42, 0x71, 0x9b, 0x15, 0xdc, 0x0c, 0x34, 0xd5, + 0x9f, 0x9b, 0x9c, 0x79, 0xf4, 0x45, 0x83, 0x52, 0x77, 0x80, 0xd1, 0xd2, 0x79, 0x7d, 0x48, 0x6d, + 0x87, 0xbe, 0x9c, 0x0f, 0xa4, 0xe8, 0xad, 0x08, 0x7a, 0x18, 0x2d, 0x0c, 0xb2, 0x2e, 0xe9, 0x73, + 0xd2, 0x6f, 0x61, 0xa1, 0x68, 0xf8, 0x0f, 0x0d, 0x46, 0x33, 0xd3, 0x8e, 0x56, 0xcf, 0x51, 0xbe, + 0xdf, 0xd2, 0xe9, 0xf7, 0xf2, 0x03, 0x15, 0xf7, 0x9a, 0xe0, 0xfe, 0x12, 0xbd, 0xe8, 0xcf, 0x5d, + 0xed, 0x27, 0xc7, 0x7b, 0xbd, 0xdd, 0xdd, 0xc7, 0xc9, 0x46, 0x73, 0xbc, 0xa7, 0xf6, 0x7c, 0x1f, + 0x67, 0x57, 0x73, 0x6d, 0xf3, 0xf0, 0xd8, 0xd0, 0x8e, 0x8e, 0x0d, 0xed, 0xcf, 0xb1, 0xa1, 0x7d, + 0x3c, 0x31, 0x0a, 0x47, 0x27, 0x46, 0xe1, 0xe7, 0x89, 0x51, 0x78, 0xbd, 0xea, 0xf9, 0x71, 0xbd, + 0x65, 0x5b, 0x0e, 0x6b, 0x62, 0xf5, 0xf7, 0xf4, 0x6d, 0x67, 0xc1, 0x63, 0xb8, 0xbd, 0x8a, 0x9b, + 0xcc, 0x6d, 0x35, 0x28, 0xff, 0x8f, 0x44, 0xbc, 0x1b, 0x52, 0x6e, 0x17, 0xc5, 0xbf, 0x6f, 0xe9, + 0x5f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x47, 0x7a, 0xbb, 0x0b, 0xd2, 0x07, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. diff --git a/modules/apps/transfer/types/query.pb.gw.go b/modules/apps/transfer/types/query.pb.gw.go index 71474a1b2fc..35d95239e65 100644 --- a/modules/apps/transfer/types/query.pb.gw.go +++ b/modules/apps/transfer/types/query.pb.gw.go @@ -20,6 +20,7 @@ import ( "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/grpclog" + "google.golang.org/grpc/metadata" "google.golang.org/grpc/status" ) @@ -30,6 +31,7 @@ var _ status.Status var _ = runtime.String var _ = utilities.NewDoubleArray var _ = descriptor.ForMessage +var _ = metadata.Join func request_Query_DenomTrace_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq QueryDenomTraceRequest @@ -272,12 +274,14 @@ func local_request_Query_EscrowAddress_0(ctx context.Context, marshaler runtime. // RegisterQueryHandlerServer registers the http handlers for service Query to "mux". // UnaryRPC :call QueryServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. -// Note that using this registration option will cause many gRPC library features (such as grpc.SendHeader, etc) to stop working. Consider using RegisterQueryHandlerFromEndpoint instead. +// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterQueryHandlerFromEndpoint instead. func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, server QueryServer) error { mux.Handle("GET", pattern_Query_DenomTrace_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { @@ -285,6 +289,7 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv return } resp, md, err := local_request_Query_DenomTrace_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) @@ -298,6 +303,8 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv mux.Handle("GET", pattern_Query_DenomTraces_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { @@ -305,6 +312,7 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv return } resp, md, err := local_request_Query_DenomTraces_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) @@ -318,6 +326,8 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv 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() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { @@ -325,6 +335,7 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv return } resp, md, err := local_request_Query_Params_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) @@ -338,6 +349,8 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv mux.Handle("GET", pattern_Query_DenomHash_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { @@ -345,6 +358,7 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv return } resp, md, err := local_request_Query_DenomHash_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) @@ -358,6 +372,8 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv mux.Handle("GET", pattern_Query_EscrowAddress_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { @@ -365,6 +381,7 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv return } resp, md, err := local_request_Query_EscrowAddress_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) @@ -520,15 +537,15 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie } var ( - pattern_Query_DenomTrace_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5}, []string{"ibc", "apps", "transfer", "v1", "denom_traces", "hash"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_DenomTrace_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 3, 0, 4, 1, 5, 5}, []string{"ibc", "apps", "transfer", "v1", "denom_traces", "hash"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_DenomTraces_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"ibc", "apps", "transfer", "v1", "denom_traces"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_DenomTraces_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"ibc", "apps", "transfer", "v1", "denom_traces"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_Params_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"ibc", "apps", "transfer", "v1", "params"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_Params_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"ibc", "apps", "transfer", "v1", "params"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_DenomHash_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5}, []string{"ibc", "apps", "transfer", "v1", "denom_hashes", "trace"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_DenomHash_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 3, 0, 4, 1, 5, 5}, []string{"ibc", "apps", "transfer", "v1", "denom_hashes", "trace"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_EscrowAddress_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 2, 8}, []string{"ibc", "apps", "transfer", "v1", "channels", "channel_id", "ports", "port_id", "escrow_address"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_EscrowAddress_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 2, 8}, []string{"ibc", "apps", "transfer", "v1", "channels", "channel_id", "ports", "port_id", "escrow_address"}, "", runtime.AssumeColonVerbOpt(false))) ) var ( diff --git a/modules/apps/transfer/types/trace.go b/modules/apps/transfer/types/trace.go index 24f10c232a9..7145ff15d96 100644 --- a/modules/apps/transfer/types/trace.go +++ b/modules/apps/transfer/types/trace.go @@ -7,13 +7,13 @@ import ( "sort" "strings" + errorsmod "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" tmbytes "github.com/tendermint/tendermint/libs/bytes" tmtypes "github.com/tendermint/tendermint/types" - channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" ) // ParseDenomTrace parses a string with the ibc prefix (denom trace) and the base denomination @@ -84,8 +84,8 @@ func extractPathAndBaseFromFullDenom(fullDenomItems []string) (string, string) { ) length := len(fullDenomItems) - for i := 0; i < length; i = i + 2 { - // The IBC specification does not guarentee the expected format of the + for i := 0; i < length; i += 2 { + // The IBC specification does not guarantee 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 @@ -113,10 +113,10 @@ func validateTraceIdentifiers(identifiers []string) error { // validate correctness of port and channel identifiers for i := 0; i < len(identifiers); i += 2 { if err := host.PortIdentifierValidator(identifiers[i]); err != nil { - return sdkerrors.Wrapf(err, "invalid port ID at position %d", i) + return errorsmod.Wrapf(err, "invalid port ID at position %d", i) } if err := host.ChannelIdentifierValidator(identifiers[i+1]); err != nil { - return sdkerrors.Wrapf(err, "invalid channel ID at position %d", i) + return errorsmod.Wrapf(err, "invalid channel ID at position %d", i) } } return nil @@ -151,7 +151,7 @@ func (t Traces) Validate() error { } if err := trace.Validate(); err != nil { - return sdkerrors.Wrapf(err, "failed denom trace %d validation", i) + return errorsmod.Wrapf(err, "failed denom trace %d validation", i) } seenTraces[hash] = true } @@ -178,8 +178,8 @@ 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: '{portIDN}/{channelIDN}/.../{portID0}/{channelID0}/baseDenom' -// - Unprefixed denomination: 'baseDenom' +// - Prefixed denomination: '{portIDN}/{channelIDN}/.../{portID0}/{channelID0}/baseDenom' +// - Unprefixed denomination: 'baseDenom' // // 'baseDenom' may or may not contain '/'s func ValidatePrefixedDenom(denom string) error { @@ -190,7 +190,7 @@ func ValidatePrefixedDenom(denom string) error { } if strings.TrimSpace(denomSplit[len(denomSplit)-1]) == "" { - return sdkerrors.Wrap(ErrInvalidDenomForTransfer, "base denomination cannot be blank") + return errorsmod.Wrap(ErrInvalidDenomForTransfer, "base denomination cannot be blank") } path, _ := extractPathAndBaseFromFullDenom(denomSplit) @@ -205,8 +205,8 @@ func ValidatePrefixedDenom(denom string) error { // ValidateIBCDenom validates that the given denomination is either: // -// - A valid base denomination (eg: 'uatom' or 'gamm/pool/1' as in https://github.com/cosmos/ibc-go/issues/894) -// - A valid fungible token representation (i.e 'ibc/{hash}') per ADR 001 https://github.com/cosmos/ibc-go/blob/main/docs/architecture/adr-001-coin-source-tracing.md +// - A valid base denomination (eg: 'uatom' or 'gamm/pool/1' as in https://github.com/cosmos/ibc-go/issues/894) +// - A valid fungible token representation (i.e 'ibc/{hash}') per ADR 001 https://github.com/cosmos/ibc-go/blob/main/docs/architecture/adr-001-coin-source-tracing.md func ValidateIBCDenom(denom string) error { if err := sdk.ValidateDenom(denom); err != nil { return err @@ -216,15 +216,15 @@ func ValidateIBCDenom(denom string) error { switch { case denom == DenomPrefix: - return sdkerrors.Wrapf(ErrInvalidDenomForTransfer, "denomination should be prefixed with the format 'ibc/{hash(trace + \"/\" + %s)}'", denom) + return errorsmod.Wrapf(ErrInvalidDenomForTransfer, "denomination should be prefixed with the format 'ibc/{hash(trace + \"/\" + %s)}'", denom) case len(denomSplit) == 2 && denomSplit[0] == DenomPrefix: if strings.TrimSpace(denomSplit[1]) == "" { - return sdkerrors.Wrapf(ErrInvalidDenomForTransfer, "denomination should be prefixed with the format 'ibc/{hash(trace + \"/\" + %s)}'", denom) + return errorsmod.Wrapf(ErrInvalidDenomForTransfer, "denomination should be prefixed with the format 'ibc/{hash(trace + \"/\" + %s)}'", denom) } if _, err := ParseHexHash(denomSplit[1]); err != nil { - return sdkerrors.Wrapf(err, "invalid denom trace hash %s", denomSplit[1]) + return errorsmod.Wrapf(err, "invalid denom trace hash %s", denomSplit[1]) } } diff --git a/modules/apps/transfer/types/trace_test.go b/modules/apps/transfer/types/trace_test.go index ba0690423bd..21e9892a2c3 100644 --- a/modules/apps/transfer/types/trace_test.go +++ b/modules/apps/transfer/types/trace_test.go @@ -1,43 +1,45 @@ -package types +package types_test import ( "testing" "github.com/stretchr/testify/require" + + "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" ) func TestParseDenomTrace(t *testing.T) { testCases := []struct { name string denom string - expTrace DenomTrace + expTrace types.DenomTrace }{ - {"empty denom", "", DenomTrace{}}, - {"base denom", "uatom", DenomTrace{BaseDenom: "uatom"}}, - {"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/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: "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/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: ""}}, + {"empty denom", "", types.DenomTrace{}}, + {"base denom", "uatom", types.DenomTrace{BaseDenom: "uatom"}}, + {"base denom ending with '/'", "uatom/", types.DenomTrace{BaseDenom: "uatom/"}}, + {"base denom with single '/'s", "gamm/pool/1", types.DenomTrace{BaseDenom: "gamm/pool/1"}}, + {"base denom with double '/'s", "gamm//pool//1", types.DenomTrace{BaseDenom: "gamm//pool//1"}}, + {"trace info", "transfer/channel-1/uatom", types.DenomTrace{BaseDenom: "uatom", Path: "transfer/channel-1"}}, + {"trace info with custom port", "customtransfer/channel-1/uatom", types.DenomTrace{BaseDenom: "uatom", Path: "customtransfer/channel-1"}}, + {"trace info with base denom ending in '/'", "transfer/channel-1/uatom/", types.DenomTrace{BaseDenom: "uatom/", Path: "transfer/channel-1"}}, + {"trace info with single '/' in base denom", "transfer/channel-1/erc20/0x85bcBCd7e79Ec36f4fBBDc54F90C643d921151AA", types.DenomTrace{BaseDenom: "erc20/0x85bcBCd7e79Ec36f4fBBDc54F90C643d921151AA", Path: "transfer/channel-1"}}, + {"trace info with multiple '/'s in base denom", "transfer/channel-1/gamm/pool/1", types.DenomTrace{BaseDenom: "gamm/pool/1", Path: "transfer/channel-1"}}, + {"trace info with multiple double '/'s in base denom", "transfer/channel-1/gamm//pool//1", types.DenomTrace{BaseDenom: "gamm//pool//1", Path: "transfer/channel-1"}}, + {"trace info with multiple port/channel pairs", "transfer/channel-1/transfer/channel-2/uatom", types.DenomTrace{BaseDenom: "uatom", Path: "transfer/channel-1/transfer/channel-2"}}, + {"trace info with multiple custom ports", "customtransfer/channel-1/alternativetransfer/channel-2/uatom", types.DenomTrace{BaseDenom: "uatom", Path: "customtransfer/channel-1/alternativetransfer/channel-2"}}, + {"incomplete path", "transfer/uatom", types.DenomTrace{BaseDenom: "transfer/uatom"}}, + {"invalid path (1)", "transfer//uatom", types.DenomTrace{BaseDenom: "transfer//uatom", Path: ""}}, + {"invalid path (2)", "channel-1/transfer/uatom", types.DenomTrace{BaseDenom: "channel-1/transfer/uatom"}}, + {"invalid path (3)", "uatom/transfer", types.DenomTrace{BaseDenom: "uatom/transfer"}}, + {"invalid path (4)", "transfer/channel-1", types.DenomTrace{BaseDenom: "transfer/channel-1"}}, + {"invalid path (5)", "transfer/channel-1/", types.DenomTrace{Path: "transfer/channel-1"}}, + {"invalid path (6)", "transfer/channel-1/transfer", types.DenomTrace{BaseDenom: "transfer", Path: "transfer/channel-1"}}, + {"invalid path (7)", "transfer/channel-1/transfer/channel-2", types.DenomTrace{Path: "transfer/channel-1/transfer/channel-2"}}, + {"invalid path (8)", "transfer/channelToA/uatom", types.DenomTrace{BaseDenom: "transfer/channelToA/uatom", Path: ""}}, } for _, tc := range testCases { - trace := ParseDenomTrace(tc.denom) + trace := types.ParseDenomTrace(tc.denom) require.Equal(t, tc.expTrace, trace, tc.name) } } @@ -45,11 +47,11 @@ func TestParseDenomTrace(t *testing.T) { func TestDenomTrace_IBCDenom(t *testing.T) { testCases := []struct { name string - trace DenomTrace + trace types.DenomTrace expDenom string }{ - {"base denom", DenomTrace{BaseDenom: "uatom"}, "uatom"}, - {"trace info", DenomTrace{BaseDenom: "uatom", Path: "transfer/channel-1"}, "ibc/C4CFF46FD6DE35CA4CF4CE031E643C8FDC9BA4B99AE598E9B0ED98FE3A2319F9"}, + {"base denom", types.DenomTrace{BaseDenom: "uatom"}, "uatom"}, + {"trace info", types.DenomTrace{BaseDenom: "uatom", Path: "transfer/channel-1"}, "ibc/C4CFF46FD6DE35CA4CF4CE031E643C8FDC9BA4B99AE598E9B0ED98FE3A2319F9"}, } for _, tc := range testCases { @@ -61,19 +63,19 @@ func TestDenomTrace_IBCDenom(t *testing.T) { func TestDenomTrace_Validate(t *testing.T) { testCases := []struct { name string - trace DenomTrace + trace types.DenomTrace expError bool }{ - {"base denom only", DenomTrace{BaseDenom: "uatom"}, false}, - {"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/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)/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}, + {"base denom only", types.DenomTrace{BaseDenom: "uatom"}, false}, + {"base denom only with single '/'", types.DenomTrace{BaseDenom: "erc20/0x85bcBCd7e79Ec36f4fBBDc54F90C643d921151AA"}, false}, + {"base denom only with multiple '/'s", types.DenomTrace{BaseDenom: "gamm/pool/1"}, false}, + {"empty DenomTrace", types.DenomTrace{}, true}, + {"valid single trace info", types.DenomTrace{BaseDenom: "uatom", Path: "transfer/channel-1"}, false}, + {"valid multiple trace info", types.DenomTrace{BaseDenom: "uatom", Path: "transfer/channel-1/transfer/channel-2"}, false}, + {"single trace identifier", types.DenomTrace{BaseDenom: "uatom", Path: "transfer"}, true}, + {"invalid port ID", types.DenomTrace{BaseDenom: "uatom", Path: "(transfer)/channel-1"}, true}, + {"invalid channel ID", types.DenomTrace{BaseDenom: "uatom", Path: "transfer/(channel-1)"}, true}, + {"empty base denom with trace", types.DenomTrace{BaseDenom: "", Path: "transfer/channel-1"}, true}, } for _, tc := range testCases { @@ -89,20 +91,20 @@ func TestDenomTrace_Validate(t *testing.T) { func TestTraces_Validate(t *testing.T) { testCases := []struct { name string - traces Traces + traces types.Traces expError bool }{ - {"empty Traces", Traces{}, false}, - {"valid multiple trace info", Traces{{BaseDenom: "uatom", Path: "transfer/channel-1/transfer/channel-2"}}, false}, + {"empty Traces", types.Traces{}, false}, + {"valid multiple trace info", types.Traces{{BaseDenom: "uatom", Path: "transfer/channel-1/transfer/channel-2"}}, false}, { "valid multiple trace info", - Traces{ + types.Traces{ {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/channel-1"}}, true}, + {"empty base denom with trace", types.Traces{{BaseDenom: "", Path: "transfer/channel-1"}}, true}, } for _, tc := range testCases { @@ -134,7 +136,7 @@ func TestValidatePrefixedDenom(t *testing.T) { } for _, tc := range testCases { - err := ValidatePrefixedDenom(tc.denom) + err := types.ValidatePrefixedDenom(tc.denom) if tc.expError { require.Error(t, err, tc.name) } else { @@ -162,7 +164,7 @@ func TestValidateIBCDenom(t *testing.T) { } for _, tc := range testCases { - err := ValidateIBCDenom(tc.denom) + err := types.ValidateIBCDenom(tc.denom) if tc.expError { require.Error(t, err, tc.name) continue diff --git a/modules/apps/transfer/types/transfer.pb.go b/modules/apps/transfer/types/transfer.pb.go index 9ef18d9d5b0..6669bda8872 100644 --- a/modules/apps/transfer/types/transfer.pb.go +++ b/modules/apps/transfer/types/transfer.pb.go @@ -5,8 +5,8 @@ package types import ( fmt "fmt" - _ "github.com/gogo/protobuf/gogoproto" - proto "github.com/gogo/protobuf/proto" + _ "github.com/cosmos/gogoproto/gogoproto" + proto "github.com/cosmos/gogoproto/proto" io "io" math "math" math_bits "math/bits" @@ -153,23 +153,23 @@ var fileDescriptor_5041673e96e97901 = []byte{ // 300 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x5c, 0x90, 0xc1, 0x4a, 0x2b, 0x31, 0x14, 0x86, 0x9b, 0x72, 0x29, 0xb7, 0x51, 0x14, 0xa2, 0x68, 0x29, 0x9a, 0xca, 0xac, 0x04, 0x71, - 0x42, 0x51, 0x10, 0xba, 0x11, 0xaa, 0xee, 0xb5, 0xb8, 0x72, 0x53, 0x92, 0xcc, 0x71, 0x1a, 0x98, - 0x4c, 0x86, 0x24, 0x1d, 0xe8, 0x23, 0xb8, 0xf3, 0xb1, 0x5c, 0x76, 0xe9, 0xaa, 0x48, 0xfb, 0x06, - 0x7d, 0x02, 0x99, 0xb4, 0x94, 0xc1, 0xdd, 0x7f, 0xce, 0xf9, 0xbe, 0xb3, 0xf8, 0xf1, 0x95, 0x12, - 0x92, 0xf1, 0xa2, 0xc8, 0x94, 0xe4, 0x5e, 0x99, 0xdc, 0x31, 0x6f, 0x79, 0xee, 0xde, 0xc1, 0xb2, - 0xb2, 0xbf, 0xcb, 0x71, 0x61, 0x8d, 0x37, 0xe4, 0x4c, 0x09, 0x19, 0xd7, 0xe1, 0x78, 0x07, 0x94, - 0xfd, 0xee, 0x71, 0x6a, 0x52, 0x13, 0x40, 0x56, 0xa5, 0x8d, 0x13, 0xdd, 0x63, 0xfc, 0x08, 0xb9, - 0xd1, 0xaf, 0x96, 0x4b, 0x20, 0x04, 0xff, 0x2b, 0xb8, 0x9f, 0x74, 0xd0, 0x05, 0xba, 0x6c, 0x8f, - 0x42, 0x26, 0xe7, 0x18, 0x0b, 0xee, 0x60, 0x9c, 0x54, 0x58, 0xa7, 0x19, 0x2e, 0xed, 0x6a, 0x13, - 0xbc, 0xe8, 0x03, 0xe1, 0xd6, 0x33, 0xb7, 0x5c, 0x3b, 0x32, 0xc0, 0xfb, 0x0e, 0xf2, 0x64, 0x0c, - 0x39, 0x17, 0x19, 0x24, 0xe1, 0xcb, 0xff, 0xe1, 0xe9, 0x7a, 0xd1, 0x3b, 0x9a, 0x71, 0x9d, 0x0d, - 0xa2, 0xfa, 0x35, 0x1a, 0xed, 0x55, 0xe3, 0xd3, 0x66, 0x22, 0x0f, 0xf8, 0xd0, 0x82, 0x04, 0x55, - 0xc2, 0x4e, 0x6f, 0x06, 0xbd, 0xbb, 0x5e, 0xf4, 0x4e, 0x36, 0xfa, 0x1f, 0x20, 0x1a, 0x1d, 0x6c, - 0x37, 0xdb, 0x27, 0xc3, 0x97, 0xaf, 0x25, 0x45, 0xf3, 0x25, 0x45, 0x3f, 0x4b, 0x8a, 0x3e, 0x57, - 0xb4, 0x31, 0x5f, 0xd1, 0xc6, 0xf7, 0x8a, 0x36, 0xde, 0xee, 0x52, 0xe5, 0x27, 0x53, 0x11, 0x4b, - 0xa3, 0x99, 0x34, 0x4e, 0x1b, 0xc7, 0x94, 0x90, 0xd7, 0xa9, 0x61, 0xe5, 0x2d, 0xd3, 0x26, 0x99, - 0x66, 0xe0, 0xaa, 0x9e, 0x6b, 0xfd, 0xfa, 0x59, 0x01, 0x4e, 0xb4, 0x42, 0x4d, 0x37, 0xbf, 0x01, - 0x00, 0x00, 0xff, 0xff, 0x71, 0xb6, 0xa1, 0xba, 0x89, 0x01, 0x00, 0x00, + 0x42, 0x71, 0x51, 0xe8, 0x46, 0xa8, 0xba, 0xd7, 0xe2, 0xca, 0x4d, 0x49, 0x32, 0xc7, 0x69, 0x60, + 0x32, 0x19, 0x92, 0x74, 0xa0, 0x8f, 0xe0, 0xce, 0xc7, 0x72, 0xd9, 0xa5, 0xab, 0x22, 0xed, 0x1b, + 0xf4, 0x09, 0x64, 0xd2, 0x52, 0x06, 0x77, 0xff, 0x39, 0xe7, 0xfb, 0xce, 0xe2, 0xc7, 0x37, 0x4a, + 0x48, 0xc6, 0x8b, 0x22, 0x53, 0x92, 0x7b, 0x65, 0x72, 0xc7, 0xbc, 0xe5, 0xb9, 0x7b, 0x07, 0xcb, + 0xca, 0xfe, 0x3e, 0xc7, 0x85, 0x35, 0xde, 0x90, 0x0b, 0x25, 0x64, 0x5c, 0x87, 0xe3, 0x3d, 0x50, + 0xf6, 0xbb, 0xa7, 0xa9, 0x49, 0x4d, 0x00, 0x59, 0x95, 0xb6, 0x4e, 0x74, 0x8f, 0xf1, 0x23, 0xe4, + 0x46, 0xbf, 0x5a, 0x2e, 0x81, 0x10, 0xfc, 0xaf, 0xe0, 0x7e, 0xda, 0x41, 0x57, 0xe8, 0xba, 0x3d, + 0x0e, 0x99, 0x5c, 0x62, 0x2c, 0xb8, 0x83, 0x49, 0x52, 0x61, 0x9d, 0x66, 0xb8, 0xb4, 0xab, 0x4d, + 0xf0, 0xa2, 0x0f, 0x84, 0x5b, 0xcf, 0xdc, 0x72, 0xed, 0xc8, 0x10, 0x1f, 0x3a, 0xc8, 0x93, 0x09, + 0xe4, 0x5c, 0x64, 0x90, 0x84, 0x2f, 0xff, 0x47, 0xe7, 0x9b, 0x65, 0xef, 0x64, 0xce, 0x75, 0x36, + 0x8c, 0xea, 0xd7, 0x68, 0x7c, 0x50, 0x8d, 0x4f, 0xdb, 0x89, 0x3c, 0xe0, 0x63, 0x0b, 0x12, 0x54, + 0x09, 0x7b, 0xbd, 0x19, 0xf4, 0xee, 0x66, 0xd9, 0x3b, 0xdb, 0xea, 0x7f, 0x80, 0x68, 0x7c, 0xb4, + 0xdb, 0xec, 0x9e, 0x8c, 0x5e, 0xbe, 0x56, 0x14, 0x2d, 0x56, 0x14, 0xfd, 0xac, 0x28, 0xfa, 0x5c, + 0xd3, 0xc6, 0x62, 0x4d, 0x1b, 0xdf, 0x6b, 0xda, 0x78, 0x1b, 0xa4, 0xca, 0x4f, 0x67, 0x22, 0x96, + 0x46, 0x33, 0x69, 0x9c, 0x36, 0x8e, 0x29, 0x21, 0x6f, 0x53, 0xc3, 0xca, 0x01, 0xd3, 0x26, 0x99, + 0x65, 0xe0, 0xaa, 0x9e, 0x6b, 0xfd, 0xfa, 0x79, 0x01, 0x4e, 0xb4, 0x42, 0x4d, 0x77, 0xbf, 0x01, + 0x00, 0x00, 0xff, 0xff, 0x11, 0x89, 0xc0, 0xe0, 0x89, 0x01, 0x00, 0x00, } func (m *DenomTrace) Marshal() (dAtA []byte, err error) { diff --git a/modules/apps/transfer/types/transfer_authorization.go b/modules/apps/transfer/types/transfer_authorization.go new file mode 100644 index 00000000000..709972afc49 --- /dev/null +++ b/modules/apps/transfer/types/transfer_authorization.go @@ -0,0 +1,128 @@ +package types + +import ( + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/authz" + + ibcerrors "github.com/cosmos/ibc-go/v7/internal/errors" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" +) + +const gasCostPerIteration = uint64(10) + +var _ authz.Authorization = &TransferAuthorization{} + +// NewTransferAuthorization creates a new TransferAuthorization object. +func NewTransferAuthorization(allocations ...Allocation) *TransferAuthorization { + return &TransferAuthorization{ + Allocations: allocations, + } +} + +// MsgTypeURL implements Authorization.MsgTypeURL. +func (a TransferAuthorization) MsgTypeURL() string { + return sdk.MsgTypeURL(&MsgTransfer{}) +} + +// Accept implements Authorization.Accept. +func (a TransferAuthorization) Accept(ctx sdk.Context, msg sdk.Msg) (authz.AcceptResponse, error) { + msgTransfer, ok := msg.(*MsgTransfer) + if !ok { + return authz.AcceptResponse{}, errorsmod.Wrap(ibcerrors.ErrInvalidType, "type mismatch") + } + + for index, allocation := range a.Allocations { + if allocation.SourceChannel == msgTransfer.SourceChannel && allocation.SourcePort == msgTransfer.SourcePort { + limitLeft, isNegative := allocation.SpendLimit.SafeSub(msgTransfer.Token) + if isNegative { + return authz.AcceptResponse{}, errorsmod.Wrapf(ibcerrors.ErrInsufficientFunds, "requested amount is more than spend limit") + } + + if !isAllowedAddress(ctx, msgTransfer.Receiver, allocation.AllowList) { + return authz.AcceptResponse{}, errorsmod.Wrap(ibcerrors.ErrInvalidAddress, "not allowed address for transfer") + } + + if limitLeft.IsZero() { + a.Allocations = append(a.Allocations[:index], a.Allocations[index+1:]...) + if len(a.Allocations) == 0 { + return authz.AcceptResponse{Accept: true, Delete: true}, nil + } + return authz.AcceptResponse{Accept: true, Delete: false, Updated: &TransferAuthorization{ + Allocations: a.Allocations, + }}, nil + } + a.Allocations[index] = Allocation{ + SourcePort: allocation.SourcePort, + SourceChannel: allocation.SourceChannel, + SpendLimit: limitLeft, + AllowList: allocation.AllowList, + } + + return authz.AcceptResponse{Accept: true, Delete: false, Updated: &TransferAuthorization{ + Allocations: a.Allocations, + }}, nil + } + } + return authz.AcceptResponse{}, errorsmod.Wrapf(ibcerrors.ErrNotFound, "requested port and channel allocation does not exist") +} + +// ValidateBasic implements Authorization.ValidateBasic. +func (a TransferAuthorization) ValidateBasic() error { + if len(a.Allocations) == 0 { + return errorsmod.Wrap(ErrInvalidAuthorization, "allocations cannot be empty") + } + + foundChannels := make(map[string]bool, 0) + + for _, allocation := range a.Allocations { + if _, found := foundChannels[allocation.SourceChannel]; found { + return errorsmod.Wrapf(channeltypes.ErrInvalidChannel, "duplicate source channel ID: %s", allocation.SourceChannel) + } + + foundChannels[allocation.SourceChannel] = true + + if allocation.SpendLimit == nil { + return errorsmod.Wrap(ibcerrors.ErrInvalidCoins, "spend limit cannot be nil") + } + + if err := allocation.SpendLimit.Validate(); err != nil { + return errorsmod.Wrapf(ibcerrors.ErrInvalidCoins, err.Error()) + } + + if err := host.PortIdentifierValidator(allocation.SourcePort); err != nil { + return errorsmod.Wrap(err, "invalid source port ID") + } + + if err := host.ChannelIdentifierValidator(allocation.SourceChannel); err != nil { + return errorsmod.Wrap(err, "invalid source channel ID") + } + + found := make(map[string]bool, 0) + for i := 0; i < len(allocation.AllowList); i++ { + if found[allocation.AllowList[i]] { + return errorsmod.Wrapf(ErrInvalidAuthorization, "duplicate entry in allow list %s", allocation.AllowList[i]) + } + found[allocation.AllowList[i]] = true + } + } + + return nil +} + +// isAllowedAddress returns a boolean indicating if the receiver address is valid for transfer. +// gasCostPerIteration gas is consumed for each iteration. +func isAllowedAddress(ctx sdk.Context, receiver string, allowedAddrs []string) bool { + if len(allowedAddrs) == 0 { + return true + } + + for _, addr := range allowedAddrs { + ctx.GasMeter().ConsumeGas(gasCostPerIteration, "transfer authorization") + if addr == receiver { + return true + } + } + return false +} diff --git a/modules/apps/transfer/types/transfer_authorization_test.go b/modules/apps/transfer/types/transfer_authorization_test.go new file mode 100644 index 00000000000..f0f7f3ab0b9 --- /dev/null +++ b/modules/apps/transfer/types/transfer_authorization_test.go @@ -0,0 +1,282 @@ +package types_test + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/authz" + + "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" + ibctesting "github.com/cosmos/ibc-go/v7/testing" + "github.com/cosmos/ibc-go/v7/testing/mock" +) + +func (suite *TypesTestSuite) TestTransferAuthorizationAccept() { + var ( + msgTransfer types.MsgTransfer + transferAuthz types.TransferAuthorization + ) + + testCases := []struct { + name string + malleate func() + assertResult func(res authz.AcceptResponse, err error) + }{ + { + "success", + func() {}, + func(res authz.AcceptResponse, err error) { + suite.Require().NoError(err) + + suite.Require().True(res.Accept) + suite.Require().True(res.Delete) + suite.Require().Nil(res.Updated) + }, + }, + { + "success: with spend limit updated", + func() { + msgTransfer.Token = sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(50)) + }, + func(res authz.AcceptResponse, err error) { + suite.Require().NoError(err) + + suite.Require().True(res.Accept) + suite.Require().False(res.Delete) + + updatedAuthz, ok := res.Updated.(*types.TransferAuthorization) + suite.Require().True(ok) + + isEqual := updatedAuthz.Allocations[0].SpendLimit.IsEqual(sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(50)))) + suite.Require().True(isEqual) + }, + }, + { + "success: with empty allow list", + func() { + transferAuthz.Allocations[0].AllowList = []string{} + }, + func(res authz.AcceptResponse, err error) { + suite.Require().NoError(err) + + suite.Require().True(res.Accept) + suite.Require().True(res.Delete) + suite.Require().Nil(res.Updated) + }, + }, + { + "success: with multiple allocations", + func() { + alloc := types.Allocation{ + SourcePort: ibctesting.MockPort, + SourceChannel: "channel-9", + SpendLimit: ibctesting.TestCoins, + } + + transferAuthz.Allocations = append(transferAuthz.Allocations, alloc) + }, + func(res authz.AcceptResponse, err error) { + suite.Require().NoError(err) + + suite.Require().True(res.Accept) + suite.Require().False(res.Delete) + + updatedAuthz, ok := res.Updated.(*types.TransferAuthorization) + suite.Require().True(ok) + + // assert spent spendlimit is removed from the list + suite.Require().Len(updatedAuthz.Allocations, 1) + }, + }, + { + "no spend limit set for MsgTransfer port/channel", + func() { + msgTransfer.SourcePort = ibctesting.MockPort + msgTransfer.SourceChannel = "channel-9" + }, + func(res authz.AcceptResponse, err error) { + suite.Require().Error(err) + }, + }, + { + "requested transfer amount is more than the spend limit", + func() { + msgTransfer.Token = sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(1000)) + }, + func(res authz.AcceptResponse, err error) { + suite.Require().Error(err) + }, + }, + { + "receiver address not permitted via allow list", + func() { + msgTransfer.Receiver = suite.chainB.SenderAccount.GetAddress().String() + }, + func(res authz.AcceptResponse, err error) { + suite.Require().Error(err) + }, + }, + } + + for _, tc := range testCases { + suite.Run(tc.name, func() { + suite.SetupTest() + + path := NewTransferPath(suite.chainA, suite.chainB) + suite.coordinator.Setup(path) + + transferAuthz = types.TransferAuthorization{ + Allocations: []types.Allocation{ + { + SourcePort: path.EndpointA.ChannelConfig.PortID, + SourceChannel: path.EndpointA.ChannelID, + SpendLimit: ibctesting.TestCoins, + AllowList: []string{ibctesting.TestAccAddress}, + }, + }, + } + + msgTransfer = types.MsgTransfer{ + SourcePort: path.EndpointA.ChannelConfig.PortID, + SourceChannel: path.EndpointA.ChannelID, + Token: ibctesting.TestCoin, + Sender: suite.chainA.SenderAccount.GetAddress().String(), + Receiver: ibctesting.TestAccAddress, + TimeoutHeight: suite.chainB.GetTimeoutHeight(), + } + + tc.malleate() + + res, err := transferAuthz.Accept(suite.chainA.GetContext(), &msgTransfer) + tc.assertResult(res, err) + }) + } +} + +func (suite *TypesTestSuite) TestTransferAuthorizationMsgTypeURL() { + var transferAuthz types.TransferAuthorization + suite.Require().Equal(sdk.MsgTypeURL(&types.MsgTransfer{}), transferAuthz.MsgTypeURL(), "invalid type url for transfer authorization") +} + +func (suite *TypesTestSuite) TestTransferAuthorizationValidateBasic() { + var transferAuthz types.TransferAuthorization + + testCases := []struct { + name string + malleate func() + expPass bool + }{ + { + "success", + func() {}, + true, + }, + { + "success: empty allow list", + func() { + transferAuthz.Allocations[0].AllowList = []string{} + }, + true, + }, + { + "success: with multiple allocations", + func() { + allocation := types.Allocation{ + SourcePort: types.PortID, + SourceChannel: "channel-1", + SpendLimit: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100))), + AllowList: []string{}, + } + + transferAuthz.Allocations = append(transferAuthz.Allocations, allocation) + }, + true, + }, + { + "empty allocations", + func() { + transferAuthz = types.TransferAuthorization{Allocations: []types.Allocation{}} + }, + false, + }, + { + "nil allocations", + func() { + transferAuthz = types.TransferAuthorization{} + }, + false, + }, + { + "nil spend limit coins", + func() { + transferAuthz.Allocations[0].SpendLimit = nil + }, + false, + }, + { + "invalid spend limit coins", + func() { + transferAuthz.Allocations[0].SpendLimit = sdk.Coins{sdk.Coin{Denom: ""}} + }, + false, + }, + { + "duplicate entry in allow list", + func() { + transferAuthz.Allocations[0].AllowList = []string{ibctesting.TestAccAddress, ibctesting.TestAccAddress} + }, + false, + }, + { + "invalid port identifier", + func() { + transferAuthz.Allocations[0].SourcePort = "" + }, + false, + }, + { + "invalid channel identifier", + func() { + transferAuthz.Allocations[0].SourceChannel = "" + }, + false, + }, + { + name: "duplicate channel ID", + malleate: func() { + allocation := types.Allocation{ + SourcePort: mock.PortID, + SourceChannel: transferAuthz.Allocations[0].SourceChannel, + SpendLimit: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100))), + AllowList: []string{ibctesting.TestAccAddress}, + } + + transferAuthz.Allocations = append(transferAuthz.Allocations, allocation) + }, + expPass: false, + }, + } + + for _, tc := range testCases { + suite.Run(tc.name, func() { + transferAuthz = types.TransferAuthorization{ + Allocations: []types.Allocation{ + { + SourcePort: mock.PortID, + SourceChannel: ibctesting.FirstChannelID, + SpendLimit: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100))), + AllowList: []string{ibctesting.TestAccAddress}, + }, + }, + } + + tc.malleate() + + err := transferAuthz.ValidateBasic() + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } +} diff --git a/modules/apps/transfer/types/tx.pb.go b/modules/apps/transfer/types/tx.pb.go index 1cf3dcabc22..077de524251 100644 --- a/modules/apps/transfer/types/tx.pb.go +++ b/modules/apps/transfer/types/tx.pb.go @@ -7,10 +7,10 @@ import ( context "context" fmt "fmt" types "github.com/cosmos/cosmos-sdk/types" - types1 "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - _ "github.com/gogo/protobuf/gogoproto" - grpc1 "github.com/gogo/protobuf/grpc" - proto "github.com/gogo/protobuf/proto" + _ "github.com/cosmos/gogoproto/gogoproto" + grpc1 "github.com/cosmos/gogoproto/grpc" + proto "github.com/cosmos/gogoproto/proto" + types1 "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" @@ -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, + // 517 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x53, 0xc1, 0x6e, 0xd3, 0x4c, + 0x10, 0xb6, 0xff, 0xa4, 0xf9, 0xc3, 0x46, 0xad, 0x60, 0x81, 0xca, 0x8d, 0x8a, 0x1d, 0x59, 0x42, + 0x0a, 0x07, 0x76, 0xe5, 0x22, 0x14, 0xa9, 0x27, 0x94, 0x5e, 0xe0, 0x50, 0x09, 0xac, 0x9e, 0xb8, + 0x14, 0x7b, 0x3b, 0x38, 0x2b, 0x62, 0x8f, 0xf1, 0x6e, 0x2c, 0xfa, 0x06, 0x1c, 0x79, 0x84, 0x3e, + 0x09, 0xe7, 0x1e, 0x7b, 0xe4, 0x14, 0xa1, 0xe4, 0xc2, 0x39, 0x4f, 0x80, 0xd6, 0x76, 0x42, 0x72, + 0x41, 0x9c, 0x3c, 0x33, 0xdf, 0x37, 0xfe, 0xf6, 0xdb, 0x99, 0x25, 0x4f, 0x65, 0x2c, 0x78, 0x94, + 0xe7, 0x53, 0x29, 0x22, 0x2d, 0x31, 0x53, 0x5c, 0x17, 0x51, 0xa6, 0x3e, 0x42, 0xc1, 0xcb, 0x80, + 0xeb, 0x2f, 0x2c, 0x2f, 0x50, 0x23, 0x3d, 0x96, 0xb1, 0x60, 0xdb, 0x34, 0xb6, 0xa6, 0xb1, 0x32, + 0xe8, 0x3f, 0x4a, 0x30, 0xc1, 0x8a, 0xc8, 0x4d, 0x54, 0xf7, 0xf4, 0x5d, 0x81, 0x2a, 0x45, 0xc5, + 0xe3, 0x48, 0x01, 0x2f, 0x83, 0x18, 0x74, 0x14, 0x70, 0x81, 0x32, 0x6b, 0x70, 0xcf, 0x48, 0x0b, + 0x2c, 0x80, 0x8b, 0xa9, 0x84, 0x4c, 0x1b, 0xc1, 0x3a, 0xaa, 0x09, 0xfe, 0xf7, 0x16, 0xe9, 0x9d, + 0xab, 0xe4, 0xa2, 0x51, 0xa2, 0x23, 0xd2, 0x53, 0x38, 0x2b, 0x04, 0x5c, 0xe6, 0x58, 0x68, 0xc7, + 0x1e, 0xd8, 0xc3, 0x7b, 0xe3, 0xc3, 0xd5, 0xdc, 0xa3, 0xd7, 0x51, 0x3a, 0x3d, 0xf5, 0xb7, 0x40, + 0x3f, 0x24, 0x75, 0xf6, 0x16, 0x0b, 0x4d, 0x5f, 0x91, 0x83, 0x06, 0x13, 0x93, 0x28, 0xcb, 0x60, + 0xea, 0xfc, 0x57, 0xf5, 0x1e, 0xad, 0xe6, 0xde, 0xe3, 0x9d, 0xde, 0x06, 0xf7, 0xc3, 0xfd, 0xba, + 0x70, 0x56, 0xe7, 0xf4, 0x25, 0xd9, 0xd3, 0xf8, 0x09, 0x32, 0xa7, 0x35, 0xb0, 0x87, 0xbd, 0x93, + 0x23, 0x56, 0x7b, 0x63, 0xc6, 0x1b, 0x6b, 0xbc, 0xb1, 0x33, 0x94, 0xd9, 0xb8, 0x7d, 0x3b, 0xf7, + 0xac, 0xb0, 0x66, 0xd3, 0x43, 0xd2, 0x51, 0x90, 0x5d, 0x41, 0xe1, 0xb4, 0x8d, 0x60, 0xd8, 0x64, + 0xb4, 0x4f, 0xba, 0x05, 0x08, 0x90, 0x25, 0x14, 0xce, 0x5e, 0x85, 0x6c, 0x72, 0xfa, 0x81, 0x1c, + 0x68, 0x99, 0x02, 0xce, 0xf4, 0xe5, 0x04, 0x64, 0x32, 0xd1, 0x4e, 0xa7, 0xd2, 0xec, 0x33, 0x33, + 0x03, 0x73, 0x5f, 0xac, 0xb9, 0xa5, 0x32, 0x60, 0xaf, 0x2b, 0xc6, 0xf8, 0x89, 0x11, 0xfd, 0x63, + 0x66, 0xb7, 0xdf, 0x0f, 0xf7, 0x9b, 0x42, 0xcd, 0xa6, 0x6f, 0xc8, 0x83, 0x35, 0xc3, 0x7c, 0x95, + 0x8e, 0xd2, 0xdc, 0xf9, 0x7f, 0x60, 0x0f, 0xdb, 0xe3, 0xe3, 0xd5, 0xdc, 0x73, 0x76, 0x7f, 0xb2, + 0xa1, 0xf8, 0xe1, 0xfd, 0xa6, 0x76, 0xb1, 0x2e, 0x51, 0x4a, 0xda, 0x29, 0xa4, 0xe8, 0x74, 0x2b, + 0x13, 0x55, 0x7c, 0xda, 0xfd, 0x7a, 0xe3, 0x59, 0xbf, 0x6e, 0x3c, 0xcb, 0x0f, 0xc8, 0xc3, 0xad, + 0xf9, 0x85, 0xa0, 0x72, 0xcc, 0x14, 0x18, 0xf7, 0x0a, 0x3e, 0xcf, 0x20, 0x13, 0x50, 0x0d, 0xb1, + 0x1d, 0x6e, 0xf2, 0x13, 0x24, 0xad, 0x73, 0x95, 0xd0, 0x09, 0xe9, 0x6e, 0xc6, 0xfe, 0x8c, 0xfd, + 0x6d, 0xf9, 0xd8, 0x96, 0x42, 0x3f, 0xf8, 0x67, 0xea, 0xfa, 0x30, 0xe3, 0x77, 0xb7, 0x0b, 0xd7, + 0xbe, 0x5b, 0xb8, 0xf6, 0xcf, 0x85, 0x6b, 0x7f, 0x5b, 0xba, 0xd6, 0xdd, 0xd2, 0xb5, 0x7e, 0x2c, + 0x5d, 0xeb, 0xfd, 0x28, 0x91, 0x7a, 0x32, 0x8b, 0x99, 0xc0, 0x94, 0x37, 0xab, 0x2c, 0x63, 0xf1, + 0x3c, 0x41, 0x5e, 0x8e, 0x78, 0x8a, 0x57, 0xb3, 0x29, 0x28, 0xf3, 0x74, 0xb6, 0x9e, 0x8c, 0xbe, + 0xce, 0x41, 0xc5, 0x9d, 0x6a, 0x7d, 0x5f, 0xfc, 0x0e, 0x00, 0x00, 0xff, 0xff, 0xde, 0x3d, 0xc2, + 0x16, 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/apps/transfer/types/ack_test.go b/modules/apps/transfer/types/types_test.go similarity index 54% rename from modules/apps/transfer/types/ack_test.go rename to modules/apps/transfer/types/types_test.go index b0782dd96ba..86069b1a887 100644 --- a/modules/apps/transfer/types/ack_test.go +++ b/modules/apps/transfer/types/types_test.go @@ -5,7 +5,8 @@ import ( "github.com/stretchr/testify/suite" - ibctesting "github.com/cosmos/ibc-go/v4/testing" + "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" + ibctesting "github.com/cosmos/ibc-go/v7/testing" ) type TypesTestSuite struct { @@ -24,6 +25,16 @@ func (suite *TypesTestSuite) SetupTest() { suite.chainB = suite.coordinator.GetChain(ibctesting.GetChainID(2)) } +func NewTransferPath(chainA, chainB *ibctesting.TestChain) *ibctesting.Path { + path := ibctesting.NewPath(chainA, chainB) + path.EndpointA.ChannelConfig.PortID = types.PortID + path.EndpointB.ChannelConfig.PortID = types.PortID + path.EndpointA.ChannelConfig.Version = types.Version + path.EndpointB.ChannelConfig.Version = types.Version + + return path +} + func TestTypesTestSuite(t *testing.T) { suite.Run(t, new(TypesTestSuite)) } diff --git a/modules/core/02-client/abci.go b/modules/core/02-client/abci.go index 87039aeee10..735834b2f65 100644 --- a/modules/core/02-client/abci.go +++ b/modules/core/02-client/abci.go @@ -3,12 +3,11 @@ package client import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/ibc-go/v4/modules/core/02-client/keeper" - "github.com/cosmos/ibc-go/v4/modules/core/exported" - ibctmtypes "github.com/cosmos/ibc-go/v4/modules/light-clients/07-tendermint/types" + "github.com/cosmos/ibc-go/v7/modules/core/02-client/keeper" + ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" ) -// BeginBlocker updates an existing localhost client with the latest block height. +// BeginBlocker is used to perform IBC client upgrades func BeginBlocker(ctx sdk.Context, k keeper.Keeper) { plan, found := k.GetUpgradePlan(ctx) if found { @@ -20,23 +19,16 @@ func BeginBlocker(ctx sdk.Context, k keeper.Keeper) { // within the trusting period of the last block time on this chain. _, exists := k.GetUpgradedClient(ctx, plan.Height) if exists && ctx.BlockHeight() == plan.Height-1 { - upgradedConsState := &ibctmtypes.ConsensusState{ + upgradedConsState := &ibctm.ConsensusState{ Timestamp: ctx.BlockTime(), NextValidatorsHash: ctx.BlockHeader().NextValidatorsHash, } bz := k.MustMarshalConsensusState(upgradedConsState) - k.SetUpgradedConsensusState(ctx, plan.Height, bz) - } - } + // SetUpgradedConsensusState always returns nil, hence the blank here. + _ = k.SetUpgradedConsensusState(ctx, plan.Height, bz) - _, found = k.GetClientState(ctx, exported.Localhost) - if !found { - return - } - - // update the localhost client with the latest block height - if err := k.UpdateClient(ctx, exported.Localhost, nil); err != nil { - panic(err) + keeper.EmitUpgradeChainEvent(ctx, plan.Height) + } } } diff --git a/modules/core/02-client/abci_test.go b/modules/core/02-client/abci_test.go index 27f8f7f4b12..c5dbf2c8a55 100644 --- a/modules/core/02-client/abci_test.go +++ b/modules/core/02-client/abci_test.go @@ -1,19 +1,19 @@ package client_test import ( + "strings" "testing" + sdk "github.com/cosmos/cosmos-sdk/types" upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" "github.com/stretchr/testify/suite" abci "github.com/tendermint/tendermint/abci/types" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" - client "github.com/cosmos/ibc-go/v4/modules/core/02-client" - "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v4/modules/core/exported" - ibctmtypes "github.com/cosmos/ibc-go/v4/modules/light-clients/07-tendermint/types" - localhosttypes "github.com/cosmos/ibc-go/v4/modules/light-clients/09-localhost/types" - ibctesting "github.com/cosmos/ibc-go/v4/testing" + client "github.com/cosmos/ibc-go/v7/modules/core/02-client" + "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" + ibctesting "github.com/cosmos/ibc-go/v7/testing" ) type ClientTestSuite struct { @@ -30,13 +30,6 @@ func (suite *ClientTestSuite) SetupTest() { suite.chainA = suite.coordinator.GetChain(ibctesting.GetChainID(1)) suite.chainB = suite.coordinator.GetChain(ibctesting.GetChainID(2)) - - // set localhost client - revision := types.ParseChainID(suite.chainA.GetContext().ChainID()) - localHostClient := localhosttypes.NewClientState( - suite.chainA.GetContext().ChainID(), types.NewHeight(revision, uint64(suite.chainA.GetContext().BlockHeight())), - ) - suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), exported.Localhost, localHostClient) } func TestClientTestSuite(t *testing.T) { @@ -44,11 +37,6 @@ func TestClientTestSuite(t *testing.T) { } func (suite *ClientTestSuite) TestBeginBlocker() { - prevHeight := types.GetSelfHeight(suite.chainA.GetContext()) - - localHostClient := suite.chainA.GetClientState(exported.Localhost) - suite.Require().Equal(prevHeight, localHostClient.GetLatestHeight()) - for i := 0; i < 10; i++ { // increment height suite.coordinator.CommitBlock(suite.chainA, suite.chainB) @@ -56,10 +44,6 @@ func (suite *ClientTestSuite) TestBeginBlocker() { suite.Require().NotPanics(func() { client.BeginBlocker(suite.chainA.GetContext(), suite.chainA.App.GetIBCKeeper().ClientKeeper) }, "BeginBlocker shouldn't panic") - - localHostClient = suite.chainA.GetClientState(exported.Localhost) - suite.Require().Equal(prevHeight.Increment(), localHostClient.GetLatestHeight()) - prevHeight = localHostClient.GetLatestHeight().(types.Height) } } @@ -88,7 +72,59 @@ func (suite *ClientTestSuite) TestBeginBlockerConsensusState() { // plan Height is at ctx.BlockHeight+1 consState, found := suite.chainA.GetSimApp().UpgradeKeeper.GetUpgradedConsensusState(newCtx, plan.Height) suite.Require().True(found) - bz, err = types.MarshalConsensusState(suite.chainA.App.AppCodec(), &ibctmtypes.ConsensusState{Timestamp: newCtx.BlockTime(), NextValidatorsHash: nextValsHash}) + bz, err = types.MarshalConsensusState(suite.chainA.App.AppCodec(), &ibctm.ConsensusState{Timestamp: newCtx.BlockTime(), NextValidatorsHash: nextValsHash}) suite.Require().NoError(err) suite.Require().Equal(bz, consState) } + +func (suite *ClientTestSuite) TestBeginBlockerUpgradeEvents() { + plan := &upgradetypes.Plan{ + Name: "test", + Height: suite.chainA.GetContext().BlockHeight() + 1, + } + // set upgrade plan in the upgrade store + store := suite.chainA.GetContext().KVStore(suite.chainA.GetSimApp().GetKey(upgradetypes.StoreKey)) + bz := suite.chainA.App.AppCodec().MustMarshal(plan) + store.Set(upgradetypes.PlanKey(), bz) + + nextValsHash := []byte("nextValsHash") + newCtx := suite.chainA.GetContext().WithBlockHeader(tmproto.Header{ + Height: suite.chainA.GetContext().BlockHeight(), + NextValidatorsHash: nextValsHash, + }) + + err := suite.chainA.GetSimApp().UpgradeKeeper.SetUpgradedClient(newCtx, plan.Height, []byte("client state")) + suite.Require().NoError(err) + + cacheCtx, writeCache := suite.chainA.GetContext().CacheContext() + + client.BeginBlocker(cacheCtx, suite.chainA.App.GetIBCKeeper().ClientKeeper) + writeCache() + + suite.requireContainsEvent(cacheCtx.EventManager().Events(), types.EventTypeUpgradeChain, true) +} + +func (suite *ClientTestSuite) TestBeginBlockerUpgradeEventsAbsence() { + cacheCtx, writeCache := suite.chainA.GetContext().CacheContext() + client.BeginBlocker(suite.chainA.GetContext(), suite.chainA.App.GetIBCKeeper().ClientKeeper) + writeCache() + suite.requireContainsEvent(cacheCtx.EventManager().Events(), types.EventTypeUpgradeChain, false) +} + +// requireContainsEvent verifies if an event of a specific type was emitted. +func (suite *ClientTestSuite) requireContainsEvent(events sdk.Events, eventType string, shouldContain bool) { + found := false + var eventTypes []string + for _, e := range events { + eventTypes = append(eventTypes, e.Type) + if e.Type == eventType { + found = true + break + } + } + if shouldContain { + suite.Require().True(found, "event type %s was not found in %s", eventType, strings.Join(eventTypes, ",")) + } else { + suite.Require().False(found, "event type %s was found in %s", eventType, strings.Join(eventTypes, ",")) + } +} diff --git a/modules/core/02-client/client/cli/cli.go b/modules/core/02-client/client/cli/cli.go index 74bb514ae1d..f1017e2df0f 100644 --- a/modules/core/02-client/client/cli/cli.go +++ b/modules/core/02-client/client/cli/cli.go @@ -4,7 +4,7 @@ import ( "github.com/cosmos/cosmos-sdk/client" "github.com/spf13/cobra" - "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" ) // GetQueryCmd returns the query commands for IBC clients @@ -26,7 +26,7 @@ func GetQueryCmd() *cobra.Command { GetCmdQueryConsensusState(), GetCmdQueryHeader(), GetCmdSelfConsensusState(), - GetCmdParams(), + GetCmdClientParams(), ) return queryCmd @@ -45,7 +45,7 @@ func NewTxCmd() *cobra.Command { txCmd.AddCommand( NewCreateClientCmd(), NewUpdateClientCmd(), - NewSubmitMisbehaviourCmd(), + NewSubmitMisbehaviourCmd(), // Deprecated NewUpgradeClientCmd(), ) diff --git a/modules/core/02-client/client/cli/query.go b/modules/core/02-client/client/cli/query.go index 66e114902fe..337b142419c 100644 --- a/modules/core/02-client/client/cli/query.go +++ b/modules/core/02-client/client/cli/query.go @@ -9,9 +9,9 @@ import ( "github.com/cosmos/cosmos-sdk/version" "github.com/spf13/cobra" - "github.com/cosmos/ibc-go/v4/modules/core/02-client/client/utils" - "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" + "github.com/cosmos/ibc-go/v7/modules/core/02-client/client/utils" + "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + ibcexported "github.com/cosmos/ibc-go/v7/modules/core/exported" ) const ( @@ -25,7 +25,7 @@ func GetCmdQueryClientStates() *cobra.Command { Use: "states", Short: "Query all available light clients", Long: "Query all available light clients", - Example: fmt.Sprintf("%s query %s %s states", version.AppName, host.ModuleName, types.SubModuleName), + Example: fmt.Sprintf("%s query %s %s states", version.AppName, ibcexported.ModuleName, types.SubModuleName), Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, _ []string) error { clientCtx, err := client.GetClientQueryContext(cmd) @@ -64,7 +64,7 @@ func GetCmdQueryClientState() *cobra.Command { Use: "state [client-id]", Short: "Query a client state", Long: "Query stored client state", - Example: fmt.Sprintf("%s query %s %s state [client-id]", version.AppName, host.ModuleName, types.SubModuleName), + Example: fmt.Sprintf("%s query %s %s state [client-id]", version.AppName, ibcexported.ModuleName, types.SubModuleName), Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { clientCtx, err := client.GetClientQueryContext(cmd) @@ -95,7 +95,7 @@ func GetCmdQueryClientStatus() *cobra.Command { Use: "status [client-id]", Short: "Query client status", Long: "Query client activity status. Any client without an 'Active' status is considered inactive", - Example: fmt.Sprintf("%s query %s %s status [client-id]", version.AppName, host.ModuleName, types.SubModuleName), + Example: fmt.Sprintf("%s query %s %s status [client-id]", version.AppName, ibcexported.ModuleName, types.SubModuleName), Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { clientCtx, err := client.GetClientQueryContext(cmd) @@ -119,6 +119,8 @@ func GetCmdQueryClientStatus() *cobra.Command { }, } + flags.AddQueryFlagsToCmd(cmd) + return cmd } @@ -129,7 +131,7 @@ func GetCmdQueryConsensusStates() *cobra.Command { Use: "consensus-states [client-id]", Short: "Query all the consensus states of a client.", Long: "Query all the consensus states from a given client state.", - Example: fmt.Sprintf("%s query %s %s consensus-states [client-id]", version.AppName, host.ModuleName, types.SubModuleName), + Example: fmt.Sprintf("%s query %s %s consensus-states [client-id]", version.AppName, ibcexported.ModuleName, types.SubModuleName), Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { clientCtx, err := client.GetClientQueryContext(cmd) @@ -171,7 +173,7 @@ func GetCmdQueryConsensusStateHeights() *cobra.Command { Use: "consensus-state-heights [client-id]", Short: "Query the heights of all consensus states of a client.", Long: "Query the heights of all consensus states associated with the provided client ID.", - Example: fmt.Sprintf("%s query %s %s consensus-state-heights [client-id]", version.AppName, host.ModuleName, types.SubModuleName), + Example: fmt.Sprintf("%s query %s %s consensus-state-heights [client-id]", version.AppName, ibcexported.ModuleName, types.SubModuleName), Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { clientCtx, err := client.GetClientQueryContext(cmd) @@ -215,7 +217,7 @@ func GetCmdQueryConsensusState() *cobra.Command { Short: "Query the consensus state of a client at a given height", Long: `Query the consensus state for a particular light client at a given height. If the '--latest' flag is included, the query returns the latest consensus state, overriding the height argument.`, - Example: fmt.Sprintf("%s query %s %s consensus-state [client-id] [height]", version.AppName, host.ModuleName, types.SubModuleName), + Example: fmt.Sprintf("%s query %s %s consensus-state [client-id] [height]", version.AppName, ibcexported.ModuleName, types.SubModuleName), Args: cobra.RangeArgs(1, 2), RunE: func(cmd *cobra.Command, args []string) error { clientCtx, err := client.GetClientQueryContext(cmd) @@ -261,7 +263,7 @@ func GetCmdQueryHeader() *cobra.Command { Use: "header", Short: "Query the latest header of the running chain", Long: "Query the latest Tendermint header of the running chain", - Example: fmt.Sprintf("%s query %s %s header", version.AppName, host.ModuleName, types.SubModuleName), + Example: fmt.Sprintf("%s query %s %s header", version.AppName, ibcexported.ModuleName, types.SubModuleName), Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, _ []string) error { clientCtx, err := client.GetClientQueryContext(cmd) @@ -288,7 +290,7 @@ func GetCmdSelfConsensusState() *cobra.Command { Use: "self-consensus-state", Short: "Query the self consensus state for this chain", Long: "Query the self consensus state for this chain. This result may be used for verifying IBC clients representing this chain which are hosted on counterparty chains.", - Example: fmt.Sprintf("%s query %s %s self-consensus-state", version.AppName, host.ModuleName, types.SubModuleName), + Example: fmt.Sprintf("%s query %s %s self-consensus-state", version.AppName, ibcexported.ModuleName, types.SubModuleName), Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, _ []string) error { clientCtx, err := client.GetClientQueryContext(cmd) @@ -309,14 +311,14 @@ func GetCmdSelfConsensusState() *cobra.Command { return cmd } -// GetCmdParams returns the command handler for ibc client parameter querying. -func GetCmdParams() *cobra.Command { +// GetCmdClientParams returns the command handler for ibc client parameter querying. +func GetCmdClientParams() *cobra.Command { cmd := &cobra.Command{ Use: "params", Short: "Query the current ibc client parameters", Long: "Query the current ibc client parameters", Args: cobra.NoArgs, - Example: fmt.Sprintf("%s query %s %s params", version.AppName, host.ModuleName, types.SubModuleName), + Example: fmt.Sprintf("%s query %s %s params", version.AppName, ibcexported.ModuleName, types.SubModuleName), RunE: func(cmd *cobra.Command, _ []string) error { clientCtx, err := client.GetClientQueryContext(cmd) if err != nil { diff --git a/modules/core/02-client/client/cli/tx.go b/modules/core/02-client/client/cli/tx.go index fdf9b8e5b89..14401a659fc 100644 --- a/modules/core/02-client/client/cli/tx.go +++ b/modules/core/02-client/client/cli/tx.go @@ -2,7 +2,7 @@ package cli import ( "fmt" - "io/ioutil" + "os" "strconv" "github.com/cosmos/cosmos-sdk/client" @@ -12,12 +12,12 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/version" govcli "github.com/cosmos/cosmos-sdk/x/gov/client/cli" - govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" "github.com/spf13/cobra" - "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v4/modules/core/exported" + "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v7/modules/core/exported" ) // NewCreateClientCmd defines the command to create a new IBC light client. @@ -43,7 +43,7 @@ func NewCreateClientCmd() *cobra.Command { if err := cdc.UnmarshalInterfaceJSON([]byte(clientContentOrFileName), &clientState); err != nil { // check for file path if JSON input is not provided - contents, err := ioutil.ReadFile(clientContentOrFileName) + contents, err := os.ReadFile(clientContentOrFileName) if err != nil { return fmt.Errorf("neither JSON input nor path to .json file for client state were provided: %w", err) } @@ -59,7 +59,7 @@ func NewCreateClientCmd() *cobra.Command { if err := cdc.UnmarshalInterfaceJSON([]byte(consensusContentOrFileName), &consensusState); err != nil { // check for file path if JSON input is not provided - contents, err := ioutil.ReadFile(consensusContentOrFileName) + contents, err := os.ReadFile(consensusContentOrFileName) if err != nil { return fmt.Errorf("neither JSON input nor path to .json file for consensus state were provided: %w", err) } @@ -79,17 +79,16 @@ func NewCreateClientCmd() *cobra.Command { } flags.AddTxFlagsToCmd(cmd) - return cmd } // NewUpdateClientCmd defines the command to update an IBC client. func NewUpdateClientCmd() *cobra.Command { - return &cobra.Command{ - Use: "update [client-id] [path/to/header.json]", - Short: "update existing client with a header", - Long: "update existing client with a header", - Example: fmt.Sprintf("%s tx ibc %s update [client-id] [path/to/header.json] --from node0 --home ../node0/cli --chain-id $CID", version.AppName, types.SubModuleName), + cmd := &cobra.Command{ + Use: "update [client-id] [path/to/client_msg.json]", + Short: "update existing client with a client message", + Long: "update existing client with a client message, for example a header, misbehaviour or batch update", + Example: fmt.Sprintf("%s tx ibc %s update [client-id] [path/to/client_msg.json] --from node0 --home ../node0/cli --chain-id $CID", version.AppName, types.SubModuleName), Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { clientCtx, err := client.GetClientTxContext(cmd) @@ -100,22 +99,22 @@ func NewUpdateClientCmd() *cobra.Command { cdc := codec.NewProtoCodec(clientCtx.InterfaceRegistry) - var header exported.Header - headerContentOrFileName := args[1] - if err := cdc.UnmarshalInterfaceJSON([]byte(headerContentOrFileName), &header); err != nil { + var clientMsg exported.ClientMessage + clientMsgContentOrFileName := args[1] + if err := cdc.UnmarshalInterfaceJSON([]byte(clientMsgContentOrFileName), &clientMsg); err != nil { // check for file path if JSON input is not provided - contents, err := ioutil.ReadFile(headerContentOrFileName) + contents, err := os.ReadFile(clientMsgContentOrFileName) if err != nil { return fmt.Errorf("neither JSON input nor path to .json file for header were provided: %w", err) } - if err := cdc.UnmarshalInterfaceJSON(contents, &header); err != nil { + if err := cdc.UnmarshalInterfaceJSON(contents, &clientMsg); err != nil { return fmt.Errorf("error unmarshalling header file: %w", err) } } - msg, err := types.NewMsgUpdateClient(clientID, header, clientCtx.GetFromAddress().String()) + msg, err := types.NewMsgUpdateClient(clientID, clientMsg, clientCtx.GetFromAddress().String()) if err != nil { return err } @@ -123,17 +122,22 @@ func NewUpdateClientCmd() *cobra.Command { return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) }, } + + flags.AddTxFlagsToCmd(cmd) + return cmd } // NewSubmitMisbehaviourCmd defines the command to submit a misbehaviour to prevent // future updates. +// Deprecated: NewSubmitMisbehaviourCmd is deprecated and will be removed in a future release. +// Please use NewUpdateClientCmd instead. func NewSubmitMisbehaviourCmd() *cobra.Command { - return &cobra.Command{ - Use: "misbehaviour [path/to/misbehaviour.json]", + cmd := &cobra.Command{ + Use: "misbehaviour [clientID] [path/to/misbehaviour.json]", Short: "submit a client misbehaviour", Long: "submit a client misbehaviour to prevent future updates", - Example: fmt.Sprintf("%s tx ibc %s misbehaviour [path/to/misbehaviour.json] --from node0 --home ../node0/cli --chain-id $CID", version.AppName, types.SubModuleName), - Args: cobra.ExactArgs(1), + Example: fmt.Sprintf("%s tx ibc %s misbehaviour [clientID] [path/to/misbehaviour.json] --from node0 --home ../node0/cli --chain-id $CID", version.AppName, types.SubModuleName), + Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { clientCtx, err := client.GetClientTxContext(cmd) if err != nil { @@ -141,12 +145,13 @@ func NewSubmitMisbehaviourCmd() *cobra.Command { } cdc := codec.NewProtoCodec(clientCtx.InterfaceRegistry) - var misbehaviour exported.Misbehaviour - misbehaviourContentOrFileName := args[0] + var misbehaviour exported.ClientMessage + clientID := args[0] + misbehaviourContentOrFileName := args[1] if err := cdc.UnmarshalInterfaceJSON([]byte(misbehaviourContentOrFileName), &misbehaviour); err != nil { // check for file path if JSON input is not provided - contents, err := ioutil.ReadFile(misbehaviourContentOrFileName) + contents, err := os.ReadFile(misbehaviourContentOrFileName) if err != nil { return fmt.Errorf("neither JSON input nor path to .json file for misbehaviour were provided: %w", err) } @@ -156,7 +161,7 @@ func NewSubmitMisbehaviourCmd() *cobra.Command { } } - msg, err := types.NewMsgSubmitMisbehaviour(misbehaviour.GetClientID(), misbehaviour, clientCtx.GetFromAddress().String()) + msg, err := types.NewMsgSubmitMisbehaviour(clientID, misbehaviour, clientCtx.GetFromAddress().String()) if err != nil { return err } @@ -164,6 +169,9 @@ func NewSubmitMisbehaviourCmd() *cobra.Command { return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) }, } + + flags.AddTxFlagsToCmd(cmd) + return cmd } // NewUpgradeClientCmd defines the command to upgrade an IBC light client. @@ -190,7 +198,7 @@ func NewUpgradeClientCmd() *cobra.Command { if err := cdc.UnmarshalInterfaceJSON([]byte(clientContentOrFileName), &clientState); err != nil { // check for file path if JSON input is not provided - contents, err := ioutil.ReadFile(clientContentOrFileName) + contents, err := os.ReadFile(clientContentOrFileName) if err != nil { return fmt.Errorf("neither JSON input nor path to .json file for client state were provided: %w", err) } @@ -206,7 +214,7 @@ func NewUpgradeClientCmd() *cobra.Command { if err := cdc.UnmarshalInterfaceJSON([]byte(consensusContentOrFileName), &consensusState); err != nil { // check for file path if JSON input is not provided - contents, err := ioutil.ReadFile(consensusContentOrFileName) + contents, err := os.ReadFile(consensusContentOrFileName) if err != nil { return fmt.Errorf("neither JSON input nor path to .json file for consensus state were provided: %w", err) } @@ -229,7 +237,6 @@ func NewUpgradeClientCmd() *cobra.Command { } flags.AddTxFlagsToCmd(cmd) - return cmd } @@ -248,12 +255,12 @@ func NewCmdSubmitUpdateClientProposal() *cobra.Command { return err } - title, err := cmd.Flags().GetString(govcli.FlagTitle) + title, err := cmd.Flags().GetString(govcli.FlagTitle) //nolint:staticcheck // need this till full govv1 conversion. if err != nil { return err } - description, err := cmd.Flags().GetString(govcli.FlagDescription) + description, err := cmd.Flags().GetString(govcli.FlagDescription) //nolint:staticcheck // need this till full govv1 conversion. if err != nil { return err } @@ -287,8 +294,8 @@ func NewCmdSubmitUpdateClientProposal() *cobra.Command { }, } - cmd.Flags().String(govcli.FlagTitle, "", "title of proposal") - cmd.Flags().String(govcli.FlagDescription, "", "description of proposal") + cmd.Flags().String(govcli.FlagTitle, "", "title of proposal") //nolint:staticcheck // need this till full govv1 conversion. + cmd.Flags().String(govcli.FlagDescription, "", "description of proposal") //nolint:staticcheck // need this till full govv1 conversion. cmd.Flags().String(govcli.FlagDeposit, "", "deposit of proposal") return cmd @@ -319,12 +326,12 @@ func NewCmdSubmitUpgradeProposal() *cobra.Command { } cdc := codec.NewProtoCodec(clientCtx.InterfaceRegistry) - title, err := cmd.Flags().GetString(govcli.FlagTitle) + title, err := cmd.Flags().GetString(govcli.FlagTitle) //nolint:staticcheck // need this till full govv1 conversion. if err != nil { return err } - description, err := cmd.Flags().GetString(govcli.FlagDescription) + description, err := cmd.Flags().GetString(govcli.FlagDescription) //nolint:staticcheck // need this till full govv1 conversion. if err != nil { return err } @@ -347,7 +354,7 @@ func NewCmdSubmitUpgradeProposal() *cobra.Command { if err := cdc.UnmarshalInterfaceJSON([]byte(clientContentOrFileName), &clientState); err != nil { // check for file path if JSON input is not provided - contents, err := ioutil.ReadFile(clientContentOrFileName) + contents, err := os.ReadFile(clientContentOrFileName) if err != nil { return fmt.Errorf("neither JSON input nor path to .json file for client state were provided: %w", err) } @@ -386,8 +393,8 @@ func NewCmdSubmitUpgradeProposal() *cobra.Command { }, } - cmd.Flags().String(govcli.FlagTitle, "", "title of proposal") - cmd.Flags().String(govcli.FlagDescription, "", "description of proposal") + cmd.Flags().String(govcli.FlagTitle, "", "title of proposal") //nolint:staticcheck // need this till full govv1 conversion. + cmd.Flags().String(govcli.FlagDescription, "", "description of proposal") //nolint:staticcheck // need this till full govv1 conversion. cmd.Flags().String(govcli.FlagDeposit, "", "deposit of proposal") return cmd diff --git a/modules/core/02-client/client/proposal_handler.go b/modules/core/02-client/client/proposal_handler.go index 2c5aa8911d1..9a540f4da3b 100644 --- a/modules/core/02-client/client/proposal_handler.go +++ b/modules/core/02-client/client/proposal_handler.go @@ -1,26 +1,12 @@ package client import ( - "net/http" - - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/types/rest" govclient "github.com/cosmos/cosmos-sdk/x/gov/client" - govrest "github.com/cosmos/cosmos-sdk/x/gov/client/rest" - "github.com/cosmos/ibc-go/v4/modules/core/02-client/client/cli" + "github.com/cosmos/ibc-go/v7/modules/core/02-client/client/cli" ) var ( - UpdateClientProposalHandler = govclient.NewProposalHandler(cli.NewCmdSubmitUpdateClientProposal, emptyRestHandler) - UpgradeProposalHandler = govclient.NewProposalHandler(cli.NewCmdSubmitUpgradeProposal, emptyRestHandler) + UpdateClientProposalHandler = govclient.NewProposalHandler(cli.NewCmdSubmitUpdateClientProposal) + UpgradeProposalHandler = govclient.NewProposalHandler(cli.NewCmdSubmitUpgradeProposal) ) - -func emptyRestHandler(client.Context) govrest.ProposalRESTHandler { - return govrest.ProposalRESTHandler{ - SubRoute: "unsupported-ibc-client", - Handler: func(w http.ResponseWriter, r *http.Request) { - rest.WriteErrorResponse(w, http.StatusBadRequest, "Legacy REST Routes are not supported for IBC proposals") - }, - } -} diff --git a/modules/core/02-client/client/utils/utils.go b/modules/core/02-client/client/utils/utils.go index d4361098085..9dd2675087a 100644 --- a/modules/core/02-client/client/utils/utils.go +++ b/modules/core/02-client/client/utils/utils.go @@ -3,17 +3,17 @@ package utils import ( "context" + errorsmod "cosmossdk.io/errors" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/codec" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" tmtypes "github.com/tendermint/tendermint/types" - "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - commitmenttypes "github.com/cosmos/ibc-go/v4/modules/core/23-commitment/types" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" - ibcclient "github.com/cosmos/ibc-go/v4/modules/core/client" - "github.com/cosmos/ibc-go/v4/modules/core/exported" - ibctmtypes "github.com/cosmos/ibc-go/v4/modules/light-clients/07-tendermint/types" + "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + commitmenttypes "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + ibcclient "github.com/cosmos/ibc-go/v7/modules/core/client" + "github.com/cosmos/ibc-go/v7/modules/core/exported" + ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" ) // QueryClientState returns a client state. If prove is true, it performs an ABCI store query @@ -46,7 +46,7 @@ func QueryClientStateABCI( // check if client exists if len(value) == 0 { - return nil, sdkerrors.Wrap(types.ErrClientNotFound, clientID) + return nil, errorsmod.Wrap(types.ErrClientNotFound, clientID) } cdc := codec.NewProtoCodec(clientCtx.InterfaceRegistry) @@ -99,7 +99,7 @@ func QueryConsensusStateABCI( // check if consensus state exists if len(value) == 0 { - return nil, sdkerrors.Wrap(types.ErrConsensusStateNotFound, clientID) + return nil, errorsmod.Wrap(types.ErrConsensusStateNotFound, clientID) } cdc := codec.NewProtoCodec(clientCtx.InterfaceRegistry) @@ -119,15 +119,15 @@ func QueryConsensusStateABCI( // QueryTendermintHeader takes a client context and returns the appropriate // tendermint header -func QueryTendermintHeader(clientCtx client.Context) (ibctmtypes.Header, int64, error) { +func QueryTendermintHeader(clientCtx client.Context) (ibctm.Header, int64, error) { node, err := clientCtx.GetNode() if err != nil { - return ibctmtypes.Header{}, 0, err + return ibctm.Header{}, 0, err } info, err := node.ABCIInfo(context.Background()) if err != nil { - return ibctmtypes.Header{}, 0, err + return ibctm.Header{}, 0, err } var height int64 @@ -139,7 +139,7 @@ func QueryTendermintHeader(clientCtx client.Context) (ibctmtypes.Header, int64, commit, err := node.Commit(context.Background(), &height) if err != nil { - return ibctmtypes.Header{}, 0, err + return ibctm.Header{}, 0, err } page := 1 @@ -147,16 +147,16 @@ func QueryTendermintHeader(clientCtx client.Context) (ibctmtypes.Header, int64, validators, err := node.Validators(context.Background(), &height, &page, &count) if err != nil { - return ibctmtypes.Header{}, 0, err + return ibctm.Header{}, 0, err } protoCommit := commit.SignedHeader.ToProto() protoValset, err := tmtypes.NewValidatorSet(validators.Validators).ToProto() if err != nil { - return ibctmtypes.Header{}, 0, err + return ibctm.Header{}, 0, err } - header := ibctmtypes.Header{ + header := ibctm.Header{ SignedHeader: protoCommit, ValidatorSet: protoValset, } @@ -166,15 +166,15 @@ func QueryTendermintHeader(clientCtx client.Context) (ibctmtypes.Header, int64, // QuerySelfConsensusState takes a client context and returns the appropriate // tendermint consensus state -func QuerySelfConsensusState(clientCtx client.Context) (*ibctmtypes.ConsensusState, int64, error) { +func QuerySelfConsensusState(clientCtx client.Context) (*ibctm.ConsensusState, int64, error) { node, err := clientCtx.GetNode() if err != nil { - return &ibctmtypes.ConsensusState{}, 0, err + return &ibctm.ConsensusState{}, 0, err } info, err := node.ABCIInfo(context.Background()) if err != nil { - return &ibctmtypes.ConsensusState{}, 0, err + return &ibctm.ConsensusState{}, 0, err } var height int64 @@ -186,7 +186,7 @@ func QuerySelfConsensusState(clientCtx client.Context) (*ibctmtypes.ConsensusSta commit, err := node.Commit(context.Background(), &height) if err != nil { - return &ibctmtypes.ConsensusState{}, 0, err + return &ibctm.ConsensusState{}, 0, err } page := 1 @@ -195,10 +195,10 @@ func QuerySelfConsensusState(clientCtx client.Context) (*ibctmtypes.ConsensusSta nextHeight := height + 1 nextVals, err := node.Validators(context.Background(), &nextHeight, &page, &count) if err != nil { - return &ibctmtypes.ConsensusState{}, 0, err + return &ibctm.ConsensusState{}, 0, err } - state := &ibctmtypes.ConsensusState{ + state := &ibctm.ConsensusState{ Timestamp: commit.Time, Root: commitmenttypes.NewMerkleRoot(commit.AppHash), NextValidatorsHash: tmtypes.NewValidatorSet(nextVals.Validators).Hash(), diff --git a/modules/core/02-client/doc.go b/modules/core/02-client/doc.go index abb7f35fff3..34fcb5d432d 100644 --- a/modules/core/02-client/doc.go +++ b/modules/core/02-client/doc.go @@ -1,10 +1,7 @@ /* Package client implements the ICS 02 - Client Semantics specification -https://github.com/cosmos/ibc/tree/master/spec/core/ics-002-client-semantics. This -concrete implementations defines types and method to store and update light +(https://github.com/cosmos/ibc/tree/master/spec/core/ics-002-client-semantics). This +concrete implementation defines types and methods to store and update light clients which tracks on other chain's state. - -The main type is `Client`, which provides `commitment.Root` to verify state proofs and `ConsensusState` to -verify header proofs. */ package client diff --git a/modules/core/02-client/genesis.go b/modules/core/02-client/genesis.go index 8d4654d6438..aae41d9ae5a 100644 --- a/modules/core/02-client/genesis.go +++ b/modules/core/02-client/genesis.go @@ -5,9 +5,9 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/ibc-go/v4/modules/core/02-client/keeper" - "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v4/modules/core/exported" + "github.com/cosmos/ibc-go/v7/modules/core/02-client/keeper" + "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v7/modules/core/exported" ) // InitGenesis initializes the ibc client submodule's state from a provided genesis @@ -46,14 +46,9 @@ func InitGenesis(ctx sdk.Context, k keeper.Keeper, gs types.GenesisState) { } k.SetNextClientSequence(ctx, gs.NextClientSequence) - - // NOTE: localhost creation is specifically disallowed for the time being. - // Issue: https://github.com/cosmos/cosmos-sdk/issues/7871 } // ExportGenesis returns the ibc client submodule's exported genesis. -// NOTE: CreateLocalhost should always be false on export since a -// created localhost will be included in the exported clients. func ExportGenesis(ctx sdk.Context, k keeper.Keeper) types.GenesisState { genClients := k.GetAllGenesisClients(ctx) clientsMetadata, err := k.GetAllClientMetadata(ctx, genClients) @@ -61,10 +56,11 @@ func ExportGenesis(ctx sdk.Context, k keeper.Keeper) types.GenesisState { panic(err) } return types.GenesisState{ - Clients: genClients, - ClientsMetadata: clientsMetadata, - ClientsConsensus: k.GetAllConsensusStates(ctx), - Params: k.GetParams(ctx), + Clients: genClients, + ClientsMetadata: clientsMetadata, + ClientsConsensus: k.GetAllConsensusStates(ctx), + Params: k.GetParams(ctx), + // Warning: CreateLocalhost is deprecated CreateLocalhost: false, NextClientSequence: k.GetNextClientSequence(ctx), } diff --git a/modules/core/02-client/keeper/client.go b/modules/core/02-client/keeper/client.go index 4b770dafe19..8cb5a02b2d2 100644 --- a/modules/core/02-client/keeper/client.go +++ b/modules/core/02-client/keeper/client.go @@ -1,145 +1,104 @@ package keeper import ( - "encoding/hex" - - "github.com/armon/go-metrics" + errorsmod "cosmossdk.io/errors" + metrics "github.com/armon/go-metrics" "github.com/cosmos/cosmos-sdk/telemetry" sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v4/modules/core/exported" + "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v7/modules/core/exported" ) -// CreateClient creates a new client state and populates it with a given consensus -// state as defined in https://github.com/cosmos/ibc/tree/master/spec/core/ics-002-client-semantics#create +// CreateClient generates a new client identifier and isolated prefix store for the provided client state. +// The client state is responsible for setting any client-specific data in the store via the Initialize method. +// This includes the client state, initial consensus state and any associated metadata. func (k Keeper) CreateClient( ctx sdk.Context, clientState exported.ClientState, consensusState exported.ConsensusState, ) (string, error) { params := k.GetParams(ctx) if !params.IsAllowedClient(clientState.ClientType()) { - return "", sdkerrors.Wrapf( + return "", errorsmod.Wrapf( types.ErrInvalidClientType, "client state type %s is not registered in the allowlist", clientState.ClientType(), ) } clientID := k.GenerateClientIdentifier(ctx, clientState.ClientType()) + clientStore := k.ClientStore(ctx, clientID) - k.SetClientState(ctx, clientID, clientState) - k.Logger(ctx).Info("client created at height", "client-id", clientID, "height", clientState.GetLatestHeight().String()) - - // verifies initial consensus state against client state and initializes client store with any client-specific metadata - // e.g. set ProcessedTime in Tendermint clients - if err := clientState.Initialize(ctx, k.cdc, k.ClientStore(ctx, clientID), consensusState); err != nil { + if err := clientState.Initialize(ctx, k.cdc, clientStore, consensusState); err != nil { return "", err } - // check if consensus state is nil in case the created client is Localhost - if consensusState != nil { - k.SetClientConsensusState(ctx, clientID, clientState.GetLatestHeight(), consensusState) - } - k.Logger(ctx).Info("client created at height", "client-id", clientID, "height", clientState.GetLatestHeight().String()) - defer func() { - telemetry.IncrCounterWithLabels( - []string{"ibc", "client", "create"}, - 1, - []metrics.Label{telemetry.NewLabel(types.LabelClientType, clientState.ClientType())}, - ) - }() + defer telemetry.IncrCounterWithLabels( + []string{"ibc", "client", "create"}, + 1, + []metrics.Label{telemetry.NewLabel(types.LabelClientType, clientState.ClientType())}, + ) - EmitCreateClientEvent(ctx, clientID, clientState) + emitCreateClientEvent(ctx, clientID, clientState) return clientID, nil } // UpdateClient updates the consensus state and the state root from a provided header. -func (k Keeper) UpdateClient(ctx sdk.Context, clientID string, header exported.Header) error { +func (k Keeper) UpdateClient(ctx sdk.Context, clientID string, clientMsg exported.ClientMessage) error { clientState, found := k.GetClientState(ctx, clientID) if !found { - return sdkerrors.Wrapf(types.ErrClientNotFound, "cannot update client with ID %s", clientID) + return errorsmod.Wrapf(types.ErrClientNotFound, "cannot update client with ID %s", clientID) } clientStore := k.ClientStore(ctx, clientID) if status := clientState.Status(ctx, clientStore, k.cdc); status != exported.Active { - return sdkerrors.Wrapf(types.ErrClientNotActive, "cannot update client (%s) with status %s", clientID, status) + return errorsmod.Wrapf(types.ErrClientNotActive, "cannot update client (%s) with status %s", clientID, status) } - // Any writes made in CheckHeaderAndUpdateState are persisted on both valid updates and misbehaviour updates. - // Light client implementations are responsible for writing the correct metadata (if any) in either case. - newClientState, newConsensusState, err := clientState.CheckHeaderAndUpdateState(ctx, k.cdc, clientStore, header) - if err != nil { - return sdkerrors.Wrapf(err, "cannot update client with ID %s", clientID) + if err := clientState.VerifyClientMessage(ctx, k.cdc, clientStore, clientMsg); err != nil { + return err } - // emit the full header in events - var ( - headerStr string - consensusHeight exported.Height - ) - if header != nil { - // Marshal the Header as an Any and encode the resulting bytes to hex. - // This prevents the event value from containing invalid UTF-8 characters - // which may cause data to be lost when JSON encoding/decoding. - headerStr = hex.EncodeToString(types.MustMarshalHeader(k.cdc, header)) - // set default consensus height with header height - consensusHeight = header.GetHeight() + foundMisbehaviour := clientState.CheckForMisbehaviour(ctx, k.cdc, clientStore, clientMsg) + if foundMisbehaviour { + clientState.UpdateStateOnMisbehaviour(ctx, k.cdc, clientStore, clientMsg) - } + k.Logger(ctx).Info("client frozen due to misbehaviour", "client-id", clientID) - // set new client state regardless of if update is valid update or misbehaviour - k.SetClientState(ctx, clientID, newClientState) - // If client state is not frozen after clientState CheckHeaderAndUpdateState, - // then update was valid. Write the update state changes, and set new consensus state. - // Else the update was proof of misbehaviour and we must emit appropriate misbehaviour events. - if status := newClientState.Status(ctx, clientStore, k.cdc); status != exported.Frozen { - // if update is not misbehaviour then update the consensus state - // we don't set consensus state for localhost client - if header != nil && clientID != exported.Localhost { - k.SetClientConsensusState(ctx, clientID, header.GetHeight(), newConsensusState) - } else { - consensusHeight = types.GetSelfHeight(ctx) - } - - k.Logger(ctx).Info("client state updated", "client-id", clientID, "height", consensusHeight.String()) - - defer func() { - telemetry.IncrCounterWithLabels( - []string{"ibc", "client", "update"}, - 1, - []metrics.Label{ - telemetry.NewLabel(types.LabelClientType, clientState.ClientType()), - telemetry.NewLabel(types.LabelClientID, clientID), - telemetry.NewLabel(types.LabelUpdateType, "msg"), - }, - ) - }() - - // emitting events in the keeper emits for both begin block and handler client updates - EmitUpdateClientEvent(ctx, clientID, newClientState, consensusHeight, headerStr) - } else { + defer telemetry.IncrCounterWithLabels( + []string{"ibc", "client", "misbehaviour"}, + 1, + []metrics.Label{ + telemetry.NewLabel(types.LabelClientType, clientState.ClientType()), + telemetry.NewLabel(types.LabelClientID, clientID), + telemetry.NewLabel(types.LabelMsgType, "update"), + }, + ) - k.Logger(ctx).Info("client frozen due to misbehaviour", "client-id", clientID) + emitSubmitMisbehaviourEvent(ctx, clientID, clientState) - defer func() { - telemetry.IncrCounterWithLabels( - []string{"ibc", "client", "misbehaviour"}, - 1, - []metrics.Label{ - telemetry.NewLabel(types.LabelClientType, clientState.ClientType()), - telemetry.NewLabel(types.LabelClientID, clientID), - telemetry.NewLabel(types.LabelMsgType, "update"), - }, - ) - }() - - EmitSubmitMisbehaviourEventOnUpdate(ctx, clientID, newClientState, consensusHeight, headerStr) + return nil } + consensusHeights := clientState.UpdateState(ctx, k.cdc, clientStore, clientMsg) + + k.Logger(ctx).Info("client state updated", "client-id", clientID, "heights", consensusHeights) + + defer telemetry.IncrCounterWithLabels( + []string{"ibc", "client", "update"}, + 1, + []metrics.Label{ + telemetry.NewLabel(types.LabelClientType, clientState.ClientType()), + telemetry.NewLabel(types.LabelClientID, clientID), + telemetry.NewLabel(types.LabelUpdateType, "msg"), + }, + ) + + // emitting events in the keeper emits for both begin block and handler client updates + emitUpdateClientEvent(ctx, clientID, clientState.ClientType(), consensusHeights, k.cdc, clientMsg) + return nil } @@ -150,81 +109,33 @@ func (k Keeper) UpgradeClient(ctx sdk.Context, clientID string, upgradedClient e ) error { clientState, found := k.GetClientState(ctx, clientID) if !found { - return sdkerrors.Wrapf(types.ErrClientNotFound, "cannot update client with ID %s", clientID) + return errorsmod.Wrapf(types.ErrClientNotFound, "cannot update client with ID %s", clientID) } clientStore := k.ClientStore(ctx, clientID) if status := clientState.Status(ctx, clientStore, k.cdc); status != exported.Active { - return sdkerrors.Wrapf(types.ErrClientNotActive, "cannot upgrade client (%s) with status %s", clientID, status) - } - - updatedClientState, updatedConsState, err := clientState.VerifyUpgradeAndUpdateState(ctx, k.cdc, clientStore, - upgradedClient, upgradedConsState, proofUpgradeClient, proofUpgradeConsState) - if err != nil { - return sdkerrors.Wrapf(err, "cannot upgrade client with ID %s", clientID) - } - - k.SetClientState(ctx, clientID, updatedClientState) - k.SetClientConsensusState(ctx, clientID, updatedClientState.GetLatestHeight(), updatedConsState) - - k.Logger(ctx).Info("client state upgraded", "client-id", clientID, "height", updatedClientState.GetLatestHeight().String()) - - defer func() { - telemetry.IncrCounterWithLabels( - []string{"ibc", "client", "upgrade"}, - 1, - []metrics.Label{ - telemetry.NewLabel(types.LabelClientType, updatedClientState.ClientType()), - telemetry.NewLabel(types.LabelClientID, clientID), - }, - ) - }() - - // emitting events in the keeper emits for client upgrades - EmitUpgradeClientEvent(ctx, clientID, updatedClientState) - - return nil -} - -// CheckMisbehaviourAndUpdateState checks for client misbehaviour and freezes the -// client if so. -func (k Keeper) CheckMisbehaviourAndUpdateState(ctx sdk.Context, misbehaviour exported.Misbehaviour) error { - clientState, found := k.GetClientState(ctx, misbehaviour.GetClientID()) - if !found { - return sdkerrors.Wrapf(types.ErrClientNotFound, "cannot check misbehaviour for client with ID %s", misbehaviour.GetClientID()) - } - - clientStore := k.ClientStore(ctx, misbehaviour.GetClientID()) - - if status := clientState.Status(ctx, clientStore, k.cdc); status != exported.Active { - return sdkerrors.Wrapf(types.ErrClientNotActive, "cannot process misbehaviour for client (%s) with status %s", misbehaviour.GetClientID(), status) - } - - if err := misbehaviour.ValidateBasic(); err != nil { - return err + return errorsmod.Wrapf(types.ErrClientNotActive, "cannot upgrade client (%s) with status %s", clientID, status) } - clientState, err := clientState.CheckMisbehaviourAndUpdateState(ctx, k.cdc, clientStore, misbehaviour) - if err != nil { - return err + if err := clientState.VerifyUpgradeAndUpdateState(ctx, k.cdc, clientStore, + upgradedClient, upgradedConsState, proofUpgradeClient, proofUpgradeConsState, + ); err != nil { + return errorsmod.Wrapf(err, "cannot upgrade client with ID %s", clientID) } - k.SetClientState(ctx, misbehaviour.GetClientID(), clientState) - k.Logger(ctx).Info("client frozen due to misbehaviour", "client-id", misbehaviour.GetClientID()) + k.Logger(ctx).Info("client state upgraded", "client-id", clientID, "height", upgradedClient.GetLatestHeight().String()) - defer func() { - telemetry.IncrCounterWithLabels( - []string{"ibc", "client", "misbehaviour"}, - 1, - []metrics.Label{ - telemetry.NewLabel(types.LabelClientType, misbehaviour.ClientType()), - telemetry.NewLabel(types.LabelClientID, misbehaviour.GetClientID()), - }, - ) - }() + defer telemetry.IncrCounterWithLabels( + []string{"ibc", "client", "upgrade"}, + 1, + []metrics.Label{ + telemetry.NewLabel(types.LabelClientType, upgradedClient.ClientType()), + telemetry.NewLabel(types.LabelClientID, clientID), + }, + ) - EmitSubmitMisbehaviourEvent(ctx, misbehaviour.GetClientID(), clientState) + emitUpgradeClientEvent(ctx, clientID, upgradedClient) return nil } diff --git a/modules/core/02-client/keeper/client_test.go b/modules/core/02-client/keeper/client_test.go index d3492e9f25e..94b5fed855a 100644 --- a/modules/core/02-client/keeper/client_test.go +++ b/modules/core/02-client/keeper/client_test.go @@ -6,16 +6,14 @@ import ( "time" upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" - tmtypes "github.com/tendermint/tendermint/types" - - "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - commitmenttypes "github.com/cosmos/ibc-go/v4/modules/core/23-commitment/types" - "github.com/cosmos/ibc-go/v4/modules/core/exported" - ibctmtypes "github.com/cosmos/ibc-go/v4/modules/light-clients/07-tendermint/types" - localhosttypes "github.com/cosmos/ibc-go/v4/modules/light-clients/09-localhost/types" - ibctesting "github.com/cosmos/ibc-go/v4/testing" - ibctestingmock "github.com/cosmos/ibc-go/v4/testing/mock" + + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + commitmenttypes "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + "github.com/cosmos/ibc-go/v7/modules/core/exported" + solomachine "github.com/cosmos/ibc-go/v7/modules/light-clients/06-solomachine" + ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" + ibctesting "github.com/cosmos/ibc-go/v7/testing" ) func (suite *KeeperTestSuite) TestCreateClient() { @@ -24,8 +22,8 @@ func (suite *KeeperTestSuite) TestCreateClient() { clientState exported.ClientState expPass bool }{ - {"success", ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), true}, - {"client type not supported", localhosttypes.NewClientState(testChainID, clienttypes.NewHeight(0, 1)), false}, + {"success", ibctm.NewClientState(testChainID, ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath), true}, + {"client type not supported", solomachine.NewClientState(0, &solomachine.ConsensusState{PublicKey: suite.solomachine.ConsensusState().PublicKey, Diversifier: suite.solomachine.Diversifier, Timestamp: suite.solomachine.Time}), false}, } for i, tc := range cases { @@ -34,9 +32,11 @@ func (suite *KeeperTestSuite) TestCreateClient() { if tc.expPass { suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.msg) suite.Require().NotNil(clientID, "valid test case %d failed: %s", i, tc.msg) + suite.Require().True(suite.keeper.ClientStore(suite.ctx, clientID).Has(host.ClientStateKey())) } else { suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.msg) suite.Require().Equal("", clientID, "invalid test case %d passed: %s", i, tc.msg) + suite.Require().False(suite.keeper.ClientStore(suite.ctx, clientID).Has(host.ClientStateKey())) } } } @@ -44,20 +44,20 @@ func (suite *KeeperTestSuite) TestCreateClient() { func (suite *KeeperTestSuite) TestUpdateClientTendermint() { var ( path *ibctesting.Path - updateHeader *ibctmtypes.Header + updateHeader *ibctm.Header ) // Must create header creation functions since suite.header gets recreated on each test case - createFutureUpdateFn := func(trustedHeight clienttypes.Height) *ibctmtypes.Header { + createFutureUpdateFn := func(trustedHeight clienttypes.Height) *ibctm.Header { header, err := suite.chainA.ConstructUpdateTMClientHeaderWithTrustedHeight(path.EndpointB.Chain, path.EndpointA.ClientID, trustedHeight) suite.Require().NoError(err) return header } - createPastUpdateFn := func(fillHeight, trustedHeight clienttypes.Height) *ibctmtypes.Header { + createPastUpdateFn := func(fillHeight, trustedHeight clienttypes.Height) *ibctm.Header { consState, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.GetClientConsensusState(suite.chainA.GetContext(), path.EndpointA.ClientID, trustedHeight) suite.Require().True(found) - return suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(fillHeight.RevisionHeight), trustedHeight, consState.(*ibctmtypes.ConsensusState).Timestamp.Add(time.Second*5), + return suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(fillHeight.RevisionHeight), trustedHeight, consState.(*ibctm.ConsensusState).Timestamp.Add(time.Second*5), suite.chainB.Vals, suite.chainB.Vals, suite.chainB.Vals, suite.chainB.Signers) } @@ -68,26 +68,28 @@ func (suite *KeeperTestSuite) TestUpdateClientTendermint() { expFreeze bool }{ {"valid update", func() { - clientState := path.EndpointA.GetClientState().(*ibctmtypes.ClientState) - trustHeight := clientState.GetLatestHeight().(types.Height) + clientState := path.EndpointA.GetClientState().(*ibctm.ClientState) + trustHeight := clientState.GetLatestHeight().(clienttypes.Height) // store intermediate consensus state to check that trustedHeight does not need to be highest consensus state before header height - path.EndpointA.UpdateClient() + err := path.EndpointA.UpdateClient() + suite.Require().NoError(err) updateHeader = createFutureUpdateFn(trustHeight) }, true, false}, {"valid past update", func() { clientState := path.EndpointA.GetClientState() - trustedHeight := clientState.GetLatestHeight().(types.Height) + trustedHeight := clientState.GetLatestHeight().(clienttypes.Height) currHeight := suite.chainB.CurrentHeader.Height - fillHeight := types.NewHeight(clientState.GetLatestHeight().GetRevisionNumber(), uint64(currHeight)) + fillHeight := clienttypes.NewHeight(clientState.GetLatestHeight().GetRevisionNumber(), uint64(currHeight)) // commit a couple blocks to allow client to fill in gaps suite.coordinator.CommitBlock(suite.chainB) // this height is not filled in yet suite.coordinator.CommitBlock(suite.chainB) // this height is filled in by the update below - path.EndpointA.UpdateClient() + err := path.EndpointA.UpdateClient() + suite.Require().NoError(err) // ensure fill height not set _, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.GetClientConsensusState(suite.chainA.GetContext(), path.EndpointA.ClientID, fillHeight) @@ -100,24 +102,24 @@ func (suite *KeeperTestSuite) TestUpdateClientTendermint() { {"valid duplicate update", func() { clientID := path.EndpointA.ClientID - height1 := types.NewHeight(0, 1) + height1 := clienttypes.NewHeight(1, 1) // store previous consensus state - prevConsState := &ibctmtypes.ConsensusState{ + prevConsState := &ibctm.ConsensusState{ Timestamp: suite.past, NextValidatorsHash: suite.chainB.Vals.Hash(), } suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(suite.chainA.GetContext(), clientID, height1, prevConsState) - height5 := types.NewHeight(0, 5) + height5 := clienttypes.NewHeight(1, 5) // store next consensus state to check that trustedHeight does not need to be hightest consensus state before header height - nextConsState := &ibctmtypes.ConsensusState{ + nextConsState := &ibctm.ConsensusState{ Timestamp: suite.past.Add(time.Minute), NextValidatorsHash: suite.chainB.Vals.Hash(), } suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(suite.chainA.GetContext(), clientID, height5, nextConsState) - height3 := types.NewHeight(0, 3) + height3 := clienttypes.NewHeight(1, 3) // updateHeader will fill in consensus state between prevConsState and suite.consState // clientState should not be updated updateHeader = createPastUpdateFn(height3, height1) @@ -127,23 +129,23 @@ func (suite *KeeperTestSuite) TestUpdateClientTendermint() { {"misbehaviour detection: conflicting header", func() { clientID := path.EndpointA.ClientID - height1 := types.NewHeight(0, 1) + height1 := clienttypes.NewHeight(1, 1) // store previous consensus state - prevConsState := &ibctmtypes.ConsensusState{ + prevConsState := &ibctm.ConsensusState{ Timestamp: suite.past, NextValidatorsHash: suite.chainB.Vals.Hash(), } suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(suite.chainA.GetContext(), clientID, height1, prevConsState) - height5 := types.NewHeight(0, 5) + height5 := clienttypes.NewHeight(1, 5) // store next consensus state to check that trustedHeight does not need to be hightest consensus state before header height - nextConsState := &ibctmtypes.ConsensusState{ + nextConsState := &ibctm.ConsensusState{ Timestamp: suite.past.Add(time.Minute), NextValidatorsHash: suite.chainB.Vals.Hash(), } suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(suite.chainA.GetContext(), clientID, height5, nextConsState) - height3 := types.NewHeight(0, 3) + height3 := clienttypes.NewHeight(1, 3) // updateHeader will fill in consensus state between prevConsState and suite.consState // clientState should not be updated updateHeader = createPastUpdateFn(height3, height1) @@ -153,21 +155,21 @@ func (suite *KeeperTestSuite) TestUpdateClientTendermint() { suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(suite.chainA.GetContext(), clientID, updateHeader.GetHeight(), conflictConsState) }, true, true}, {"misbehaviour detection: monotonic time violation", func() { - clientState := path.EndpointA.GetClientState().(*ibctmtypes.ClientState) + clientState := path.EndpointA.GetClientState().(*ibctm.ClientState) clientID := path.EndpointA.ClientID - trustedHeight := clientState.GetLatestHeight().(types.Height) + trustedHeight := clientState.GetLatestHeight().(clienttypes.Height) // store intermediate consensus state at a time greater than updateHeader time // this will break time monotonicity - incrementedClientHeight := clientState.GetLatestHeight().Increment().(types.Height) - intermediateConsState := &ibctmtypes.ConsensusState{ + incrementedClientHeight := clientState.GetLatestHeight().Increment().(clienttypes.Height) + intermediateConsState := &ibctm.ConsensusState{ Timestamp: suite.coordinator.CurrentTime.Add(2 * time.Hour), NextValidatorsHash: suite.chainB.Vals.Hash(), } suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(suite.chainA.GetContext(), clientID, incrementedClientHeight, intermediateConsState) // set iteration key clientStore := suite.keeper.ClientStore(suite.ctx, clientID) - ibctmtypes.SetIterationKey(clientStore, incrementedClientHeight) + ibctm.SetIterationKey(clientStore, incrementedClientHeight) clientState.LatestHeight = incrementedClientHeight suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), clientID, clientState) @@ -175,28 +177,28 @@ func (suite *KeeperTestSuite) TestUpdateClientTendermint() { updateHeader = createFutureUpdateFn(trustedHeight) }, true, true}, {"client state not found", func() { - updateHeader = createFutureUpdateFn(path.EndpointA.GetClientState().GetLatestHeight().(types.Height)) + updateHeader = createFutureUpdateFn(path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height)) path.EndpointA.ClientID = ibctesting.InvalidID }, false, false}, {"consensus state not found", func() { clientState := path.EndpointA.GetClientState() - tmClient, ok := clientState.(*ibctmtypes.ClientState) + tmClient, ok := clientState.(*ibctm.ClientState) suite.Require().True(ok) - tmClient.LatestHeight = tmClient.LatestHeight.Increment().(types.Height) + tmClient.LatestHeight = tmClient.LatestHeight.Increment().(clienttypes.Height) suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), path.EndpointA.ClientID, clientState) - updateHeader = createFutureUpdateFn(clientState.GetLatestHeight().(types.Height)) + updateHeader = createFutureUpdateFn(clientState.GetLatestHeight().(clienttypes.Height)) }, false, false}, {"client is not active", func() { - clientState := path.EndpointA.GetClientState().(*ibctmtypes.ClientState) - clientState.FrozenHeight = types.NewHeight(0, 1) + clientState := path.EndpointA.GetClientState().(*ibctm.ClientState) + clientState.FrozenHeight = clienttypes.NewHeight(1, 1) suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), path.EndpointA.ClientID, clientState) - updateHeader = createFutureUpdateFn(clientState.GetLatestHeight().(types.Height)) + updateHeader = createFutureUpdateFn(clientState.GetLatestHeight().(clienttypes.Height)) }, false, false}, {"invalid header", func() { - updateHeader = createFutureUpdateFn(path.EndpointA.GetClientState().GetLatestHeight().(types.Height)) - updateHeader.TrustedHeight = updateHeader.TrustedHeight.Increment().(types.Height) + updateHeader = createFutureUpdateFn(path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height)) + updateHeader.TrustedHeight = updateHeader.TrustedHeight.Increment().(clienttypes.Height) }, false, false}, } @@ -222,9 +224,9 @@ func (suite *KeeperTestSuite) TestUpdateClientTendermint() { newClientState := path.EndpointA.GetClientState() if tc.expFreeze { - suite.Require().True(!newClientState.(*ibctmtypes.ClientState).FrozenHeight.IsZero(), "client did not freeze after conflicting header was submitted to UpdateClient") + suite.Require().True(!newClientState.(*ibctm.ClientState).FrozenHeight.IsZero(), "client did not freeze after conflicting header was submitted to UpdateClient") } else { - expConsensusState := &ibctmtypes.ConsensusState{ + expConsensusState := &ibctm.ConsensusState{ Timestamp: updateHeader.GetTime(), Root: commitmenttypes.NewMerkleRoot(updateHeader.Header.GetAppHash()), NextValidatorsHash: updateHeader.Header.NextValidatorsHash, @@ -252,19 +254,6 @@ func (suite *KeeperTestSuite) TestUpdateClientTendermint() { } } -func (suite *KeeperTestSuite) TestUpdateClientLocalhost() { - revision := types.ParseChainID(suite.chainA.ChainID) - var localhostClient exported.ClientState = localhosttypes.NewClientState(suite.chainA.ChainID, types.NewHeight(revision, uint64(suite.chainA.GetContext().BlockHeight()))) - - ctx := suite.chainA.GetContext().WithBlockHeight(suite.chainA.GetContext().BlockHeight() + 1) - err := suite.chainA.App.GetIBCKeeper().ClientKeeper.UpdateClient(ctx, exported.Localhost, nil) - suite.Require().NoError(err) - - clientState, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.GetClientState(ctx, exported.Localhost) - suite.Require().True(found) - suite.Require().Equal(localhostClient.GetLatestHeight().(types.Height).Increment(), clientState.GetLatestHeight()) -} - func (suite *KeeperTestSuite) TestUpgradeClient() { var ( path *ibctesting.Path @@ -273,7 +262,6 @@ func (suite *KeeperTestSuite) TestUpgradeClient() { lastHeight exported.Height proofUpgradedClient, proofUpgradedConsState []byte upgradedClientBz, upgradedConsStateBz []byte - err error ) testCases := []struct { @@ -285,16 +273,17 @@ func (suite *KeeperTestSuite) TestUpgradeClient() { name: "successful upgrade", setup: func() { // last Height is at next block - lastHeight = clienttypes.NewHeight(0, uint64(suite.chainB.GetContext().BlockHeight()+1)) + lastHeight = clienttypes.NewHeight(1, uint64(suite.chainB.GetContext().BlockHeight()+1)) // zero custom fields and store in upgrade store - suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) - suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) + err := suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) + suite.Require().NoError(err) + err = suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) + suite.Require().NoError(err) // commit upgrade store changes and update clients - suite.coordinator.CommitBlock(suite.chainB) - err := path.EndpointA.UpdateClient() + err = path.EndpointA.UpdateClient() suite.Require().NoError(err) cs, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.GetClientState(suite.chainA.GetContext(), path.EndpointA.ClientID) @@ -309,16 +298,18 @@ func (suite *KeeperTestSuite) TestUpgradeClient() { name: "client state not found", setup: func() { // last Height is at next block - lastHeight = clienttypes.NewHeight(0, uint64(suite.chainB.GetContext().BlockHeight()+1)) + lastHeight = clienttypes.NewHeight(1, uint64(suite.chainB.GetContext().BlockHeight()+1)) // zero custom fields and store in upgrade store - suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) - suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) + err := suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) + suite.Require().NoError(err) + err = suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) + suite.Require().NoError(err) // commit upgrade store changes and update clients suite.coordinator.CommitBlock(suite.chainB) - err := path.EndpointA.UpdateClient() + err = path.EndpointA.UpdateClient() suite.Require().NoError(err) cs, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.GetClientState(suite.chainA.GetContext(), path.EndpointA.ClientID) @@ -337,16 +328,18 @@ func (suite *KeeperTestSuite) TestUpgradeClient() { // client is frozen // last Height is at next block - lastHeight = clienttypes.NewHeight(0, uint64(suite.chainB.GetContext().BlockHeight()+1)) + lastHeight = clienttypes.NewHeight(1, uint64(suite.chainB.GetContext().BlockHeight()+1)) // zero custom fields and store in upgrade store - suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) - suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) + err := suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) + suite.Require().NoError(err) + err = suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) + suite.Require().NoError(err) // commit upgrade store changes and update clients suite.coordinator.CommitBlock(suite.chainB) - err := path.EndpointA.UpdateClient() + err = path.EndpointA.UpdateClient() suite.Require().NoError(err) cs, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.GetClientState(suite.chainA.GetContext(), path.EndpointA.ClientID) @@ -356,9 +349,9 @@ func (suite *KeeperTestSuite) TestUpgradeClient() { proofUpgradedConsState, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedConsStateKey(int64(lastHeight.GetRevisionHeight())), cs.GetLatestHeight().GetRevisionHeight()) // set frozen client in store - tmClient, ok := cs.(*ibctmtypes.ClientState) + tmClient, ok := cs.(*ibctm.ClientState) suite.Require().True(ok) - tmClient.FrozenHeight = types.NewHeight(0, 1) + tmClient.FrozenHeight = clienttypes.NewHeight(1, 1) suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), path.EndpointA.ClientID, tmClient) }, expPass: false, @@ -367,17 +360,21 @@ func (suite *KeeperTestSuite) TestUpgradeClient() { name: "tendermint client VerifyUpgrade fails", setup: func() { // last Height is at next block - lastHeight = clienttypes.NewHeight(0, uint64(suite.chainB.GetContext().BlockHeight()+1)) + lastHeight = clienttypes.NewHeight(1, uint64(suite.chainB.GetContext().BlockHeight()+1)) // zero custom fields and store in upgrade store - suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) - suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) + err := suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) + suite.Require().NoError(err) + err = suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) + suite.Require().NoError(err) // change upgradedClient client-specified parameters - upgradedClient = ibctmtypes.NewClientState("wrongchainID", ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, newClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, true, true) + tmClient := upgradedClient.(*ibctm.ClientState) + tmClient.ChainId = "wrongchainID" + upgradedClient = tmClient suite.coordinator.CommitBlock(suite.chainB) - err := path.EndpointA.UpdateClient() + err = path.EndpointA.UpdateClient() suite.Require().NoError(err) cs, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.GetClientState(suite.chainA.GetContext(), path.EndpointA.ClientID) @@ -394,22 +391,26 @@ func (suite *KeeperTestSuite) TestUpgradeClient() { tc := tc path = ibctesting.NewPath(suite.chainA, suite.chainB) suite.coordinator.SetupClients(path) - upgradedClient = ibctmtypes.NewClientState("newChainId-1", ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod+trustingPeriod, maxClockDrift, newClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) + + clientState := path.EndpointA.GetClientState().(*ibctm.ClientState) + revisionNumber := clienttypes.ParseChainID(clientState.ChainId) + + newChainID, err := clienttypes.SetRevisionNumber(clientState.ChainId, revisionNumber+1) + suite.Require().NoError(err) + + upgradedClient = ibctm.NewClientState(newChainID, ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod+trustingPeriod, maxClockDrift, clienttypes.NewHeight(revisionNumber+1, clientState.GetLatestHeight().GetRevisionHeight()+1), commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath) upgradedClient = upgradedClient.ZeroCustomFields() - upgradedClientBz, err = types.MarshalClientState(suite.chainA.App.AppCodec(), upgradedClient) + upgradedClientBz, err = clienttypes.MarshalClientState(suite.chainA.App.AppCodec(), upgradedClient) suite.Require().NoError(err) - upgradedConsState = &ibctmtypes.ConsensusState{ + upgradedConsState = &ibctm.ConsensusState{ NextValidatorsHash: []byte("nextValsHash"), } - upgradedConsStateBz, err = types.MarshalConsensusState(suite.chainA.App.AppCodec(), upgradedConsState) + upgradedConsStateBz, err = clienttypes.MarshalConsensusState(suite.chainA.App.AppCodec(), upgradedConsState) suite.Require().NoError(err) tc.setup() - // Call ZeroCustomFields on upgraded clients to clear any client-chosen parameters in test-case upgradedClient - upgradedClient = upgradedClient.ZeroCustomFields() - err = suite.chainA.App.GetIBCKeeper().ClientKeeper.UpgradeClient(suite.chainA.GetContext(), path.EndpointA.ClientID, upgradedClient, upgradedConsState, proofUpgradedClient, proofUpgradedConsState) if tc.expPass { @@ -420,256 +421,10 @@ func (suite *KeeperTestSuite) TestUpgradeClient() { } } -func (suite *KeeperTestSuite) TestCheckMisbehaviourAndUpdateState() { - var ( - clientID string - err error - ) - - altPrivVal := ibctestingmock.NewPV() - altPubKey, err := altPrivVal.GetPubKey() - suite.Require().NoError(err) - altVal := tmtypes.NewValidator(altPubKey, 4) - - // Set valSet here with suite.valSet so it doesn't get reset on each testcase - valSet := suite.valSet - valsHash := valSet.Hash() - - // Create bothValSet with both suite validator and altVal - bothValSet := tmtypes.NewValidatorSet(append(suite.valSet.Validators, altVal)) - bothValsHash := bothValSet.Hash() - // Create alternative validator set with only altVal - altValSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{altVal}) - - // Create signer array and ensure it is in same order as bothValSet - _, suiteVal := suite.valSet.GetByIndex(0) - bothSigners := make(map[string]tmtypes.PrivValidator, 2) - bothSigners[suiteVal.Address.String()] = suite.privVal - bothSigners[altVal.Address.String()] = altPrivVal - - altSigners := make(map[string]tmtypes.PrivValidator, 1) - altSigners[altVal.Address.String()] = altPrivVal - - // Create valid Misbehaviour by making a duplicate header that signs over different block time - altTime := suite.ctx.BlockTime().Add(time.Minute) - - heightPlus3 := types.NewHeight(0, height+3) - heightPlus5 := types.NewHeight(0, height+5) - - testCases := []struct { - name string - misbehaviour *ibctmtypes.Misbehaviour - malleate func() error - expPass bool - }{ - { - "trusting period misbehavior should pass", - &ibctmtypes.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(testChainID, int64(testClientHeight.RevisionHeight+1), testClientHeight, altTime, bothValSet, bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(testChainID, int64(testClientHeight.RevisionHeight+1), testClientHeight, suite.ctx.BlockTime(), bothValSet, bothValSet, bothValSet, bothSigners), - ClientId: clientID, - }, - func() error { - suite.consensusState.NextValidatorsHash = bothValsHash - clientState := ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) - clientID, err = suite.keeper.CreateClient(suite.ctx, clientState, suite.consensusState) - - return err - }, - true, - }, - { - "time misbehavior should pass", - &ibctmtypes.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(testChainID, int64(testClientHeight.RevisionHeight+5), testClientHeight, suite.ctx.BlockTime(), bothValSet, bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(testChainID, int64(testClientHeight.RevisionHeight+1), testClientHeight, altTime, bothValSet, bothValSet, bothValSet, bothSigners), - ClientId: clientID, - }, - func() error { - suite.consensusState.NextValidatorsHash = bothValsHash - clientState := ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) - clientID, err = suite.keeper.CreateClient(suite.ctx, clientState, suite.consensusState) - - return err - }, - true, - }, - { - "misbehavior at later height should pass", - &ibctmtypes.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(testChainID, int64(heightPlus5.RevisionHeight+1), testClientHeight, altTime, bothValSet, bothValSet, valSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(testChainID, int64(heightPlus5.RevisionHeight+1), testClientHeight, suite.ctx.BlockTime(), bothValSet, bothValSet, valSet, bothSigners), - ClientId: clientID, - }, - func() error { - suite.consensusState.NextValidatorsHash = valsHash - clientState := ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) - clientID, err = suite.keeper.CreateClient(suite.ctx, clientState, suite.consensusState) - - // store intermediate consensus state to check that trustedHeight does not need to be highest consensus state before header height - intermediateConsState := &ibctmtypes.ConsensusState{ - Timestamp: suite.now.Add(time.Minute), - NextValidatorsHash: suite.chainB.Vals.Hash(), - } - suite.keeper.SetClientConsensusState(suite.ctx, clientID, heightPlus3, intermediateConsState) - - clientState.LatestHeight = heightPlus3 - suite.keeper.SetClientState(suite.ctx, clientID, clientState) - - return err - }, - true, - }, - { - "misbehavior at later height with different trusted heights should pass", - &ibctmtypes.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(testChainID, int64(heightPlus5.RevisionHeight+1), testClientHeight, altTime, bothValSet, bothValSet, valSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(testChainID, int64(heightPlus5.RevisionHeight+1), heightPlus3, suite.ctx.BlockTime(), bothValSet, bothValSet, bothValSet, bothSigners), - ClientId: clientID, - }, - func() error { - suite.consensusState.NextValidatorsHash = valsHash - clientState := ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) - clientID, err = suite.keeper.CreateClient(suite.ctx, clientState, suite.consensusState) - - // store trusted consensus state for Header2 - intermediateConsState := &ibctmtypes.ConsensusState{ - Timestamp: suite.now.Add(time.Minute), - NextValidatorsHash: bothValsHash, - } - suite.keeper.SetClientConsensusState(suite.ctx, clientID, heightPlus3, intermediateConsState) - - clientState.LatestHeight = heightPlus3 - suite.keeper.SetClientState(suite.ctx, clientID, clientState) - - return err - }, - true, - }, - { - "misbehavior ValidateBasic fails: misbehaviour height is at same height as trusted height", - &ibctmtypes.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(testChainID, int64(testClientHeight.RevisionHeight), testClientHeight, altTime, bothValSet, bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(testChainID, int64(testClientHeight.RevisionHeight), testClientHeight, suite.ctx.BlockTime(), bothValSet, bothValSet, bothValSet, bothSigners), - ClientId: clientID, - }, - func() error { - suite.consensusState.NextValidatorsHash = bothValsHash - clientState := ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) - clientID, err = suite.keeper.CreateClient(suite.ctx, clientState, suite.consensusState) - - return err - }, - false, - }, - { - "trusted ConsensusState1 not found", - &ibctmtypes.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(testChainID, int64(heightPlus5.RevisionHeight+1), heightPlus3, altTime, bothValSet, bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(testChainID, int64(heightPlus5.RevisionHeight+1), testClientHeight, suite.ctx.BlockTime(), bothValSet, bothValSet, valSet, bothSigners), - ClientId: clientID, - }, - func() error { - suite.consensusState.NextValidatorsHash = valsHash - clientState := ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) - clientID, err = suite.keeper.CreateClient(suite.ctx, clientState, suite.consensusState) - // intermediate consensus state at height + 3 is not created - return err - }, - false, - }, - { - "trusted ConsensusState2 not found", - &ibctmtypes.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(testChainID, int64(heightPlus5.RevisionHeight+1), testClientHeight, altTime, bothValSet, bothValSet, valSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(testChainID, int64(heightPlus5.RevisionHeight+1), heightPlus3, suite.ctx.BlockTime(), bothValSet, bothValSet, bothValSet, bothSigners), - ClientId: clientID, - }, - func() error { - suite.consensusState.NextValidatorsHash = valsHash - clientState := ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) - clientID, err = suite.keeper.CreateClient(suite.ctx, clientState, suite.consensusState) - // intermediate consensus state at height + 3 is not created - return err - }, - false, - }, - { - "client state not found", - &ibctmtypes.Misbehaviour{}, - func() error { return nil }, - false, - }, - { - "client already is not active - client is frozen", - &ibctmtypes.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(testChainID, int64(testClientHeight.RevisionHeight+1), testClientHeight, altTime, bothValSet, bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(testChainID, int64(testClientHeight.RevisionHeight+1), testClientHeight, suite.ctx.BlockTime(), bothValSet, bothValSet, bothValSet, bothSigners), - ClientId: clientID, - }, - func() error { - suite.consensusState.NextValidatorsHash = bothValsHash - clientState := ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) - clientID, err = suite.keeper.CreateClient(suite.ctx, clientState, suite.consensusState) - - clientState.FrozenHeight = types.NewHeight(0, 1) - suite.keeper.SetClientState(suite.ctx, clientID, clientState) - - return err - }, - false, - }, - { - "misbehaviour check failed", - &ibctmtypes.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(testChainID, int64(testClientHeight.RevisionHeight+1), testClientHeight, altTime, bothValSet, bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(testChainID, int64(testClientHeight.RevisionHeight+1), testClientHeight, suite.ctx.BlockTime(), altValSet, altValSet, bothValSet, altSigners), - ClientId: clientID, - }, - func() error { - clientState := ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) - if err != nil { - return err - } - clientID, err = suite.keeper.CreateClient(suite.ctx, clientState, suite.consensusState) - - return err - }, - false, - }, - } - - for i, tc := range testCases { - tc := tc - i := i - - suite.Run(tc.name, func() { - suite.SetupTest() // reset - clientID = testClientID // must be explicitly changed - - err := tc.malleate() - suite.Require().NoError(err) - - tc.misbehaviour.ClientId = clientID - - err = suite.keeper.CheckMisbehaviourAndUpdateState(suite.ctx, tc.misbehaviour) - - if tc.expPass { - suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.name) - - clientState, found := suite.keeper.GetClientState(suite.ctx, clientID) - suite.Require().True(found, "valid test case %d failed: %s", i, tc.name) - suite.Require().True(!clientState.(*ibctmtypes.ClientState).FrozenHeight.IsZero(), "valid test case %d failed: %s", i, tc.name) - } else { - suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.name) - } - }) - } -} - func (suite *KeeperTestSuite) TestUpdateClientEventEmission() { path := ibctesting.NewPath(suite.chainA, suite.chainB) suite.coordinator.SetupClients(path) + header, err := suite.chainA.ConstructUpdateTMClientHeader(suite.chainB, path.EndpointA.ClientID) suite.Require().NoError(err) @@ -677,6 +432,7 @@ func (suite *KeeperTestSuite) TestUpdateClientEventEmission() { path.EndpointA.ClientID, header, suite.chainA.SenderAccount.GetAddress().String(), ) + suite.Require().NoError(err) result, err := suite.chainA.SendMsgs(msg) suite.Require().NoError(err) @@ -687,13 +443,13 @@ func (suite *KeeperTestSuite) TestUpdateClientEventEmission() { // use a boolean to ensure the update event contains the header contains := false for _, attr := range updateEvent.Attributes { - if string(attr.Key) == clienttypes.AttributeKeyHeader { + if attr.Key == clienttypes.AttributeKeyHeader { contains = true - bz, err := hex.DecodeString(string(attr.Value)) + bz, err := hex.DecodeString(attr.Value) suite.Require().NoError(err) - emittedHeader, err := types.UnmarshalHeader(suite.chainA.App.AppCodec(), bz) + emittedHeader, err := clienttypes.UnmarshalClientMessage(suite.chainA.App.AppCodec(), bz) suite.Require().NoError(err) suite.Require().Equal(header, emittedHeader) } diff --git a/modules/core/02-client/keeper/encoding.go b/modules/core/02-client/keeper/encoding.go index 6c036455ce3..0b9e2a307b3 100644 --- a/modules/core/02-client/keeper/encoding.go +++ b/modules/core/02-client/keeper/encoding.go @@ -1,8 +1,8 @@ package keeper import ( - "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v4/modules/core/exported" + "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v7/modules/core/exported" ) // UnmarshalClientState attempts to decode and return an ClientState object from diff --git a/modules/core/02-client/keeper/events.go b/modules/core/02-client/keeper/events.go index 0702651a611..9b0bb6841ba 100644 --- a/modules/core/02-client/keeper/events.go +++ b/modules/core/02-client/keeper/events.go @@ -1,16 +1,21 @@ package keeper import ( + "encoding/hex" "fmt" + "strconv" + "strings" + "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" + upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" - "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v4/modules/core/exported" + "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v7/modules/core/exported" ) -// EmitCreateClientEvent emits a create client event -func EmitCreateClientEvent(ctx sdk.Context, clientID string, clientState exported.ClientState) { +// emitCreateClientEvent emits a create client event +func emitCreateClientEvent(ctx sdk.Context, clientID string, clientState exported.ClientState) { ctx.EventManager().EmitEvents(sdk.Events{ sdk.NewEvent( types.EventTypeCreateClient, @@ -25,15 +30,33 @@ func EmitCreateClientEvent(ctx sdk.Context, clientID string, clientState exporte }) } -// EmitUpdateClientEvent emits an update client event -func EmitUpdateClientEvent(ctx sdk.Context, clientID string, clientState exported.ClientState, consensusHeight exported.Height, headerStr string) { +// emitUpdateClientEvent emits an update client event +func emitUpdateClientEvent(ctx sdk.Context, clientID string, clientType string, consensusHeights []exported.Height, cdc codec.BinaryCodec, clientMsg exported.ClientMessage) { + // Marshal the ClientMessage as an Any and encode the resulting bytes to hex. + // This prevents the event value from containing invalid UTF-8 characters + // which may cause data to be lost when JSON encoding/decoding. + clientMsgStr := hex.EncodeToString(types.MustMarshalClientMessage(cdc, clientMsg)) + + var consensusHeightAttr string + if len(consensusHeights) != 0 { + consensusHeightAttr = consensusHeights[0].String() + } + + consensusHeightsAttr := make([]string, len(consensusHeights)) + for i, height := range consensusHeights { + consensusHeightsAttr[i] = height.String() + } + ctx.EventManager().EmitEvents(sdk.Events{ sdk.NewEvent( types.EventTypeUpdateClient, sdk.NewAttribute(types.AttributeKeyClientID, clientID), - sdk.NewAttribute(types.AttributeKeyClientType, clientState.ClientType()), - sdk.NewAttribute(types.AttributeKeyConsensusHeight, consensusHeight.String()), - sdk.NewAttribute(types.AttributeKeyHeader, headerStr), + sdk.NewAttribute(types.AttributeKeyClientType, clientType), + // Deprecated: AttributeKeyConsensusHeight is deprecated and will be removed in a future release. + // Please use AttributeKeyConsensusHeights instead. + sdk.NewAttribute(types.AttributeKeyConsensusHeight, consensusHeightAttr), + sdk.NewAttribute(types.AttributeKeyConsensusHeights, strings.Join(consensusHeightsAttr, ",")), + sdk.NewAttribute(types.AttributeKeyHeader, clientMsgStr), ), sdk.NewEvent( sdk.EventTypeMessage, @@ -42,8 +65,8 @@ func EmitUpdateClientEvent(ctx sdk.Context, clientID string, clientState exporte }) } -// EmitUpdateClientEvent emits an upgrade client event -func EmitUpgradeClientEvent(ctx sdk.Context, clientID string, clientState exported.ClientState) { +// emitUpgradeClientEvent emits an upgrade client event +func emitUpgradeClientEvent(ctx sdk.Context, clientID string, clientState exported.ClientState) { ctx.EventManager().EmitEvents(sdk.Events{ sdk.NewEvent( types.EventTypeUpgradeClient, @@ -58,49 +81,62 @@ func EmitUpgradeClientEvent(ctx sdk.Context, clientID string, clientState export }) } -// EmitUpdateClientProposalEvent emits an update client proposal event -func EmitUpdateClientProposalEvent(ctx sdk.Context, clientID string, clientState exported.ClientState) { - ctx.EventManager().EmitEvent( +// emitUpdateClientProposalEvent emits an update client proposal event +func emitUpdateClientProposalEvent(ctx sdk.Context, clientID, clientType string) { + ctx.EventManager().EmitEvents(sdk.Events{ sdk.NewEvent( types.EventTypeUpdateClientProposal, sdk.NewAttribute(types.AttributeKeySubjectClientID, clientID), - sdk.NewAttribute(types.AttributeKeyClientType, clientState.ClientType()), - sdk.NewAttribute(types.AttributeKeyConsensusHeight, clientState.GetLatestHeight().String()), + sdk.NewAttribute(types.AttributeKeyClientType, clientType), ), - ) + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + ), + }) } -// EmitUpgradeClientProposalEvent emits an upgrade client proposal event -func EmitUpgradeClientProposalEvent(ctx sdk.Context, title string, height int64) { - ctx.EventManager().EmitEvent( +// emitUpgradeClientProposalEvent emits an upgrade client proposal event +func emitUpgradeClientProposalEvent(ctx sdk.Context, title string, height int64) { + ctx.EventManager().EmitEvents(sdk.Events{ sdk.NewEvent( types.EventTypeUpgradeClientProposal, sdk.NewAttribute(types.AttributeKeyUpgradePlanTitle, title), sdk.NewAttribute(types.AttributeKeyUpgradePlanHeight, fmt.Sprintf("%d", height)), ), - ) + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + ), + }) } -// EmitSubmitMisbehaviourEvent emits a client misbehaviour event -func EmitSubmitMisbehaviourEvent(ctx sdk.Context, clientID string, clientState exported.ClientState) { - ctx.EventManager().EmitEvent( +// emitSubmitMisbehaviourEvent emits a client misbehaviour event +func emitSubmitMisbehaviourEvent(ctx sdk.Context, clientID string, clientState exported.ClientState) { + ctx.EventManager().EmitEvents(sdk.Events{ sdk.NewEvent( types.EventTypeSubmitMisbehaviour, sdk.NewAttribute(types.AttributeKeyClientID, clientID), sdk.NewAttribute(types.AttributeKeyClientType, clientState.ClientType()), ), - ) + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + ), + }) } -// EmitSubmitMisbehaviourEventOnUpdate emits a client misbehaviour event on a client update event -func EmitSubmitMisbehaviourEventOnUpdate(ctx sdk.Context, clientID string, clientState exported.ClientState, consensusHeight exported.Height, headerStr string) { - ctx.EventManager().EmitEvent( +// EmitUpgradeChainEvent emits an upgrade chain event. +func EmitUpgradeChainEvent(ctx sdk.Context, height int64) { + ctx.EventManager().EmitEvents(sdk.Events{ sdk.NewEvent( - types.EventTypeSubmitMisbehaviour, - sdk.NewAttribute(types.AttributeKeyClientID, clientID), - sdk.NewAttribute(types.AttributeKeyClientType, clientState.ClientType()), - sdk.NewAttribute(types.AttributeKeyConsensusHeight, consensusHeight.String()), - sdk.NewAttribute(types.AttributeKeyHeader, headerStr), + types.EventTypeUpgradeChain, + sdk.NewAttribute(types.AttributeKeyUpgradePlanHeight, strconv.FormatInt(height, 10)), + sdk.NewAttribute(types.AttributeKeyUpgradeStore, upgradetypes.StoreKey), // which store to query proof of consensus state from + ), + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), ), - ) + }) } diff --git a/modules/core/02-client/keeper/grpc_query.go b/modules/core/02-client/keeper/grpc_query.go index 3fc7b6e7cde..5f575303c02 100644 --- a/modules/core/02-client/keeper/grpc_query.go +++ b/modules/core/02-client/keeper/grpc_query.go @@ -7,16 +7,16 @@ import ( "sort" "strings" + errorsmod "cosmossdk.io/errors" "github.com/cosmos/cosmos-sdk/store/prefix" sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/types/query" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" - "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" - "github.com/cosmos/ibc-go/v4/modules/core/exported" + "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + "github.com/cosmos/ibc-go/v7/modules/core/exported" ) var _ types.QueryServer = Keeper{} @@ -36,18 +36,18 @@ func (q Keeper) ClientState(c context.Context, req *types.QueryClientStateReques if !found { return nil, status.Error( codes.NotFound, - sdkerrors.Wrap(types.ErrClientNotFound, req.ClientId).Error(), + errorsmod.Wrap(types.ErrClientNotFound, req.ClientId).Error(), ) } - any, err := types.PackClientState(clientState) + protoAny, err := types.PackClientState(clientState) if err != nil { return nil, status.Error(codes.Internal, err.Error()) } proofHeight := types.GetSelfHeight(ctx) return &types.QueryClientStateResponse{ - ClientState: any, + ClientState: protoAny, ProofHeight: proofHeight, }, nil } @@ -63,25 +63,26 @@ func (q Keeper) ClientStates(c context.Context, req *types.QueryClientStatesRequ clientStates := types.IdentifiedClientStates{} store := prefix.NewStore(ctx.KVStore(q.storeKey), host.KeyClientStorePrefix) - pageRes, err := query.Paginate(store, req.Pagination, func(key, value []byte) error { + pageRes, err := query.FilteredPaginate(store, req.Pagination, func(key, value []byte, accumulate bool) (bool, error) { + // filter any metadata stored under client state key keySplit := strings.Split(string(key), "/") if keySplit[len(keySplit)-1] != "clientState" { - return nil + return false, nil } clientState, err := q.UnmarshalClientState(value) if err != nil { - return err + return false, err } clientID := keySplit[1] if err := host.ClientIdentifierValidator(clientID); err != nil { - return err + return false, err } identifiedClient := types.NewIdentifiedClientState(clientID, clientState) clientStates = append(clientStates, identifiedClient) - return nil + return true, nil }) if err != nil { return nil, err @@ -126,18 +127,18 @@ func (q Keeper) ConsensusState(c context.Context, req *types.QueryConsensusState if !found { return nil, status.Error( codes.NotFound, - sdkerrors.Wrapf(types.ErrConsensusStateNotFound, "client-id: %s, height: %s", req.ClientId, height).Error(), + errorsmod.Wrapf(types.ErrConsensusStateNotFound, "client-id: %s, height: %s", req.ClientId, height).Error(), ) } - any, err := types.PackConsensusState(consensusState) + protoAny, err := types.PackConsensusState(consensusState) if err != nil { return nil, status.Error(codes.Internal, err.Error()) } proofHeight := types.GetSelfHeight(ctx) return &types.QueryConsensusStateResponse{ - ConsensusState: any, + ConsensusState: protoAny, ProofHeight: proofHeight, }, nil } @@ -215,7 +216,6 @@ func (q Keeper) ConsensusStateHeights(c context.Context, req *types.QueryConsens consensusStateHeights = append(consensusStateHeights, height) return true, nil }) - if err != nil { return nil, err } @@ -241,7 +241,7 @@ func (q Keeper) ClientStatus(c context.Context, req *types.QueryClientStatusRequ if !found { return nil, status.Error( codes.NotFound, - sdkerrors.Wrap(types.ErrClientNotFound, req.ClientId).Error(), + errorsmod.Wrap(types.ErrClientNotFound, req.ClientId).Error(), ) } @@ -290,13 +290,13 @@ func (q Keeper) UpgradedClientState(c context.Context, req *types.QueryUpgradedC ) } - any, err := types.PackClientState(clientState) + protoAny, err := types.PackClientState(clientState) if err != nil { return nil, status.Error(codes.Internal, err.Error()) } return &types.QueryUpgradedClientStateResponse{ - UpgradedClientState: any, + UpgradedClientState: protoAny, }, nil } @@ -320,12 +320,12 @@ func (q Keeper) UpgradedConsensusState(c context.Context, req *types.QueryUpgrad ) } - any, err := types.PackConsensusState(consensusState) + protoAny, err := types.PackConsensusState(consensusState) if err != nil { return nil, status.Error(codes.Internal, err.Error()) } return &types.QueryUpgradedConsensusStateResponse{ - UpgradedConsensusState: any, + UpgradedConsensusState: protoAny, }, nil } diff --git a/modules/core/02-client/keeper/grpc_query_test.go b/modules/core/02-client/keeper/grpc_query_test.go index 1fb29d06964..50683e98723 100644 --- a/modules/core/02-client/keeper/grpc_query_test.go +++ b/modules/core/02-client/keeper/grpc_query_test.go @@ -9,10 +9,10 @@ import ( "github.com/cosmos/cosmos-sdk/types/query" "google.golang.org/grpc/metadata" - "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v4/modules/core/exported" - ibctmtypes "github.com/cosmos/ibc-go/v4/modules/light-clients/07-tendermint/types" - ibctesting "github.com/cosmos/ibc-go/v4/testing" + "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v7/modules/core/exported" + ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" + ibctesting "github.com/cosmos/ibc-go/v7/testing" ) func (suite *KeeperTestSuite) TestQueryClientState() { @@ -142,7 +142,7 @@ func (suite *KeeperTestSuite) TestQueryClientStates() { idcs := types.NewIdentifiedClientState(path1.EndpointA.ClientID, clientStateA1) idcs2 := types.NewIdentifiedClientState(path2.EndpointA.ClientID, clientStateA2) - // order is sorted by client id, localhost is last + // order is sorted by client id expClientStates = types.IdentifiedClientStates{idcs, idcs2}.Sort() req = &types.QueryClientStatesRequest{ Pagination: &query.PageRequest{ @@ -158,22 +158,16 @@ func (suite *KeeperTestSuite) TestQueryClientStates() { for _, tc := range testCases { suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { suite.SetupTest() // reset - expClientStates = nil - tc.malleate() - // always add localhost which is created by default in init genesis - localhostClientState := suite.chainA.GetClientState(exported.Localhost) - identifiedLocalhost := types.NewIdentifiedClientState(exported.Localhost, localhostClientState) - expClientStates = append(expClientStates, identifiedLocalhost) ctx := sdk.WrapSDKContext(suite.chainA.GetContext()) res, err := suite.chainA.QueryServer.ClientStates(ctx, req) - if tc.expPass { suite.Require().NoError(err) suite.Require().NotNil(res) suite.Require().Equal(expClientStates.Sort(), res.ClientStates) + suite.Require().Equal(len(expClientStates), int(res.Pagination.Total)) } else { suite.Require().Error(err) } @@ -259,7 +253,8 @@ func (suite *KeeperTestSuite) TestQueryConsensusState() { suite.Require().NoError(err) // update client to new height - path.EndpointA.UpdateClient() + err = path.EndpointA.UpdateClient() + suite.Require().NoError(err) req = &types.QueryConsensusStateRequest{ ClientId: path.EndpointA.ClientID, @@ -386,7 +381,6 @@ func (suite *KeeperTestSuite) TestQueryConsensusStates() { for i := range expConsensusStates { suite.Require().NotNil(res.ConsensusStates[i]) suite.Require().Equal(expConsensusStates[i], res.ConsensusStates[i]) - // ensure UnpackInterfaces is defined cachedValue := res.ConsensusStates[i].ConsensusState.GetCachedValue() suite.Require().NotNil(cachedValue) @@ -534,7 +528,7 @@ func (suite *KeeperTestSuite) TestQueryClientStatus() { func() { path := ibctesting.NewPath(suite.chainA, suite.chainB) suite.coordinator.SetupClients(path) - clientState := path.EndpointA.GetClientState().(*ibctmtypes.ClientState) + clientState := path.EndpointA.GetClientState().(*ibctm.ClientState) // increment latest height so no consensus state is stored clientState.LatestHeight = clientState.LatestHeight.Increment().(types.Height) @@ -551,7 +545,7 @@ func (suite *KeeperTestSuite) TestQueryClientStatus() { func() { path := ibctesting.NewPath(suite.chainA, suite.chainB) suite.coordinator.SetupClients(path) - clientState := path.EndpointA.GetClientState().(*ibctmtypes.ClientState) + clientState := path.EndpointA.GetClientState().(*ibctm.ClientState) clientState.FrozenHeight = types.NewHeight(0, 1) path.EndpointA.SetClientState(clientState) @@ -646,7 +640,7 @@ func (suite *KeeperTestSuite) TestQueryUpgradedConsensusStates() { } } -func (suite *KeeperTestSuite) TestQueryParams() { +func (suite *KeeperTestSuite) TestQueryClientParams() { ctx := sdk.WrapSDKContext(suite.chainA.GetContext()) expParams := types.DefaultParams() res, _ := suite.chainA.QueryServer.ClientParams(ctx, &types.QueryClientParamsRequest{}) diff --git a/modules/core/02-client/keeper/keeper.go b/modules/core/02-client/keeper/keeper.go index fbb911f8997..aa0eea8c194 100644 --- a/modules/core/02-client/keeper/keeper.go +++ b/modules/core/02-client/keeper/keeper.go @@ -5,26 +5,28 @@ import ( "reflect" "strings" + errorsmod "cosmossdk.io/errors" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/store/prefix" + storetypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" "github.com/tendermint/tendermint/libs/log" "github.com/tendermint/tendermint/light" - "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - commitmenttypes "github.com/cosmos/ibc-go/v4/modules/core/23-commitment/types" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" - "github.com/cosmos/ibc-go/v4/modules/core/exported" - ibctmtypes "github.com/cosmos/ibc-go/v4/modules/light-clients/07-tendermint/types" + ibcerrors "github.com/cosmos/ibc-go/v7/internal/errors" + "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + commitmenttypes "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + "github.com/cosmos/ibc-go/v7/modules/core/exported" + ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" ) // Keeper represents a type that grants read and write permissions to any client // state information type Keeper struct { - storeKey sdk.StoreKey + storeKey storetypes.StoreKey cdc codec.BinaryCodec paramSpace paramtypes.Subspace stakingKeeper types.StakingKeeper @@ -32,7 +34,7 @@ type Keeper struct { } // NewKeeper creates a new NewKeeper instance -func NewKeeper(cdc codec.BinaryCodec, key sdk.StoreKey, paramSpace paramtypes.Subspace, sk types.StakingKeeper, uk types.UpgradeKeeper) Keeper { +func NewKeeper(cdc codec.BinaryCodec, key storetypes.StoreKey, paramSpace paramtypes.Subspace, sk types.StakingKeeper, uk types.UpgradeKeeper) Keeper { // set KeyTable if it has not already been set if !paramSpace.HasKeyTable() { paramSpace = paramSpace.WithKeyTable(types.ParamKeyTable()) @@ -49,7 +51,7 @@ func NewKeeper(cdc codec.BinaryCodec, key sdk.StoreKey, paramSpace paramtypes.Su // Logger returns a module-specific logger. func (k Keeper) Logger(ctx sdk.Context) log.Logger { - return ctx.Logger().With("module", "x/"+host.ModuleName+"/"+types.SubModuleName) + return ctx.Logger().With("module", "x/"+exported.ModuleName+"/"+types.SubModuleName) } // GenerateClientIdentifier returns the next client identifier. @@ -66,7 +68,7 @@ func (k Keeper) GenerateClientIdentifier(ctx sdk.Context, clientType string) str func (k Keeper) GetClientState(ctx sdk.Context, clientID string) (exported.ClientState, bool) { store := k.ClientStore(ctx, clientID) bz := store.Get(host.ClientStateKey()) - if bz == nil { + if len(bz) == 0 { return nil, false } @@ -84,7 +86,7 @@ func (k Keeper) SetClientState(ctx sdk.Context, clientID string, clientState exp func (k Keeper) GetClientConsensusState(ctx sdk.Context, clientID string, height exported.Height) (exported.ConsensusState, bool) { store := k.ClientStore(ctx, clientID) bz := store.Get(host.ConsensusStateKey(height)) - if bz == nil { + if len(bz) == 0 { return nil, false } @@ -103,7 +105,7 @@ func (k Keeper) SetClientConsensusState(ctx sdk.Context, clientID string, height func (k Keeper) GetNextClientSequence(ctx sdk.Context) uint64 { store := ctx.KVStore(k.storeKey) bz := store.Get([]byte(types.KeyNextClientSequence)) - if bz == nil { + if len(bz) == 0 { panic("next client sequence is nil") } @@ -124,7 +126,7 @@ func (k Keeper) IterateConsensusStates(ctx sdk.Context, cb func(clientID string, store := ctx.KVStore(k.storeKey) iterator := sdk.KVStorePrefixIterator(store, host.KeyClientStorePrefix) - defer iterator.Close() + defer sdk.LogDeferred(ctx.Logger(), func() error { return iterator.Close() }) for ; iterator.Valid(); iterator.Next() { keySplit := strings.Split(string(iterator.Key()), "/") // consensus key is in the format "clients//consensusStates/" @@ -146,7 +148,7 @@ func (k Keeper) IterateConsensusStates(ctx sdk.Context, cb func(clientID string, // GetAllGenesisClients returns all the clients in state with their client ids returned as IdentifiedClientState func (k Keeper) GetAllGenesisClients(ctx sdk.Context) types.IdentifiedClientStates { var genClients types.IdentifiedClientStates - k.IterateClients(ctx, func(clientID string, cs exported.ClientState) bool { + k.IterateClientStates(ctx, nil, func(clientID string, cs exported.ClientState) bool { genClients = append(genClients, types.NewIdentifiedClientState(clientID, cs)) return false }) @@ -172,7 +174,7 @@ func (k Keeper) GetAllClientMetadata(ctx sdk.Context, genClients []types.Identif for i, metadata := range gms { cmd, ok := metadata.(types.GenesisMetadata) if !ok { - return nil, sdkerrors.Wrapf(types.ErrInvalidClientMetadata, "expected metadata type: %T, got: %T", + return nil, errorsmod.Wrapf(types.ErrInvalidClientMetadata, "expected metadata type: %T, got: %T", types.GenesisMetadata{}, cmd) } clientMetadata[i] = cmd @@ -244,19 +246,19 @@ func (k Keeper) GetLatestClientConsensusState(ctx sdk.Context, clientID string) func (k Keeper) GetSelfConsensusState(ctx sdk.Context, height exported.Height) (exported.ConsensusState, error) { selfHeight, ok := height.(types.Height) if !ok { - return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "expected %T, got %T", types.Height{}, height) + return nil, errorsmod.Wrapf(ibcerrors.ErrInvalidType, "expected %T, got %T", types.Height{}, height) } // check that height revision matches chainID revision revision := types.ParseChainID(ctx.ChainID()) if revision != height.GetRevisionNumber() { - return nil, sdkerrors.Wrapf(types.ErrInvalidHeight, "chainID revision number does not match height revision number: expected %d, got %d", revision, height.GetRevisionNumber()) + return nil, errorsmod.Wrapf(types.ErrInvalidHeight, "chainID revision number does not match height revision number: expected %d, got %d", revision, height.GetRevisionNumber()) } histInfo, found := k.stakingKeeper.GetHistoricalInfo(ctx, int64(selfHeight.RevisionHeight)) if !found { - return nil, sdkerrors.Wrapf(sdkerrors.ErrNotFound, "no historical info found at height %d", selfHeight.RevisionHeight) + return nil, errorsmod.Wrapf(ibcerrors.ErrNotFound, "no historical info found at height %d", selfHeight.RevisionHeight) } - consensusState := &ibctmtypes.ConsensusState{ + consensusState := &ibctm.ConsensusState{ Timestamp: histInfo.Header.Time, Root: commitmenttypes.NewMerkleRoot(histInfo.Header.GetAppHash()), NextValidatorsHash: histInfo.Header.NextValidatorsHash, @@ -268,10 +270,10 @@ func (k Keeper) GetSelfConsensusState(ctx sdk.Context, height exported.Height) ( // This function is only used to validate the client state the counterparty stores for this chain // Client must be in same revision as the executing chain func (k Keeper) ValidateSelfClient(ctx sdk.Context, clientState exported.ClientState) error { - tmClient, ok := clientState.(*ibctmtypes.ClientState) + tmClient, ok := clientState.(*ibctm.ClientState) if !ok { - return sdkerrors.Wrapf(types.ErrInvalidClient, "client must be a Tendermint client, expected: %T, got: %T", - &ibctmtypes.ClientState{}, tmClient) + return errorsmod.Wrapf(types.ErrInvalidClient, "client must be a Tendermint client, expected: %T, got: %T", + &ibctm.ClientState{}, tmClient) } if !tmClient.FrozenHeight.IsZero() { @@ -279,7 +281,7 @@ func (k Keeper) ValidateSelfClient(ctx sdk.Context, clientState exported.ClientS } if ctx.ChainID() != tmClient.ChainId { - return sdkerrors.Wrapf(types.ErrInvalidClient, "invalid chain-id. expected: %s, got: %s", + return errorsmod.Wrapf(types.ErrInvalidClient, "invalid chain-id. expected: %s, got: %s", ctx.ChainID(), tmClient.ChainId) } @@ -287,34 +289,34 @@ func (k Keeper) ValidateSelfClient(ctx sdk.Context, clientState exported.ClientS // client must be in the same revision as executing chain if tmClient.LatestHeight.RevisionNumber != revision { - return sdkerrors.Wrapf(types.ErrInvalidClient, "client is not in the same revision as the chain. expected revision: %d, got: %d", + return errorsmod.Wrapf(types.ErrInvalidClient, "client is not in the same revision as the chain. expected revision: %d, got: %d", tmClient.LatestHeight.RevisionNumber, revision) } selfHeight := types.NewHeight(revision, uint64(ctx.BlockHeight())) if tmClient.LatestHeight.GTE(selfHeight) { - return sdkerrors.Wrapf(types.ErrInvalidClient, "client has LatestHeight %d greater than or equal to chain height %d", + return errorsmod.Wrapf(types.ErrInvalidClient, "client has LatestHeight %d greater than or equal to chain height %d", tmClient.LatestHeight, selfHeight) } expectedProofSpecs := commitmenttypes.GetSDKSpecs() if !reflect.DeepEqual(expectedProofSpecs, tmClient.ProofSpecs) { - return sdkerrors.Wrapf(types.ErrInvalidClient, "client has invalid proof specs. expected: %v got: %v", + return errorsmod.Wrapf(types.ErrInvalidClient, "client has invalid proof specs. expected: %v got: %v", expectedProofSpecs, tmClient.ProofSpecs) } if err := light.ValidateTrustLevel(tmClient.TrustLevel.ToTendermint()); err != nil { - return sdkerrors.Wrapf(types.ErrInvalidClient, "trust-level invalid: %v", err) + return errorsmod.Wrapf(types.ErrInvalidClient, "trust-level invalid: %v", err) } expectedUbdPeriod := k.stakingKeeper.UnbondingTime(ctx) if expectedUbdPeriod != tmClient.UnbondingPeriod { - return sdkerrors.Wrapf(types.ErrInvalidClient, "invalid unbonding period. expected: %s, got: %s", + return errorsmod.Wrapf(types.ErrInvalidClient, "invalid unbonding period. expected: %s, got: %s", expectedUbdPeriod, tmClient.UnbondingPeriod) } if tmClient.UnbondingPeriod < tmClient.TrustingPeriod { - return sdkerrors.Wrapf(types.ErrInvalidClient, "unbonding period must be greater than trusting period. unbonding period (%d) < trusting period (%d)", + return errorsmod.Wrapf(types.ErrInvalidClient, "unbonding period must be greater than trusting period. unbonding period (%d) < trusting period (%d)", tmClient.UnbondingPeriod, tmClient.TrustingPeriod) } @@ -322,7 +324,7 @@ func (k Keeper) ValidateSelfClient(ctx sdk.Context, clientState exported.ClientS // For now, SDK IBC implementation assumes that upgrade path (if defined) is defined by SDK upgrade module expectedUpgradePath := []string{upgradetypes.StoreKey, upgradetypes.KeyUpgradedIBCState} if !reflect.DeepEqual(expectedUpgradePath, tmClient.UpgradePath) { - return sdkerrors.Wrapf(types.ErrInvalidClient, "upgrade path must be the upgrade path defined by upgrade module. expected %v, got %v", + return errorsmod.Wrapf(types.ErrInvalidClient, "upgrade path must be the upgrade path defined by upgrade module. expected %v, got %v", expectedUpgradePath, tmClient.UpgradePath) } } @@ -349,35 +351,38 @@ func (k Keeper) SetUpgradedConsensusState(ctx sdk.Context, planHeight int64, bz return k.upgradeKeeper.SetUpgradedConsensusState(ctx, planHeight, bz) } -// IterateClients provides an iterator over all stored light client State +// IterateClientStates provides an iterator over all stored light client State // objects. For each State object, cb will be called. If the cb returns true, // the iterator will close and stop. -func (k Keeper) IterateClients(ctx sdk.Context, cb func(clientID string, cs exported.ClientState) bool) { +func (k Keeper) IterateClientStates(ctx sdk.Context, prefix []byte, cb func(clientID string, cs exported.ClientState) bool) { store := ctx.KVStore(k.storeKey) - iterator := sdk.KVStorePrefixIterator(store, host.KeyClientStorePrefix) + iterator := sdk.KVStorePrefixIterator(store, host.PrefixedClientStoreKey(prefix)) - defer iterator.Close() + defer sdk.LogDeferred(ctx.Logger(), func() error { return iterator.Close() }) for ; iterator.Valid(); iterator.Next() { - keySplit := strings.Split(string(iterator.Key()), "/") - if keySplit[len(keySplit)-1] != host.KeyClientState { + path := string(iterator.Key()) + if !strings.Contains(path, host.KeyClientState) { + // skip non client state keys continue } + + clientID := host.MustParseClientStatePath(path) clientState := k.MustUnmarshalClientState(iterator.Value()) - // key is ibc/{clientid}/clientState - // Thus, keySplit[1] is clientID - if cb(keySplit[1], clientState) { + if cb(clientID, clientState) { break } } } // GetAllClients returns all stored light client State objects. -func (k Keeper) GetAllClients(ctx sdk.Context) (states []exported.ClientState) { - k.IterateClients(ctx, func(_ string, state exported.ClientState) bool { +func (k Keeper) GetAllClients(ctx sdk.Context) []exported.ClientState { + var states []exported.ClientState + k.IterateClientStates(ctx, nil, func(_ string, state exported.ClientState) bool { states = append(states, state) return false }) + return states } diff --git a/modules/core/02-client/keeper/keeper_test.go b/modules/core/02-client/keeper/keeper_test.go index 11acea3df7d..8e5cac289d4 100644 --- a/modules/core/02-client/keeper/keeper_test.go +++ b/modules/core/02-client/keeper/keeper_test.go @@ -15,15 +15,15 @@ import ( tmproto "github.com/tendermint/tendermint/proto/tendermint/types" tmtypes "github.com/tendermint/tendermint/types" - "github.com/cosmos/ibc-go/v4/modules/core/02-client/keeper" - "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - commitmenttypes "github.com/cosmos/ibc-go/v4/modules/core/23-commitment/types" - "github.com/cosmos/ibc-go/v4/modules/core/exported" - ibctmtypes "github.com/cosmos/ibc-go/v4/modules/light-clients/07-tendermint/types" - localhosttypes "github.com/cosmos/ibc-go/v4/modules/light-clients/09-localhost/types" - ibctesting "github.com/cosmos/ibc-go/v4/testing" - ibctestingmock "github.com/cosmos/ibc-go/v4/testing/mock" - "github.com/cosmos/ibc-go/v4/testing/simapp" + "github.com/cosmos/ibc-go/v7/modules/core/02-client/keeper" + "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + commitmenttypes "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/types" + "github.com/cosmos/ibc-go/v7/modules/core/exported" + solomachine "github.com/cosmos/ibc-go/v7/modules/light-clients/06-solomachine" + ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" + ibctesting "github.com/cosmos/ibc-go/v7/testing" + ibctestingmock "github.com/cosmos/ibc-go/v7/testing/mock" + "github.com/cosmos/ibc-go/v7/testing/simapp" ) const ( @@ -44,7 +44,6 @@ const ( var ( testClientHeight = types.NewHeight(0, 5) testClientHeightRevision1 = types.NewHeight(1, 5) - newClientHeight = types.NewHeight(1, 1) ) type KeeperTestSuite struct { @@ -58,13 +57,14 @@ type KeeperTestSuite struct { cdc codec.Codec ctx sdk.Context keeper *keeper.Keeper - consensusState *ibctmtypes.ConsensusState - header *ibctmtypes.Header + consensusState *ibctm.ConsensusState + header *ibctm.Header valSet *tmtypes.ValidatorSet valSetHash tmbytes.HexBytes privVal tmtypes.PrivValidator now time.Time past time.Time + solomachine *ibctesting.Solomachine signers map[string]tmtypes.PrivValidator @@ -102,7 +102,7 @@ func (suite *KeeperTestSuite) SetupTest() { suite.signers[validator.Address.String()] = suite.privVal suite.header = suite.chainA.CreateTMClientHeader(testChainID, int64(testClientHeight.RevisionHeight), testClientHeightMinus1, now2, suite.valSet, suite.valSet, suite.valSet, suite.signers) - suite.consensusState = ibctmtypes.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot([]byte("hash")), suite.valSetHash) + suite.consensusState = ibctm.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot([]byte("hash")), suite.valSetHash) var validators stakingtypes.Validators for i := 1; i < 11; i++ { @@ -122,12 +122,7 @@ func (suite *KeeperTestSuite) SetupTest() { app.StakingKeeper.SetHistoricalInfo(suite.ctx, int64(i), &hi) } - // add localhost client - revision := types.ParseChainID(suite.chainA.ChainID) - localHostClient := localhosttypes.NewClientState( - suite.chainA.ChainID, types.NewHeight(revision, uint64(suite.chainA.GetContext().BlockHeight())), - ) - suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), exported.Localhost, localHostClient) + suite.solomachine = ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachinesingle", "testing", 1) // TODO: deprecate queryHelper := baseapp.NewQueryServerTestHelper(suite.ctx, app.InterfaceRegistry()) @@ -140,7 +135,7 @@ func TestKeeperTestSuite(t *testing.T) { } func (suite *KeeperTestSuite) TestSetClientState() { - clientState := ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, types.ZeroHeight(), commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) + clientState := ibctm.NewClientState(testChainID, ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, types.ZeroHeight(), commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath) suite.keeper.SetClientState(suite.ctx, testClientID, clientState) retrievedState, found := suite.keeper.GetClientState(suite.ctx, testClientID) @@ -154,13 +149,14 @@ func (suite *KeeperTestSuite) TestSetClientConsensusState() { retrievedConsState, found := suite.keeper.GetClientConsensusState(suite.ctx, testClientID, testClientHeight) suite.Require().True(found, "GetConsensusState failed") - tmConsState, ok := retrievedConsState.(*ibctmtypes.ConsensusState) + tmConsState, ok := retrievedConsState.(*ibctm.ConsensusState) suite.Require().True(ok) suite.Require().Equal(suite.consensusState, tmConsState, "ConsensusState not stored correctly") } func (suite *KeeperTestSuite) TestValidateSelfClient() { - testClientHeight := types.NewHeight(0, uint64(suite.chainA.GetContext().BlockHeight()-1)) + testClientHeight := types.GetSelfHeight(suite.chainA.GetContext()) + testClientHeight.RevisionHeight-- testCases := []struct { name string @@ -169,62 +165,61 @@ func (suite *KeeperTestSuite) TestValidateSelfClient() { }{ { "success", - ibctmtypes.NewClientState(suite.chainA.ChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + ibctm.NewClientState(suite.chainA.ChainID, ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath), true, }, { "success with nil UpgradePath", - ibctmtypes.NewClientState(suite.chainA.ChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), nil, false, false), + ibctm.NewClientState(suite.chainA.ChainID, ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), nil), true, }, - { - "invalid client type", - localhosttypes.NewClientState(suite.chainA.ChainID, testClientHeight), - false, - }, { "frozen client", - &ibctmtypes.ClientState{suite.chainA.ChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false}, + &ibctm.ClientState{ChainId: suite.chainA.ChainID, TrustLevel: ibctm.DefaultTrustLevel, TrustingPeriod: trustingPeriod, UnbondingPeriod: ubdPeriod, MaxClockDrift: maxClockDrift, FrozenHeight: testClientHeight, LatestHeight: testClientHeight, ProofSpecs: commitmenttypes.GetSDKSpecs(), UpgradePath: ibctesting.UpgradePath}, false, }, { "incorrect chainID", - ibctmtypes.NewClientState("gaiatestnet", ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + ibctm.NewClientState("gaiatestnet", ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath), false, }, { "invalid client height", - ibctmtypes.NewClientState(suite.chainA.ChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, types.NewHeight(0, uint64(suite.chainA.GetContext().BlockHeight())), commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + ibctm.NewClientState(suite.chainA.ChainID, ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, types.GetSelfHeight(suite.chainA.GetContext()).Increment().(types.Height), commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath), + false, + }, + { + "invalid client type", + solomachine.NewClientState(0, &solomachine.ConsensusState{PublicKey: suite.solomachine.ConsensusState().PublicKey, Diversifier: suite.solomachine.Diversifier, Timestamp: suite.solomachine.Time}), false, }, { "invalid client revision", - ibctmtypes.NewClientState(suite.chainA.ChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeightRevision1, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + ibctm.NewClientState(suite.chainA.ChainID, ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeightRevision1, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath), false, }, { "invalid proof specs", - ibctmtypes.NewClientState(suite.chainA.ChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, nil, ibctesting.UpgradePath, false, false), + ibctm.NewClientState(suite.chainA.ChainID, ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, nil, ibctesting.UpgradePath), false, }, { "invalid trust level", - ibctmtypes.NewClientState(suite.chainA.ChainID, ibctmtypes.Fraction{0, 1}, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), - false, + ibctm.NewClientState(suite.chainA.ChainID, ibctm.Fraction{Numerator: 0, Denominator: 1}, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath), false, }, { "invalid unbonding period", - ibctmtypes.NewClientState(suite.chainA.ChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod+10, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + ibctm.NewClientState(suite.chainA.ChainID, ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod+10, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath), false, }, { "invalid trusting period", - ibctmtypes.NewClientState(suite.chainA.ChainID, ibctmtypes.DefaultTrustLevel, ubdPeriod+10, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + ibctm.NewClientState(suite.chainA.ChainID, ibctm.DefaultTrustLevel, ubdPeriod+10, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath), false, }, { "invalid upgrade path", - ibctmtypes.NewClientState(suite.chainA.ChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), []string{"bad", "upgrade", "path"}, false, false), + ibctm.NewClientState(suite.chainA.ChainID, ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), []string{"bad", "upgrade", "path"}), false, }, } @@ -239,14 +234,14 @@ func (suite *KeeperTestSuite) TestValidateSelfClient() { } } -func (suite KeeperTestSuite) TestGetAllGenesisClients() { +func (suite KeeperTestSuite) TestGetAllGenesisClients() { //nolint:govet // this is a test, we are okay with copying locks clientIDs := []string{ testClientID2, testClientID3, testClientID, } expClients := []exported.ClientState{ - ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, types.ZeroHeight(), commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), - ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, types.ZeroHeight(), commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), - ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, types.ZeroHeight(), commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + ibctm.NewClientState(testChainID, ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, types.ZeroHeight(), commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath), + ibctm.NewClientState(testChainID, ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, types.ZeroHeight(), commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath), + ibctm.NewClientState(testChainID, ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, types.ZeroHeight(), commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath), } expGenClients := make(types.IdentifiedClientStates, len(expClients)) @@ -256,38 +251,32 @@ func (suite KeeperTestSuite) TestGetAllGenesisClients() { expGenClients[i] = types.NewIdentifiedClientState(clientIDs[i], expClients[i]) } - // add localhost client - localHostClient, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.GetClientState(suite.chainA.GetContext(), exported.Localhost) - suite.Require().True(found) - expGenClients = append(expGenClients, types.NewIdentifiedClientState(exported.Localhost, localHostClient)) - genClients := suite.chainA.App.GetIBCKeeper().ClientKeeper.GetAllGenesisClients(suite.chainA.GetContext()) suite.Require().Equal(expGenClients.Sort(), genClients) } -func (suite KeeperTestSuite) TestGetAllGenesisMetadata() { +func (suite KeeperTestSuite) TestGetAllGenesisMetadata() { //nolint:govet // this is a test, we are okay with copying locks expectedGenMetadata := []types.IdentifiedGenesisMetadata{ types.NewIdentifiedGenesisMetadata( "07-tendermint-1", []types.GenesisMetadata{ - types.NewGenesisMetadata(ibctmtypes.ProcessedTimeKey(types.NewHeight(0, 1)), []byte("foo")), - types.NewGenesisMetadata(ibctmtypes.ProcessedTimeKey(types.NewHeight(0, 2)), []byte("bar")), - types.NewGenesisMetadata(ibctmtypes.ProcessedTimeKey(types.NewHeight(0, 3)), []byte("baz")), + types.NewGenesisMetadata(ibctm.ProcessedTimeKey(types.NewHeight(0, 1)), []byte("foo")), + types.NewGenesisMetadata(ibctm.ProcessedTimeKey(types.NewHeight(0, 2)), []byte("bar")), + types.NewGenesisMetadata(ibctm.ProcessedTimeKey(types.NewHeight(0, 3)), []byte("baz")), }, ), types.NewIdentifiedGenesisMetadata( "clientB", []types.GenesisMetadata{ - types.NewGenesisMetadata(ibctmtypes.ProcessedTimeKey(types.NewHeight(1, 100)), []byte("val1")), - types.NewGenesisMetadata(ibctmtypes.ProcessedTimeKey(types.NewHeight(2, 300)), []byte("val2")), + types.NewGenesisMetadata(ibctm.ProcessedTimeKey(types.NewHeight(1, 100)), []byte("val1")), + types.NewGenesisMetadata(ibctm.ProcessedTimeKey(types.NewHeight(2, 300)), []byte("val2")), }, ), } genClients := []types.IdentifiedClientState{ - types.NewIdentifiedClientState("07-tendermint-1", &ibctmtypes.ClientState{}), types.NewIdentifiedClientState("clientB", &ibctmtypes.ClientState{}), - types.NewIdentifiedClientState("clientC", &ibctmtypes.ClientState{}), types.NewIdentifiedClientState("clientD", &localhosttypes.ClientState{}), + types.NewIdentifiedClientState("07-tendermint-1", &ibctm.ClientState{}), types.NewIdentifiedClientState("clientB", &ibctm.ClientState{}), } suite.chainA.App.GetIBCKeeper().ClientKeeper.SetAllClientMetadata(suite.chainA.GetContext(), expectedGenMetadata) @@ -297,7 +286,7 @@ func (suite KeeperTestSuite) TestGetAllGenesisMetadata() { suite.Require().Equal(expectedGenMetadata, actualGenMetadata, "retrieved metadata is unexpected") } -func (suite KeeperTestSuite) TestGetConsensusState() { +func (suite KeeperTestSuite) TestGetConsensusState() { //nolint:govet // this is a test, we are okay with copying locks suite.ctx = suite.ctx.WithBlockHeight(10) cases := []struct { name string @@ -323,14 +312,14 @@ func (suite KeeperTestSuite) TestGetConsensusState() { } } -func (suite KeeperTestSuite) TestConsensusStateHelpers() { +func (suite KeeperTestSuite) TestConsensusStateHelpers() { //nolint:govet // this is a test, we are okay with copying locks // initial setup - clientState := ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) + clientState := ibctm.NewClientState(testChainID, ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath) suite.keeper.SetClientState(suite.ctx, testClientID, clientState) suite.keeper.SetClientConsensusState(suite.ctx, testClientID, testClientHeight, suite.consensusState) - nextState := ibctmtypes.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot([]byte("next")), suite.valSetHash) + nextState := ibctm.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot([]byte("next")), suite.valSetHash) testClientHeightPlus5 := types.NewHeight(0, height+5) @@ -349,7 +338,7 @@ func (suite KeeperTestSuite) TestConsensusStateHelpers() { // 2 clients in total are created on chainA. The first client is updated so it contains an initial consensus state // and a consensus state at the update height. -func (suite KeeperTestSuite) TestGetAllConsensusStates() { +func (suite KeeperTestSuite) TestGetAllConsensusStates() { //nolint:govet // this is a test, we are okay with copying locks path := ibctesting.NewPath(suite.chainA, suite.chainB) suite.coordinator.SetupClients(path) @@ -397,3 +386,67 @@ func (suite KeeperTestSuite) TestGetAllConsensusStates() { consStates := suite.chainA.App.GetIBCKeeper().ClientKeeper.GetAllConsensusStates(suite.chainA.GetContext()) suite.Require().Equal(expConsensusStates, consStates, "%s \n\n%s", expConsensusStates, consStates) } + +func (suite KeeperTestSuite) TestIterateClientStates() { //nolint:govet // this is a test, we are okay with copying locks + paths := []*ibctesting.Path{ + ibctesting.NewPath(suite.chainA, suite.chainB), + ibctesting.NewPath(suite.chainA, suite.chainB), + ibctesting.NewPath(suite.chainA, suite.chainB), + } + + solomachines := []*ibctesting.Solomachine{ + ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "06-solomachine-0", "testing", 1), + ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "06-solomachine-1", "testing", 4), + } + + var ( + expTMClientIDs = make([]string, len(paths)) + expSMClientIDs = make([]string, len(solomachines)) + ) + + // create tendermint clients + for i, path := range paths { + suite.coordinator.SetupClients(path) + expTMClientIDs[i] = path.EndpointA.ClientID + } + + // create solomachine clients + for i, sm := range solomachines { + expSMClientIDs[i] = sm.CreateClient(suite.chainA) + } + + testCases := []struct { + name string + prefix []byte + expClientIDs []string + }{ + { + "all clientIDs", + nil, + append(expSMClientIDs, expTMClientIDs...), + }, + { + "tendermint clientIDs", + []byte(exported.Tendermint), + expTMClientIDs, + }, + { + "solo machine clientIDs", + []byte(exported.Solomachine), + expSMClientIDs, + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + var clientIDs []string + suite.chainA.GetSimApp().IBCKeeper.ClientKeeper.IterateClientStates(suite.chainA.GetContext(), tc.prefix, func(clientID string, _ exported.ClientState) bool { + clientIDs = append(clientIDs, clientID) + return false + }) + + suite.Require().Equal(tc.expClientIDs, clientIDs) + }) + } +} diff --git a/modules/core/02-client/keeper/migrations.go b/modules/core/02-client/keeper/migrations.go index c0ad7210341..ad3587ae901 100644 --- a/modules/core/02-client/keeper/migrations.go +++ b/modules/core/02-client/keeper/migrations.go @@ -3,7 +3,7 @@ package keeper import ( sdk "github.com/cosmos/cosmos-sdk/types" - v100 "github.com/cosmos/ibc-go/v4/modules/core/02-client/legacy/v100" + v7 "github.com/cosmos/ibc-go/v7/modules/core/02-client/migrations/v7" ) // Migrator is a struct for handling in-place store migrations. @@ -16,12 +16,12 @@ func NewMigrator(keeper Keeper) Migrator { return Migrator{keeper: keeper} } -// Migrate1to2 migrates from version 1 to 2. +// Migrate2to3 migrates from version 2 to 3. // This migration -// - migrates solo machine client states from v1 to v2 protobuf definition +// - migrates solo machine client states from v2 to v3 protobuf definition // - prunes solo machine consensus states -// - prunes expired tendermint consensus states -// - adds iteration and processed height keys for unexpired tendermint consensus states -func (m Migrator) Migrate1to2(ctx sdk.Context) error { - return v100.MigrateStore(ctx, m.keeper.storeKey, m.keeper.cdc) +// - removes the localhost client +// - asserts that existing tendermint clients are properly registered on the chain codec +func (m Migrator) Migrate2to3(ctx sdk.Context) error { + return v7.MigrateStore(ctx, m.keeper.storeKey, m.keeper.cdc, m.keeper) } diff --git a/modules/core/02-client/keeper/params.go b/modules/core/02-client/keeper/params.go index d2ba2000792..5ed3181472f 100644 --- a/modules/core/02-client/keeper/params.go +++ b/modules/core/02-client/keeper/params.go @@ -3,7 +3,7 @@ package keeper import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" ) // GetAllowedClients retrieves the allowed clients from the paramstore diff --git a/modules/core/02-client/keeper/params_test.go b/modules/core/02-client/keeper/params_test.go index fe136a6346b..49484ad4c85 100644 --- a/modules/core/02-client/keeper/params_test.go +++ b/modules/core/02-client/keeper/params_test.go @@ -1,7 +1,7 @@ package keeper_test import ( - "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" ) func (suite *KeeperTestSuite) TestParams() { @@ -12,6 +12,6 @@ func (suite *KeeperTestSuite) TestParams() { expParams.AllowedClients = []string{} suite.chainA.App.GetIBCKeeper().ClientKeeper.SetParams(suite.chainA.GetContext(), expParams) - params = suite.chainA.App.GetIBCKeeper().ClientKeeper.GetParams(suite.chainA.GetContext()) + _ = suite.chainA.App.GetIBCKeeper().ClientKeeper.GetParams(suite.chainA.GetContext()) suite.Require().Empty(expParams.AllowedClients) } diff --git a/modules/core/02-client/keeper/proposal.go b/modules/core/02-client/keeper/proposal.go index 4b182e5e7c3..a56d9b6ca72 100644 --- a/modules/core/02-client/keeper/proposal.go +++ b/modules/core/02-client/keeper/proposal.go @@ -1,68 +1,61 @@ package keeper import ( - "github.com/armon/go-metrics" + errorsmod "cosmossdk.io/errors" + metrics "github.com/armon/go-metrics" "github.com/cosmos/cosmos-sdk/telemetry" sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v4/modules/core/exported" + "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v7/modules/core/exported" ) // ClientUpdateProposal will retrieve the subject and substitute client. // A callback will occur to the subject client state with the client // prefixed store being provided for both the subject and the substitute client. -// The localhost client is not allowed to be modified with a proposal. The IBC -// client implementations are responsible for validating the parameters of the +// The IBC client implementations are responsible for validating the parameters of the // subtitute (enusring they match the subject's parameters) as well as copying // the necessary consensus states from the subtitute to the subject client // store. The substitute must be Active and the subject must not be Active. func (k Keeper) ClientUpdateProposal(ctx sdk.Context, p *types.ClientUpdateProposal) error { - if p.SubjectClientId == exported.Localhost || p.SubstituteClientId == exported.Localhost { - return sdkerrors.Wrap(types.ErrInvalidUpdateClientProposal, "cannot update localhost client with proposal") - } - subjectClientState, found := k.GetClientState(ctx, p.SubjectClientId) if !found { - return sdkerrors.Wrapf(types.ErrClientNotFound, "subject client with ID %s", p.SubjectClientId) + return errorsmod.Wrapf(types.ErrClientNotFound, "subject client with ID %s", p.SubjectClientId) } subjectClientStore := k.ClientStore(ctx, p.SubjectClientId) if status := subjectClientState.Status(ctx, subjectClientStore, k.cdc); status == exported.Active { - return sdkerrors.Wrap(types.ErrInvalidUpdateClientProposal, "cannot update Active subject client") + return errorsmod.Wrap(types.ErrInvalidUpdateClientProposal, "cannot update Active subject client") } substituteClientState, found := k.GetClientState(ctx, p.SubstituteClientId) if !found { - return sdkerrors.Wrapf(types.ErrClientNotFound, "substitute client with ID %s", p.SubstituteClientId) + return errorsmod.Wrapf(types.ErrClientNotFound, "substitute client with ID %s", p.SubstituteClientId) } if subjectClientState.GetLatestHeight().GTE(substituteClientState.GetLatestHeight()) { - return sdkerrors.Wrapf(types.ErrInvalidHeight, "subject client state latest height is greater or equal to substitute client state latest height (%s >= %s)", subjectClientState.GetLatestHeight(), substituteClientState.GetLatestHeight()) + return errorsmod.Wrapf(types.ErrInvalidHeight, "subject client state latest height is greater or equal to substitute client state latest height (%s >= %s)", subjectClientState.GetLatestHeight(), substituteClientState.GetLatestHeight()) } substituteClientStore := k.ClientStore(ctx, p.SubstituteClientId) if status := substituteClientState.Status(ctx, substituteClientStore, k.cdc); status != exported.Active { - return sdkerrors.Wrapf(types.ErrClientNotActive, "substitute client is not Active, status is %s", status) + return errorsmod.Wrapf(types.ErrClientNotActive, "substitute client is not Active, status is %s", status) } - clientState, err := subjectClientState.CheckSubstituteAndUpdateState(ctx, k.cdc, subjectClientStore, substituteClientStore, substituteClientState) - if err != nil { + if err := subjectClientState.CheckSubstituteAndUpdateState(ctx, k.cdc, subjectClientStore, substituteClientStore, substituteClientState); err != nil { return err } - k.SetClientState(ctx, p.SubjectClientId, clientState) - k.Logger(ctx).Info("client updated after governance proposal passed", "client-id", p.SubjectClientId, "height", clientState.GetLatestHeight().String()) + k.Logger(ctx).Info("client updated after governance proposal passed", "client-id", p.SubjectClientId) defer func() { telemetry.IncrCounterWithLabels( []string{"ibc", "client", "update"}, 1, []metrics.Label{ - telemetry.NewLabel(types.LabelClientType, clientState.ClientType()), + telemetry.NewLabel(types.LabelClientType, substituteClientState.ClientType()), telemetry.NewLabel(types.LabelClientID, p.SubjectClientId), telemetry.NewLabel(types.LabelUpdateType, "proposal"), }, @@ -70,7 +63,7 @@ func (k Keeper) ClientUpdateProposal(ctx sdk.Context, p *types.ClientUpdatePropo }() // emitting events in the keeper for proposal updates to clients - EmitUpdateClientProposalEvent(ctx, p.SubjectClientId, clientState) + emitUpdateClientProposalEvent(ctx, p.SubjectClientId, substituteClientState.ClientType()) return nil } @@ -82,14 +75,14 @@ func (k Keeper) ClientUpdateProposal(ctx sdk.Context, p *types.ClientUpdatePropo func (k Keeper) HandleUpgradeProposal(ctx sdk.Context, p *types.UpgradeProposal) error { clientState, err := types.UnpackClientState(p.UpgradedClientState) if err != nil { - return sdkerrors.Wrap(err, "could not unpack UpgradedClientState") + return errorsmod.Wrap(err, "could not unpack UpgradedClientState") } // zero out any custom fields before setting cs := clientState.ZeroCustomFields() bz, err := types.MarshalClientState(k.cdc, cs) if err != nil { - return sdkerrors.Wrap(err, "could not marshal UpgradedClientState") + return errorsmod.Wrap(err, "could not marshal UpgradedClientState") } if err := k.upgradeKeeper.ScheduleUpgrade(ctx, p.Plan); err != nil { @@ -103,7 +96,7 @@ func (k Keeper) HandleUpgradeProposal(ctx sdk.Context, p *types.UpgradeProposal) } // emitting an event for handling client upgrade proposal - EmitUpgradeClientProposalEvent(ctx, p.Title, p.Plan.Height) + emitUpgradeClientProposalEvent(ctx, p.Title, p.Plan.Height) return nil } diff --git a/modules/core/02-client/keeper/proposal_test.go b/modules/core/02-client/keeper/proposal_test.go index 5a635d99ecc..651e3db61b5 100644 --- a/modules/core/02-client/keeper/proposal_test.go +++ b/modules/core/02-client/keeper/proposal_test.go @@ -1,13 +1,13 @@ package keeper_test import ( - govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" - "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v4/modules/core/exported" - ibctmtypes "github.com/cosmos/ibc-go/v4/modules/light-clients/07-tendermint/types" - ibctesting "github.com/cosmos/ibc-go/v4/testing" + "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v7/modules/core/exported" + ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" + ibctesting "github.com/cosmos/ibc-go/v7/testing" ) func (suite *KeeperTestSuite) TestClientUpdateProposal() { @@ -15,7 +15,6 @@ func (suite *KeeperTestSuite) TestClientUpdateProposal() { subject, substitute string subjectClientState, substituteClientState exported.ClientState content govtypes.Content - err error ) testCases := []struct { @@ -30,7 +29,7 @@ func (suite *KeeperTestSuite) TestClientUpdateProposal() { }, { "subject and substitute use different revision numbers", func() { - tmClientState, ok := substituteClientState.(*ibctmtypes.ClientState) + tmClientState, ok := substituteClientState.(*ibctm.ClientState) suite.Require().True(ok) consState, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.GetClientConsensusState(suite.chainA.GetContext(), substitute, tmClientState.LatestHeight) suite.Require().True(found) @@ -40,23 +39,13 @@ func (suite *KeeperTestSuite) TestClientUpdateProposal() { suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(suite.chainA.GetContext(), substitute, tmClientState.LatestHeight, consState) clientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), substitute) - ibctmtypes.SetProcessedTime(clientStore, tmClientState.LatestHeight, 100) - ibctmtypes.SetProcessedHeight(clientStore, tmClientState.LatestHeight, types.NewHeight(0, 1)) + ibctm.SetProcessedTime(clientStore, tmClientState.LatestHeight, 100) + ibctm.SetProcessedHeight(clientStore, tmClientState.LatestHeight, types.NewHeight(0, 1)) suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), substitute, tmClientState) content = types.NewClientUpdateProposal(ibctesting.Title, ibctesting.Description, subject, substitute) }, true, }, - { - "cannot use localhost as subject", func() { - content = types.NewClientUpdateProposal(ibctesting.Title, ibctesting.Description, exported.Localhost, substitute) - }, false, - }, - { - "cannot use localhost as substitute", func() { - content = types.NewClientUpdateProposal(ibctesting.Title, ibctesting.Description, subject, exported.Localhost) - }, false, - }, { "cannot use solomachine as substitute for tendermint client", func() { solomachine := ibctesting.NewSolomachine(suite.T(), suite.cdc, "solo machine", "", 1) @@ -78,7 +67,7 @@ func (suite *KeeperTestSuite) TestClientUpdateProposal() { }, { "subject and substitute have equal latest height", func() { - tmClientState, ok := subjectClientState.(*ibctmtypes.ClientState) + tmClientState, ok := subjectClientState.(*ibctm.ClientState) suite.Require().True(ok) tmClientState.LatestHeight = substituteClientState.GetLatestHeight().(types.Height) suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), subject, tmClientState) @@ -88,7 +77,7 @@ func (suite *KeeperTestSuite) TestClientUpdateProposal() { }, { "update fails, client is not frozen or expired", func() { - tmClientState, ok := subjectClientState.(*ibctmtypes.ClientState) + tmClientState, ok := subjectClientState.(*ibctm.ClientState) suite.Require().True(ok) tmClientState.FrozenHeight = types.ZeroHeight() suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), subject, tmClientState) @@ -98,7 +87,7 @@ func (suite *KeeperTestSuite) TestClientUpdateProposal() { }, { "substitute is frozen", func() { - tmClientState, ok := substituteClientState.(*ibctmtypes.ClientState) + tmClientState, ok := substituteClientState.(*ibctm.ClientState) suite.Require().True(ok) tmClientState.FrozenHeight = types.NewHeight(0, 1) suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), substitute, tmClientState) @@ -124,18 +113,20 @@ func (suite *KeeperTestSuite) TestClientUpdateProposal() { substitute = substitutePath.EndpointA.ClientID // update substitute twice - substitutePath.EndpointA.UpdateClient() - substitutePath.EndpointA.UpdateClient() + err := substitutePath.EndpointA.UpdateClient() + suite.Require().NoError(err) + err = substitutePath.EndpointA.UpdateClient() + suite.Require().NoError(err) substituteClientState = suite.chainA.GetClientState(substitute) - tmClientState, ok := subjectClientState.(*ibctmtypes.ClientState) + tmClientState, ok := subjectClientState.(*ibctm.ClientState) suite.Require().True(ok) tmClientState.AllowUpdateAfterMisbehaviour = true tmClientState.AllowUpdateAfterExpiry = true tmClientState.FrozenHeight = tmClientState.LatestHeight suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), subject, tmClientState) - tmClientState, ok = substituteClientState.(*ibctmtypes.ClientState) + tmClientState, ok = substituteClientState.(*ibctm.ClientState) suite.Require().True(ok) tmClientState.AllowUpdateAfterMisbehaviour = true tmClientState.AllowUpdateAfterExpiry = true @@ -158,7 +149,7 @@ func (suite *KeeperTestSuite) TestClientUpdateProposal() { func (suite *KeeperTestSuite) TestHandleUpgradeProposal() { var ( - upgradedClientState *ibctmtypes.ClientState + upgradedClientState *ibctm.ClientState oldPlan, plan upgradetypes.Plan content govtypes.Content err error @@ -188,13 +179,13 @@ func (suite *KeeperTestSuite) TestHandleUpgradeProposal() { }, { "cannot unpack client state", func() { - any, err := types.PackConsensusState(&ibctmtypes.ConsensusState{}) + protoAny, err := types.PackConsensusState(&ibctm.ConsensusState{}) suite.Require().NoError(err) content = &types.UpgradeProposal{ Title: ibctesting.Title, Description: ibctesting.Description, Plan: plan, - UpgradedClientState: any, + UpgradedClientState: protoAny, } }, false, }, @@ -209,7 +200,7 @@ func (suite *KeeperTestSuite) TestHandleUpgradeProposal() { path := ibctesting.NewPath(suite.chainA, suite.chainB) suite.coordinator.SetupClients(path) - upgradedClientState = suite.chainA.GetClientState(path.EndpointA.ClientID).ZeroCustomFields().(*ibctmtypes.ClientState) + upgradedClientState = suite.chainA.GetClientState(path.EndpointA.ClientID).ZeroCustomFields().(*ibctm.ClientState) // use height 1000 to distinguish from old plan plan = upgradetypes.Plan{ @@ -228,7 +219,8 @@ func (suite *KeeperTestSuite) TestHandleUpgradeProposal() { bz, err := types.MarshalClientState(suite.chainA.App.AppCodec(), upgradedClientState) suite.Require().NoError(err) - suite.chainA.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainA.GetContext(), oldPlan.Height, bz) + + suite.chainA.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainA.GetContext(), oldPlan.Height, bz) //nolint:errcheck } upgradeProp, ok := content.(*types.UpgradeProposal) diff --git a/modules/core/02-client/legacy/v100/genesis.go b/modules/core/02-client/legacy/v100/genesis.go deleted file mode 100644 index 156a067f386..00000000000 --- a/modules/core/02-client/legacy/v100/genesis.go +++ /dev/null @@ -1,151 +0,0 @@ -package v100 - -import ( - "bytes" - "time" - - "github.com/cosmos/cosmos-sdk/codec" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - - "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" - "github.com/cosmos/ibc-go/v4/modules/core/exported" - ibctmtypes "github.com/cosmos/ibc-go/v4/modules/light-clients/07-tendermint/types" -) - -// MigrateGenesis accepts exported v1.0.0 IBC client genesis file and migrates it to: -// -// - Update solo machine client state protobuf definition (v1 to v2) -// - Remove all solo machine consensus states -// - Remove all expired tendermint consensus states -// - Adds ProcessedHeight and Iteration keys for unexpired tendermint consensus states -func MigrateGenesis(cdc codec.BinaryCodec, clientGenState *types.GenesisState, genesisBlockTime time.Time, selfHeight exported.Height) (*types.GenesisState, error) { - // To prune the consensus states, we will create new clientsConsensus - // and clientsMetadata. These slices will be filled up with consensus states - // which should not be pruned. No solo machine consensus states should be added - // and only unexpired consensus states for tendermint clients will be added. - // The metadata keys for unexpired consensus states will be added to clientsMetadata - var ( - clientsConsensus []types.ClientConsensusStates - clientsMetadata []types.IdentifiedGenesisMetadata - ) - - for i, client := range clientGenState.Clients { - clientType, _, err := types.ParseClientIdentifier(client.ClientId) - if err != nil { - return nil, err - } - - // update solo machine client state defintions - if clientType == exported.Solomachine { - clientState := &ClientState{} - if err := cdc.Unmarshal(client.ClientState.Value, clientState); err != nil { - return nil, sdkerrors.Wrap(err, "failed to unmarshal client state bytes into solo machine client state") - } - - updatedClientState := migrateSolomachine(clientState) - - any, err := types.PackClientState(updatedClientState) - if err != nil { - return nil, err - } - - clientGenState.Clients[i] = types.IdentifiedClientState{ - ClientId: client.ClientId, - ClientState: any, - } - } - - // iterate consensus states by client - for _, clientConsensusStates := range clientGenState.ClientsConsensus { - // look for consensus states for the current client - if clientConsensusStates.ClientId == client.ClientId { - switch clientType { - case exported.Solomachine: - // remove all consensus states for the solo machine - // do not add to new clientsConsensus - - case exported.Tendermint: - // only add non expired consensus states to new clientsConsensus - tmClientState, ok := client.ClientState.GetCachedValue().(*ibctmtypes.ClientState) - if !ok { - return nil, types.ErrInvalidClient - } - - // collect unexpired consensus states - var unexpiredConsensusStates []types.ConsensusStateWithHeight - for _, consState := range clientConsensusStates.ConsensusStates { - tmConsState := consState.ConsensusState.GetCachedValue().(*ibctmtypes.ConsensusState) - if !tmClientState.IsExpired(tmConsState.Timestamp, genesisBlockTime) { - unexpiredConsensusStates = append(unexpiredConsensusStates, consState) - } - } - - // if we found at least one unexpired consensus state, create a clientConsensusState - // and add it to clientsConsensus - if len(unexpiredConsensusStates) != 0 { - clientsConsensus = append(clientsConsensus, types.ClientConsensusStates{ - ClientId: client.ClientId, - ConsensusStates: unexpiredConsensusStates, - }) - } - - // collect metadata for unexpired consensus states - var clientMetadata []types.GenesisMetadata - - // remove all expired tendermint consensus state metadata by adding only - // unexpired consensus state metadata - for _, consState := range unexpiredConsensusStates { - for _, identifiedGenMetadata := range clientGenState.ClientsMetadata { - // look for metadata for current client - if identifiedGenMetadata.ClientId == client.ClientId { - - // obtain height for consensus state being pruned - height := consState.Height - - // iterate through metadata and find metadata for current unexpired height - // only unexpired consensus state metadata should be added - for _, metadata := range identifiedGenMetadata.ClientMetadata { - // the previous version of IBC only contained the processed time metadata - // if we find the processed time metadata for an unexpired height, add the - // iteration key and processed height keys. - if bytes.Equal(metadata.Key, ibctmtypes.ProcessedTimeKey(height)) { - clientMetadata = append(clientMetadata, - // set the processed height using the current self height - // this is safe, it may cause delays in packet processing if there - // is a non zero connection delay time - types.GenesisMetadata{ - Key: ibctmtypes.ProcessedHeightKey(height), - Value: []byte(selfHeight.String()), - }, - metadata, // processed time - types.GenesisMetadata{ - Key: ibctmtypes.IterationKey(height), - Value: host.ConsensusStateKey(height), - }) - } - } - - } - } - } - - // if we have metadata for unexipred consensus states, add it to consensusMetadata - if len(clientMetadata) != 0 { - clientsMetadata = append(clientsMetadata, types.IdentifiedGenesisMetadata{ - ClientId: client.ClientId, - ClientMetadata: clientMetadata, - }) - } - - default: - break - } - } - } - } - - clientGenState.ClientsConsensus = clientsConsensus - clientGenState.ClientsMetadata = clientsMetadata - return clientGenState, nil -} diff --git a/modules/core/02-client/legacy/v100/genesis_test.go b/modules/core/02-client/legacy/v100/genesis_test.go deleted file mode 100644 index 04f6b33f053..00000000000 --- a/modules/core/02-client/legacy/v100/genesis_test.go +++ /dev/null @@ -1,308 +0,0 @@ -package v100_test - -import ( - "bytes" - "encoding/json" - "time" - - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/codec" - codectypes "github.com/cosmos/cosmos-sdk/codec/types" - - ibcclient "github.com/cosmos/ibc-go/v4/modules/core/02-client" - v100 "github.com/cosmos/ibc-go/v4/modules/core/02-client/legacy/v100" - "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" - "github.com/cosmos/ibc-go/v4/modules/core/exported" - ibctmtypes "github.com/cosmos/ibc-go/v4/modules/light-clients/07-tendermint/types" - ibctesting "github.com/cosmos/ibc-go/v4/testing" - "github.com/cosmos/ibc-go/v4/testing/simapp" -) - -func (suite *LegacyTestSuite) TestMigrateGenesisSolomachine() { - path := ibctesting.NewPath(suite.chainA, suite.chainB) - encodingConfig := simapp.MakeTestEncodingConfig() - clientCtx := client.Context{}. - WithInterfaceRegistry(encodingConfig.InterfaceRegistry). - WithTxConfig(encodingConfig.TxConfig). - WithJSONCodec(encodingConfig.Marshaler) - - // create multiple legacy solo machine clients - solomachine := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "06-solomachine-0", "testing", 1) - solomachineMulti := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "06-solomachine-1", "testing", 4) - - // create tendermint clients - suite.coordinator.SetupClients(path) - err := path.EndpointA.UpdateClient() - suite.Require().NoError(err) - clientGenState := ibcclient.ExportGenesis(path.EndpointA.Chain.GetContext(), path.EndpointA.Chain.App.GetIBCKeeper().ClientKeeper) - - // manually generate old proto buf definitions and set in genesis - // NOTE: we cannot use 'ExportGenesis' for the solo machines since we are - // using client states and consensus states which do not implement the exported.ClientState - // and exported.ConsensusState interface - var clients []types.IdentifiedClientState - for _, sm := range []*ibctesting.Solomachine{solomachine, solomachineMulti} { - clientState := sm.ClientState() - - var seq uint64 - if clientState.IsFrozen { - seq = 1 - } - - // generate old client state proto defintion - legacyClientState := &v100.ClientState{ - Sequence: clientState.Sequence, - FrozenSequence: seq, - ConsensusState: &v100.ConsensusState{ - PublicKey: clientState.ConsensusState.PublicKey, - Diversifier: clientState.ConsensusState.Diversifier, - Timestamp: clientState.ConsensusState.Timestamp, - }, - AllowUpdateAfterProposal: clientState.AllowUpdateAfterProposal, - } - - // set client state - any, err := codectypes.NewAnyWithValue(legacyClientState) - suite.Require().NoError(err) - suite.Require().NotNil(any) - client := types.IdentifiedClientState{ - ClientId: sm.ClientID, - ClientState: any, - } - clients = append(clients, client) - - // set in store for ease of determining expected genesis - clientStore := path.EndpointA.Chain.App.GetIBCKeeper().ClientKeeper.ClientStore(path.EndpointA.Chain.GetContext(), sm.ClientID) - bz, err := path.EndpointA.Chain.App.AppCodec().MarshalInterface(legacyClientState) - suite.Require().NoError(err) - clientStore.Set(host.ClientStateKey(), bz) - - // set some consensus states - height1 := types.NewHeight(0, 1) - height2 := types.NewHeight(1, 2) - height3 := types.NewHeight(0, 123) - - any, err = codectypes.NewAnyWithValue(legacyClientState.ConsensusState) - suite.Require().NoError(err) - suite.Require().NotNil(any) - consensusState1 := types.ConsensusStateWithHeight{ - Height: height1, - ConsensusState: any, - } - consensusState2 := types.ConsensusStateWithHeight{ - Height: height2, - ConsensusState: any, - } - consensusState3 := types.ConsensusStateWithHeight{ - Height: height3, - ConsensusState: any, - } - - clientConsensusState := types.ClientConsensusStates{ - ClientId: sm.ClientID, - ConsensusStates: []types.ConsensusStateWithHeight{consensusState1, consensusState2, consensusState3}, - } - - clientGenState.ClientsConsensus = append(clientGenState.ClientsConsensus, clientConsensusState) - - // set in store for ease of determining expected genesis - bz, err = path.EndpointA.Chain.App.AppCodec().MarshalInterface(legacyClientState.ConsensusState) - suite.Require().NoError(err) - clientStore.Set(host.ConsensusStateKey(height1), bz) - clientStore.Set(host.ConsensusStateKey(height2), bz) - clientStore.Set(host.ConsensusStateKey(height3), bz) - } - // solo machine clients must come before tendermint in expected - clientGenState.Clients = append(clients, clientGenState.Clients...) - - // migrate store get expected genesis - // store migration and genesis migration should produce identical results - err = v100.MigrateStore(path.EndpointA.Chain.GetContext(), path.EndpointA.Chain.GetSimApp().GetKey(host.StoreKey), path.EndpointA.Chain.App.AppCodec()) - suite.Require().NoError(err) - expectedClientGenState := ibcclient.ExportGenesis(path.EndpointA.Chain.GetContext(), path.EndpointA.Chain.App.GetIBCKeeper().ClientKeeper) - - // NOTE: genesis time isn't updated since we aren't testing for tendermint consensus state pruning - migrated, err := v100.MigrateGenesis(codec.NewProtoCodec(clientCtx.InterfaceRegistry), &clientGenState, suite.coordinator.CurrentTime, types.GetSelfHeight(suite.chainA.GetContext())) - suite.Require().NoError(err) - - // 'ExportGenesis' order metadata keys by processedheight, processedtime for all heights, then it appends all iteration keys - // In order to match the genesis migration with export genesis (from store migrations) we must reorder the iteration keys to be last - // This isn't ideal, but it is better than modifying the genesis migration from a previous version to match the export genesis of a new version - // which provides no benefit except nicer testing - for i, clientMetadata := range migrated.ClientsMetadata { - var updatedMetadata []types.GenesisMetadata - var iterationKeys []types.GenesisMetadata - for _, metadata := range clientMetadata.ClientMetadata { - if bytes.HasPrefix(metadata.Key, []byte(ibctmtypes.KeyIterateConsensusStatePrefix)) { - iterationKeys = append(iterationKeys, metadata) - } else { - updatedMetadata = append(updatedMetadata, metadata) - } - } - updatedMetadata = append(updatedMetadata, iterationKeys...) - migrated.ClientsMetadata[i] = types.IdentifiedGenesisMetadata{ - ClientId: clientMetadata.ClientId, - ClientMetadata: updatedMetadata, - } - } - - bz, err := clientCtx.JSONCodec.MarshalJSON(&expectedClientGenState) - suite.Require().NoError(err) - - // Indent the JSON bz correctly. - var jsonObj map[string]interface{} - err = json.Unmarshal(bz, &jsonObj) - suite.Require().NoError(err) - expectedIndentedBz, err := json.MarshalIndent(jsonObj, "", "\t") - suite.Require().NoError(err) - - bz, err = clientCtx.JSONCodec.MarshalJSON(migrated) - suite.Require().NoError(err) - - // Indent the JSON bz correctly. - err = json.Unmarshal(bz, &jsonObj) - suite.Require().NoError(err) - indentedBz, err := json.MarshalIndent(jsonObj, "", "\t") - suite.Require().NoError(err) - - suite.Require().Equal(string(expectedIndentedBz), string(indentedBz)) -} - -func (suite *LegacyTestSuite) TestMigrateGenesisTendermint() { - // create two paths and setup clients - path1 := ibctesting.NewPath(suite.chainA, suite.chainB) - path2 := ibctesting.NewPath(suite.chainA, suite.chainB) - encodingConfig := simapp.MakeTestEncodingConfig() - clientCtx := client.Context{}. - WithInterfaceRegistry(encodingConfig.InterfaceRegistry). - WithTxConfig(encodingConfig.TxConfig). - WithJSONCodec(encodingConfig.Marshaler) - - suite.coordinator.SetupClients(path1) - suite.coordinator.SetupClients(path2) - - // collect all heights expected to be pruned - var path1PruneHeights, path2PruneHeights []exported.Height - path1PruneHeights = append(path1PruneHeights, path1.EndpointA.GetClientState().GetLatestHeight()) - path2PruneHeights = append(path2PruneHeights, path2.EndpointA.GetClientState().GetLatestHeight()) - - // these heights will be expired and also pruned - for i := 0; i < 3; i++ { - path1.EndpointA.UpdateClient() - path1PruneHeights = append(path1PruneHeights, path1.EndpointA.GetClientState().GetLatestHeight()) - } - for i := 0; i < 3; i++ { - path2.EndpointA.UpdateClient() - path2PruneHeights = append(path2PruneHeights, path2.EndpointA.GetClientState().GetLatestHeight()) - } - - // Increment the time by a week - suite.coordinator.IncrementTimeBy(7 * 24 * time.Hour) - - // create the consensus state that can be used as trusted height for next update - path1.EndpointA.UpdateClient() - path1.EndpointA.UpdateClient() - path2.EndpointA.UpdateClient() - path2.EndpointA.UpdateClient() - - clientGenState := ibcclient.ExportGenesis(suite.chainA.GetContext(), suite.chainA.App.GetIBCKeeper().ClientKeeper) - suite.Require().NotNil(clientGenState.Clients) - suite.Require().NotNil(clientGenState.ClientsConsensus) - suite.Require().NotNil(clientGenState.ClientsMetadata) - - // Increment the time by another week, then update the client. - // This will cause the consensus states created before the first time increment - // to be expired - suite.coordinator.IncrementTimeBy(7 * 24 * time.Hour) - - // migrate store get expected genesis - // store migration and genesis migration should produce identical results - err := v100.MigrateStore(path1.EndpointA.Chain.GetContext(), path1.EndpointA.Chain.GetSimApp().GetKey(host.StoreKey), path1.EndpointA.Chain.App.AppCodec()) - suite.Require().NoError(err) - expectedClientGenState := ibcclient.ExportGenesis(path1.EndpointA.Chain.GetContext(), path1.EndpointA.Chain.App.GetIBCKeeper().ClientKeeper) - - migrated, err := v100.MigrateGenesis(codec.NewProtoCodec(clientCtx.InterfaceRegistry), &clientGenState, suite.coordinator.CurrentTime, types.GetSelfHeight(suite.chainA.GetContext())) - suite.Require().NoError(err) - - // 'ExportGenesis' order metadata keys by processedheight, processedtime for all heights, then it appends all iteration keys - // In order to match the genesis migration with export genesis we must reorder the iteration keys to be last - // This isn't ideal, but it is better than modifying the genesis migration from a previous version to match the export genesis of a new version - // which provides no benefit except nicer testing - for i, clientMetadata := range migrated.ClientsMetadata { - var updatedMetadata []types.GenesisMetadata - var iterationKeys []types.GenesisMetadata - for _, metadata := range clientMetadata.ClientMetadata { - if bytes.HasPrefix(metadata.Key, []byte(ibctmtypes.KeyIterateConsensusStatePrefix)) { - iterationKeys = append(iterationKeys, metadata) - } else { - updatedMetadata = append(updatedMetadata, metadata) - } - } - updatedMetadata = append(updatedMetadata, iterationKeys...) - migrated.ClientsMetadata[i] = types.IdentifiedGenesisMetadata{ - ClientId: clientMetadata.ClientId, - ClientMetadata: updatedMetadata, - } - } - - // check path 1 client pruning - for _, height := range path1PruneHeights { - for _, client := range migrated.ClientsConsensus { - if client.ClientId == path1.EndpointA.ClientID { - for _, consensusState := range client.ConsensusStates { - suite.Require().NotEqual(height, consensusState.Height) - } - } - } - for _, client := range migrated.ClientsMetadata { - if client.ClientId == path1.EndpointA.ClientID { - for _, metadata := range client.ClientMetadata { - suite.Require().NotEqual(ibctmtypes.ProcessedTimeKey(height), metadata.Key) - suite.Require().NotEqual(ibctmtypes.ProcessedHeightKey(height), metadata.Key) - suite.Require().NotEqual(ibctmtypes.IterationKey(height), metadata.Key) - } - } - } - } - - // check path 2 client pruning - for _, height := range path2PruneHeights { - for _, client := range migrated.ClientsConsensus { - if client.ClientId == path2.EndpointA.ClientID { - for _, consensusState := range client.ConsensusStates { - suite.Require().NotEqual(height, consensusState.Height) - } - } - } - for _, client := range migrated.ClientsMetadata { - if client.ClientId == path2.EndpointA.ClientID { - for _, metadata := range client.ClientMetadata { - suite.Require().NotEqual(ibctmtypes.ProcessedTimeKey(height), metadata.Key) - suite.Require().NotEqual(ibctmtypes.ProcessedHeightKey(height), metadata.Key) - suite.Require().NotEqual(ibctmtypes.IterationKey(height), metadata.Key) - } - } - } - } - bz, err := clientCtx.JSONCodec.MarshalJSON(&expectedClientGenState) - suite.Require().NoError(err) - - // Indent the JSON bz correctly. - var jsonObj map[string]interface{} - err = json.Unmarshal(bz, &jsonObj) - suite.Require().NoError(err) - expectedIndentedBz, err := json.MarshalIndent(jsonObj, "", "\t") - suite.Require().NoError(err) - - bz, err = clientCtx.JSONCodec.MarshalJSON(migrated) - suite.Require().NoError(err) - - // Indent the JSON bz correctly. - err = json.Unmarshal(bz, &jsonObj) - suite.Require().NoError(err) - indentedBz, err := json.MarshalIndent(jsonObj, "", "\t") - suite.Require().NoError(err) - - suite.Require().Equal(string(expectedIndentedBz), string(indentedBz)) -} diff --git a/modules/core/02-client/legacy/v100/solomachine.pb.go b/modules/core/02-client/legacy/v100/solomachine.pb.go deleted file mode 100644 index 3ed69fb1b21..00000000000 --- a/modules/core/02-client/legacy/v100/solomachine.pb.go +++ /dev/null @@ -1,4121 +0,0 @@ -// Code generated by protoc-gen-gogo. DO NOT EDIT. -// source: ibc/lightclients/solomachine/v1/solomachine.proto - -package v100 - -import ( - fmt "fmt" - types "github.com/cosmos/cosmos-sdk/codec/types" - types1 "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types" - types2 "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" - _ "github.com/gogo/protobuf/gogoproto" - proto "github.com/gogo/protobuf/proto" - io "io" - math "math" - math_bits "math/bits" -) - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package - -// DataType defines the type of solo machine proof being created. This is done -// to preserve uniqueness of different data sign byte encodings. -type DataType int32 - -const ( - // Default State - UNSPECIFIED DataType = 0 - // Data type for client state verification - CLIENT DataType = 1 - // Data type for consensus state verification - CONSENSUS DataType = 2 - // Data type for connection state verification - CONNECTION DataType = 3 - // Data type for channel state verification - CHANNEL DataType = 4 - // Data type for packet commitment verification - PACKETCOMMITMENT DataType = 5 - // Data type for packet acknowledgement verification - PACKETACKNOWLEDGEMENT DataType = 6 - // Data type for packet receipt absence verification - PACKETRECEIPTABSENCE DataType = 7 - // Data type for next sequence recv verification - NEXTSEQUENCERECV DataType = 8 - // Data type for header verification - HEADER DataType = 9 -) - -var DataType_name = map[int32]string{ - 0: "DATA_TYPE_UNINITIALIZED_UNSPECIFIED", - 1: "DATA_TYPE_CLIENT_STATE", - 2: "DATA_TYPE_CONSENSUS_STATE", - 3: "DATA_TYPE_CONNECTION_STATE", - 4: "DATA_TYPE_CHANNEL_STATE", - 5: "DATA_TYPE_PACKET_COMMITMENT", - 6: "DATA_TYPE_PACKET_ACKNOWLEDGEMENT", - 7: "DATA_TYPE_PACKET_RECEIPT_ABSENCE", - 8: "DATA_TYPE_NEXT_SEQUENCE_RECV", - 9: "DATA_TYPE_HEADER", -} - -var DataType_value = map[string]int32{ - "DATA_TYPE_UNINITIALIZED_UNSPECIFIED": 0, - "DATA_TYPE_CLIENT_STATE": 1, - "DATA_TYPE_CONSENSUS_STATE": 2, - "DATA_TYPE_CONNECTION_STATE": 3, - "DATA_TYPE_CHANNEL_STATE": 4, - "DATA_TYPE_PACKET_COMMITMENT": 5, - "DATA_TYPE_PACKET_ACKNOWLEDGEMENT": 6, - "DATA_TYPE_PACKET_RECEIPT_ABSENCE": 7, - "DATA_TYPE_NEXT_SEQUENCE_RECV": 8, - "DATA_TYPE_HEADER": 9, -} - -func (x DataType) String() string { - return proto.EnumName(DataType_name, int32(x)) -} - -func (DataType) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_6cc2ee18f7f86d4e, []int{0} -} - -// ClientState defines a solo machine client that tracks the current consensus -// state and if the client is frozen. -type ClientState struct { - // latest sequence of the client state - Sequence uint64 `protobuf:"varint,1,opt,name=sequence,proto3" json:"sequence,omitempty"` - // frozen sequence of the solo machine - FrozenSequence uint64 `protobuf:"varint,2,opt,name=frozen_sequence,json=frozenSequence,proto3" json:"frozen_sequence,omitempty" yaml:"frozen_sequence"` - ConsensusState *ConsensusState `protobuf:"bytes,3,opt,name=consensus_state,json=consensusState,proto3" json:"consensus_state,omitempty" yaml:"consensus_state"` - // when set to true, will allow governance to update a solo machine client. - // The client will be unfrozen if it is frozen. - AllowUpdateAfterProposal bool `protobuf:"varint,4,opt,name=allow_update_after_proposal,json=allowUpdateAfterProposal,proto3" json:"allow_update_after_proposal,omitempty" yaml:"allow_update_after_proposal"` -} - -func (m *ClientState) Reset() { *m = ClientState{} } -func (m *ClientState) String() string { return proto.CompactTextString(m) } -func (*ClientState) ProtoMessage() {} -func (*ClientState) Descriptor() ([]byte, []int) { - return fileDescriptor_6cc2ee18f7f86d4e, []int{0} -} -func (m *ClientState) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *ClientState) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_ClientState.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalToSizedBuffer(b) - if err != nil { - return nil, err - } - return b[:n], nil - } -} -func (m *ClientState) XXX_Merge(src proto.Message) { - xxx_messageInfo_ClientState.Merge(m, src) -} -func (m *ClientState) XXX_Size() int { - return m.Size() -} -func (m *ClientState) XXX_DiscardUnknown() { - xxx_messageInfo_ClientState.DiscardUnknown(m) -} - -var xxx_messageInfo_ClientState proto.InternalMessageInfo - -// ConsensusState defines a solo machine consensus state. The sequence of a -// consensus state is contained in the "height" key used in storing the -// consensus state. -type ConsensusState struct { - // public key of the solo machine - PublicKey *types.Any `protobuf:"bytes,1,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty" yaml:"public_key"` - // diversifier allows the same public key to be re-used across different solo - // machine clients (potentially on different chains) without being considered - // misbehaviour. - Diversifier string `protobuf:"bytes,2,opt,name=diversifier,proto3" json:"diversifier,omitempty"` - Timestamp uint64 `protobuf:"varint,3,opt,name=timestamp,proto3" json:"timestamp,omitempty"` -} - -func (m *ConsensusState) Reset() { *m = ConsensusState{} } -func (m *ConsensusState) String() string { return proto.CompactTextString(m) } -func (*ConsensusState) ProtoMessage() {} -func (*ConsensusState) Descriptor() ([]byte, []int) { - return fileDescriptor_6cc2ee18f7f86d4e, []int{1} -} -func (m *ConsensusState) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *ConsensusState) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_ConsensusState.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 *ConsensusState) XXX_Merge(src proto.Message) { - xxx_messageInfo_ConsensusState.Merge(m, src) -} -func (m *ConsensusState) XXX_Size() int { - return m.Size() -} -func (m *ConsensusState) XXX_DiscardUnknown() { - xxx_messageInfo_ConsensusState.DiscardUnknown(m) -} - -var xxx_messageInfo_ConsensusState proto.InternalMessageInfo - -// Header defines a solo machine consensus header -type Header struct { - // sequence to update solo machine public key at - Sequence uint64 `protobuf:"varint,1,opt,name=sequence,proto3" json:"sequence,omitempty"` - Timestamp uint64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` - Signature []byte `protobuf:"bytes,3,opt,name=signature,proto3" json:"signature,omitempty"` - NewPublicKey *types.Any `protobuf:"bytes,4,opt,name=new_public_key,json=newPublicKey,proto3" json:"new_public_key,omitempty" yaml:"new_public_key"` - NewDiversifier string `protobuf:"bytes,5,opt,name=new_diversifier,json=newDiversifier,proto3" json:"new_diversifier,omitempty" yaml:"new_diversifier"` -} - -func (m *Header) Reset() { *m = Header{} } -func (m *Header) String() string { return proto.CompactTextString(m) } -func (*Header) ProtoMessage() {} -func (*Header) Descriptor() ([]byte, []int) { - return fileDescriptor_6cc2ee18f7f86d4e, []int{2} -} -func (m *Header) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *Header) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_Header.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 *Header) XXX_Merge(src proto.Message) { - xxx_messageInfo_Header.Merge(m, src) -} -func (m *Header) XXX_Size() int { - return m.Size() -} -func (m *Header) XXX_DiscardUnknown() { - xxx_messageInfo_Header.DiscardUnknown(m) -} - -var xxx_messageInfo_Header proto.InternalMessageInfo - -// Misbehaviour defines misbehaviour for a solo machine which consists -// of a sequence and two signatures over different messages at that sequence. -type Misbehaviour struct { - ClientId string `protobuf:"bytes,1,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty" yaml:"client_id"` - Sequence uint64 `protobuf:"varint,2,opt,name=sequence,proto3" json:"sequence,omitempty"` - SignatureOne *SignatureAndData `protobuf:"bytes,3,opt,name=signature_one,json=signatureOne,proto3" json:"signature_one,omitempty" yaml:"signature_one"` - SignatureTwo *SignatureAndData `protobuf:"bytes,4,opt,name=signature_two,json=signatureTwo,proto3" json:"signature_two,omitempty" yaml:"signature_two"` -} - -func (m *Misbehaviour) Reset() { *m = Misbehaviour{} } -func (m *Misbehaviour) String() string { return proto.CompactTextString(m) } -func (*Misbehaviour) ProtoMessage() {} -func (*Misbehaviour) Descriptor() ([]byte, []int) { - return fileDescriptor_6cc2ee18f7f86d4e, []int{3} -} -func (m *Misbehaviour) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *Misbehaviour) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_Misbehaviour.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 *Misbehaviour) XXX_Merge(src proto.Message) { - xxx_messageInfo_Misbehaviour.Merge(m, src) -} -func (m *Misbehaviour) XXX_Size() int { - return m.Size() -} -func (m *Misbehaviour) XXX_DiscardUnknown() { - xxx_messageInfo_Misbehaviour.DiscardUnknown(m) -} - -var xxx_messageInfo_Misbehaviour proto.InternalMessageInfo - -// SignatureAndData contains a signature and the data signed over to create that -// signature. -type SignatureAndData struct { - Signature []byte `protobuf:"bytes,1,opt,name=signature,proto3" json:"signature,omitempty"` - DataType DataType `protobuf:"varint,2,opt,name=data_type,json=dataType,proto3,enum=ibc.lightclients.solomachine.v1.DataType" json:"data_type,omitempty" yaml:"data_type"` - Data []byte `protobuf:"bytes,3,opt,name=data,proto3" json:"data,omitempty"` - Timestamp uint64 `protobuf:"varint,4,opt,name=timestamp,proto3" json:"timestamp,omitempty"` -} - -func (m *SignatureAndData) Reset() { *m = SignatureAndData{} } -func (m *SignatureAndData) String() string { return proto.CompactTextString(m) } -func (*SignatureAndData) ProtoMessage() {} -func (*SignatureAndData) Descriptor() ([]byte, []int) { - return fileDescriptor_6cc2ee18f7f86d4e, []int{4} -} -func (m *SignatureAndData) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *SignatureAndData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_SignatureAndData.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 *SignatureAndData) XXX_Merge(src proto.Message) { - xxx_messageInfo_SignatureAndData.Merge(m, src) -} -func (m *SignatureAndData) XXX_Size() int { - return m.Size() -} -func (m *SignatureAndData) XXX_DiscardUnknown() { - xxx_messageInfo_SignatureAndData.DiscardUnknown(m) -} - -var xxx_messageInfo_SignatureAndData proto.InternalMessageInfo - -// TimestampedSignatureData contains the signature data and the timestamp of the -// signature. -type TimestampedSignatureData struct { - SignatureData []byte `protobuf:"bytes,1,opt,name=signature_data,json=signatureData,proto3" json:"signature_data,omitempty" yaml:"signature_data"` - Timestamp uint64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` -} - -func (m *TimestampedSignatureData) Reset() { *m = TimestampedSignatureData{} } -func (m *TimestampedSignatureData) String() string { return proto.CompactTextString(m) } -func (*TimestampedSignatureData) ProtoMessage() {} -func (*TimestampedSignatureData) Descriptor() ([]byte, []int) { - return fileDescriptor_6cc2ee18f7f86d4e, []int{5} -} -func (m *TimestampedSignatureData) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *TimestampedSignatureData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_TimestampedSignatureData.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 *TimestampedSignatureData) XXX_Merge(src proto.Message) { - xxx_messageInfo_TimestampedSignatureData.Merge(m, src) -} -func (m *TimestampedSignatureData) XXX_Size() int { - return m.Size() -} -func (m *TimestampedSignatureData) XXX_DiscardUnknown() { - xxx_messageInfo_TimestampedSignatureData.DiscardUnknown(m) -} - -var xxx_messageInfo_TimestampedSignatureData proto.InternalMessageInfo - -// SignBytes defines the signed bytes used for signature verification. -type SignBytes struct { - Sequence uint64 `protobuf:"varint,1,opt,name=sequence,proto3" json:"sequence,omitempty"` - Timestamp uint64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` - Diversifier string `protobuf:"bytes,3,opt,name=diversifier,proto3" json:"diversifier,omitempty"` - // type of the data used - DataType DataType `protobuf:"varint,4,opt,name=data_type,json=dataType,proto3,enum=ibc.lightclients.solomachine.v1.DataType" json:"data_type,omitempty" yaml:"data_type"` - // marshaled data - Data []byte `protobuf:"bytes,5,opt,name=data,proto3" json:"data,omitempty"` -} - -func (m *SignBytes) Reset() { *m = SignBytes{} } -func (m *SignBytes) String() string { return proto.CompactTextString(m) } -func (*SignBytes) ProtoMessage() {} -func (*SignBytes) Descriptor() ([]byte, []int) { - return fileDescriptor_6cc2ee18f7f86d4e, []int{6} -} -func (m *SignBytes) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *SignBytes) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_SignBytes.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 *SignBytes) XXX_Merge(src proto.Message) { - xxx_messageInfo_SignBytes.Merge(m, src) -} -func (m *SignBytes) XXX_Size() int { - return m.Size() -} -func (m *SignBytes) XXX_DiscardUnknown() { - xxx_messageInfo_SignBytes.DiscardUnknown(m) -} - -var xxx_messageInfo_SignBytes proto.InternalMessageInfo - -// HeaderData returns the SignBytes data for update verification. -type HeaderData struct { - // header public key - NewPubKey *types.Any `protobuf:"bytes,1,opt,name=new_pub_key,json=newPubKey,proto3" json:"new_pub_key,omitempty" yaml:"new_pub_key"` - // header diversifier - NewDiversifier string `protobuf:"bytes,2,opt,name=new_diversifier,json=newDiversifier,proto3" json:"new_diversifier,omitempty" yaml:"new_diversifier"` -} - -func (m *HeaderData) Reset() { *m = HeaderData{} } -func (m *HeaderData) String() string { return proto.CompactTextString(m) } -func (*HeaderData) ProtoMessage() {} -func (*HeaderData) Descriptor() ([]byte, []int) { - return fileDescriptor_6cc2ee18f7f86d4e, []int{7} -} -func (m *HeaderData) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *HeaderData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_HeaderData.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 *HeaderData) XXX_Merge(src proto.Message) { - xxx_messageInfo_HeaderData.Merge(m, src) -} -func (m *HeaderData) XXX_Size() int { - return m.Size() -} -func (m *HeaderData) XXX_DiscardUnknown() { - xxx_messageInfo_HeaderData.DiscardUnknown(m) -} - -var xxx_messageInfo_HeaderData proto.InternalMessageInfo - -// ClientStateData returns the SignBytes data for client state verification. -type ClientStateData struct { - Path []byte `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` - ClientState *types.Any `protobuf:"bytes,2,opt,name=client_state,json=clientState,proto3" json:"client_state,omitempty" yaml:"client_state"` -} - -func (m *ClientStateData) Reset() { *m = ClientStateData{} } -func (m *ClientStateData) String() string { return proto.CompactTextString(m) } -func (*ClientStateData) ProtoMessage() {} -func (*ClientStateData) Descriptor() ([]byte, []int) { - return fileDescriptor_6cc2ee18f7f86d4e, []int{8} -} -func (m *ClientStateData) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *ClientStateData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_ClientStateData.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 *ClientStateData) XXX_Merge(src proto.Message) { - xxx_messageInfo_ClientStateData.Merge(m, src) -} -func (m *ClientStateData) XXX_Size() int { - return m.Size() -} -func (m *ClientStateData) XXX_DiscardUnknown() { - xxx_messageInfo_ClientStateData.DiscardUnknown(m) -} - -var xxx_messageInfo_ClientStateData proto.InternalMessageInfo - -// ConsensusStateData returns the SignBytes data for consensus state -// verification. -type ConsensusStateData struct { - Path []byte `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` - ConsensusState *types.Any `protobuf:"bytes,2,opt,name=consensus_state,json=consensusState,proto3" json:"consensus_state,omitempty" yaml:"consensus_state"` -} - -func (m *ConsensusStateData) Reset() { *m = ConsensusStateData{} } -func (m *ConsensusStateData) String() string { return proto.CompactTextString(m) } -func (*ConsensusStateData) ProtoMessage() {} -func (*ConsensusStateData) Descriptor() ([]byte, []int) { - return fileDescriptor_6cc2ee18f7f86d4e, []int{9} -} -func (m *ConsensusStateData) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *ConsensusStateData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_ConsensusStateData.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 *ConsensusStateData) XXX_Merge(src proto.Message) { - xxx_messageInfo_ConsensusStateData.Merge(m, src) -} -func (m *ConsensusStateData) XXX_Size() int { - return m.Size() -} -func (m *ConsensusStateData) XXX_DiscardUnknown() { - xxx_messageInfo_ConsensusStateData.DiscardUnknown(m) -} - -var xxx_messageInfo_ConsensusStateData proto.InternalMessageInfo - -// ConnectionStateData returns the SignBytes data for connection state -// verification. -type ConnectionStateData struct { - Path []byte `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` - Connection *types1.ConnectionEnd `protobuf:"bytes,2,opt,name=connection,proto3" json:"connection,omitempty"` -} - -func (m *ConnectionStateData) Reset() { *m = ConnectionStateData{} } -func (m *ConnectionStateData) String() string { return proto.CompactTextString(m) } -func (*ConnectionStateData) ProtoMessage() {} -func (*ConnectionStateData) Descriptor() ([]byte, []int) { - return fileDescriptor_6cc2ee18f7f86d4e, []int{10} -} -func (m *ConnectionStateData) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *ConnectionStateData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_ConnectionStateData.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 *ConnectionStateData) XXX_Merge(src proto.Message) { - xxx_messageInfo_ConnectionStateData.Merge(m, src) -} -func (m *ConnectionStateData) XXX_Size() int { - return m.Size() -} -func (m *ConnectionStateData) XXX_DiscardUnknown() { - xxx_messageInfo_ConnectionStateData.DiscardUnknown(m) -} - -var xxx_messageInfo_ConnectionStateData proto.InternalMessageInfo - -// ChannelStateData returns the SignBytes data for channel state -// verification. -type ChannelStateData struct { - Path []byte `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` - Channel *types2.Channel `protobuf:"bytes,2,opt,name=channel,proto3" json:"channel,omitempty"` -} - -func (m *ChannelStateData) Reset() { *m = ChannelStateData{} } -func (m *ChannelStateData) String() string { return proto.CompactTextString(m) } -func (*ChannelStateData) ProtoMessage() {} -func (*ChannelStateData) Descriptor() ([]byte, []int) { - return fileDescriptor_6cc2ee18f7f86d4e, []int{11} -} -func (m *ChannelStateData) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *ChannelStateData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_ChannelStateData.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 *ChannelStateData) XXX_Merge(src proto.Message) { - xxx_messageInfo_ChannelStateData.Merge(m, src) -} -func (m *ChannelStateData) XXX_Size() int { - return m.Size() -} -func (m *ChannelStateData) XXX_DiscardUnknown() { - xxx_messageInfo_ChannelStateData.DiscardUnknown(m) -} - -var xxx_messageInfo_ChannelStateData proto.InternalMessageInfo - -// PacketCommitmentData returns the SignBytes data for packet commitment -// verification. -type PacketCommitmentData struct { - Path []byte `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` - Commitment []byte `protobuf:"bytes,2,opt,name=commitment,proto3" json:"commitment,omitempty"` -} - -func (m *PacketCommitmentData) Reset() { *m = PacketCommitmentData{} } -func (m *PacketCommitmentData) String() string { return proto.CompactTextString(m) } -func (*PacketCommitmentData) ProtoMessage() {} -func (*PacketCommitmentData) Descriptor() ([]byte, []int) { - return fileDescriptor_6cc2ee18f7f86d4e, []int{12} -} -func (m *PacketCommitmentData) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *PacketCommitmentData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_PacketCommitmentData.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 *PacketCommitmentData) XXX_Merge(src proto.Message) { - xxx_messageInfo_PacketCommitmentData.Merge(m, src) -} -func (m *PacketCommitmentData) XXX_Size() int { - return m.Size() -} -func (m *PacketCommitmentData) XXX_DiscardUnknown() { - xxx_messageInfo_PacketCommitmentData.DiscardUnknown(m) -} - -var xxx_messageInfo_PacketCommitmentData proto.InternalMessageInfo - -func (m *PacketCommitmentData) GetPath() []byte { - if m != nil { - return m.Path - } - return nil -} - -func (m *PacketCommitmentData) GetCommitment() []byte { - if m != nil { - return m.Commitment - } - return nil -} - -// PacketAcknowledgementData returns the SignBytes data for acknowledgement -// verification. -type PacketAcknowledgementData struct { - Path []byte `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` - Acknowledgement []byte `protobuf:"bytes,2,opt,name=acknowledgement,proto3" json:"acknowledgement,omitempty"` -} - -func (m *PacketAcknowledgementData) Reset() { *m = PacketAcknowledgementData{} } -func (m *PacketAcknowledgementData) String() string { return proto.CompactTextString(m) } -func (*PacketAcknowledgementData) ProtoMessage() {} -func (*PacketAcknowledgementData) Descriptor() ([]byte, []int) { - return fileDescriptor_6cc2ee18f7f86d4e, []int{13} -} -func (m *PacketAcknowledgementData) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *PacketAcknowledgementData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_PacketAcknowledgementData.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 *PacketAcknowledgementData) XXX_Merge(src proto.Message) { - xxx_messageInfo_PacketAcknowledgementData.Merge(m, src) -} -func (m *PacketAcknowledgementData) XXX_Size() int { - return m.Size() -} -func (m *PacketAcknowledgementData) XXX_DiscardUnknown() { - xxx_messageInfo_PacketAcknowledgementData.DiscardUnknown(m) -} - -var xxx_messageInfo_PacketAcknowledgementData proto.InternalMessageInfo - -func (m *PacketAcknowledgementData) GetPath() []byte { - if m != nil { - return m.Path - } - return nil -} - -func (m *PacketAcknowledgementData) GetAcknowledgement() []byte { - if m != nil { - return m.Acknowledgement - } - return nil -} - -// PacketReceiptAbsenceData returns the SignBytes data for -// packet receipt absence verification. -type PacketReceiptAbsenceData struct { - Path []byte `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` -} - -func (m *PacketReceiptAbsenceData) Reset() { *m = PacketReceiptAbsenceData{} } -func (m *PacketReceiptAbsenceData) String() string { return proto.CompactTextString(m) } -func (*PacketReceiptAbsenceData) ProtoMessage() {} -func (*PacketReceiptAbsenceData) Descriptor() ([]byte, []int) { - return fileDescriptor_6cc2ee18f7f86d4e, []int{14} -} -func (m *PacketReceiptAbsenceData) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *PacketReceiptAbsenceData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_PacketReceiptAbsenceData.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 *PacketReceiptAbsenceData) XXX_Merge(src proto.Message) { - xxx_messageInfo_PacketReceiptAbsenceData.Merge(m, src) -} -func (m *PacketReceiptAbsenceData) XXX_Size() int { - return m.Size() -} -func (m *PacketReceiptAbsenceData) XXX_DiscardUnknown() { - xxx_messageInfo_PacketReceiptAbsenceData.DiscardUnknown(m) -} - -var xxx_messageInfo_PacketReceiptAbsenceData proto.InternalMessageInfo - -func (m *PacketReceiptAbsenceData) GetPath() []byte { - if m != nil { - return m.Path - } - return nil -} - -// NextSequenceRecvData returns the SignBytes data for verification of the next -// sequence to be received. -type NextSequenceRecvData struct { - Path []byte `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` - NextSeqRecv uint64 `protobuf:"varint,2,opt,name=next_seq_recv,json=nextSeqRecv,proto3" json:"next_seq_recv,omitempty" yaml:"next_seq_recv"` -} - -func (m *NextSequenceRecvData) Reset() { *m = NextSequenceRecvData{} } -func (m *NextSequenceRecvData) String() string { return proto.CompactTextString(m) } -func (*NextSequenceRecvData) ProtoMessage() {} -func (*NextSequenceRecvData) Descriptor() ([]byte, []int) { - return fileDescriptor_6cc2ee18f7f86d4e, []int{15} -} -func (m *NextSequenceRecvData) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *NextSequenceRecvData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_NextSequenceRecvData.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 *NextSequenceRecvData) XXX_Merge(src proto.Message) { - xxx_messageInfo_NextSequenceRecvData.Merge(m, src) -} -func (m *NextSequenceRecvData) XXX_Size() int { - return m.Size() -} -func (m *NextSequenceRecvData) XXX_DiscardUnknown() { - xxx_messageInfo_NextSequenceRecvData.DiscardUnknown(m) -} - -var xxx_messageInfo_NextSequenceRecvData proto.InternalMessageInfo - -func (m *NextSequenceRecvData) GetPath() []byte { - if m != nil { - return m.Path - } - return nil -} - -func (m *NextSequenceRecvData) GetNextSeqRecv() uint64 { - if m != nil { - return m.NextSeqRecv - } - return 0 -} - -func init() { - proto.RegisterEnum("ibc.lightclients.solomachine.v1.DataType", DataType_name, DataType_value) - proto.RegisterType((*ClientState)(nil), "ibc.lightclients.solomachine.v1.ClientState") - proto.RegisterType((*ConsensusState)(nil), "ibc.lightclients.solomachine.v1.ConsensusState") - proto.RegisterType((*Header)(nil), "ibc.lightclients.solomachine.v1.Header") - proto.RegisterType((*Misbehaviour)(nil), "ibc.lightclients.solomachine.v1.Misbehaviour") - proto.RegisterType((*SignatureAndData)(nil), "ibc.lightclients.solomachine.v1.SignatureAndData") - proto.RegisterType((*TimestampedSignatureData)(nil), "ibc.lightclients.solomachine.v1.TimestampedSignatureData") - proto.RegisterType((*SignBytes)(nil), "ibc.lightclients.solomachine.v1.SignBytes") - proto.RegisterType((*HeaderData)(nil), "ibc.lightclients.solomachine.v1.HeaderData") - proto.RegisterType((*ClientStateData)(nil), "ibc.lightclients.solomachine.v1.ClientStateData") - proto.RegisterType((*ConsensusStateData)(nil), "ibc.lightclients.solomachine.v1.ConsensusStateData") - proto.RegisterType((*ConnectionStateData)(nil), "ibc.lightclients.solomachine.v1.ConnectionStateData") - proto.RegisterType((*ChannelStateData)(nil), "ibc.lightclients.solomachine.v1.ChannelStateData") - proto.RegisterType((*PacketCommitmentData)(nil), "ibc.lightclients.solomachine.v1.PacketCommitmentData") - proto.RegisterType((*PacketAcknowledgementData)(nil), "ibc.lightclients.solomachine.v1.PacketAcknowledgementData") - proto.RegisterType((*PacketReceiptAbsenceData)(nil), "ibc.lightclients.solomachine.v1.PacketReceiptAbsenceData") - proto.RegisterType((*NextSequenceRecvData)(nil), "ibc.lightclients.solomachine.v1.NextSequenceRecvData") -} - -func init() { - proto.RegisterFile("ibc/lightclients/solomachine/v1/solomachine.proto", fileDescriptor_6cc2ee18f7f86d4e) -} - -var fileDescriptor_6cc2ee18f7f86d4e = []byte{ - // 1369 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x57, 0x5f, 0x6f, 0xdb, 0x54, - 0x14, 0xaf, 0xb3, 0xac, 0x6b, 0x4e, 0xba, 0x36, 0x78, 0xd9, 0x96, 0x7a, 0x53, 0x62, 0x8c, 0x18, - 0x05, 0xb1, 0x78, 0x2d, 0x30, 0xa1, 0x09, 0x0d, 0x1c, 0xc7, 0xb0, 0x6c, 0xad, 0x1b, 0x1c, 0x17, - 0xb6, 0x09, 0xc9, 0x72, 0x9c, 0xdb, 0xd4, 0x5a, 0xe2, 0x1b, 0x62, 0x27, 0x5d, 0x90, 0x90, 0x10, - 0x4f, 0x23, 0xe2, 0x81, 0x2f, 0x10, 0x09, 0x81, 0xf8, 0x2a, 0xc0, 0xe3, 0x78, 0xe3, 0x29, 0xa0, - 0xed, 0x1b, 0xe4, 0x13, 0x20, 0xfb, 0xde, 0xc4, 0x76, 0xba, 0xa6, 0xe2, 0xdf, 0xdb, 0xbd, 0xe7, - 0xfc, 0xce, 0xef, 0x9c, 0x7b, 0xce, 0xf1, 0xb9, 0xd7, 0xb0, 0x65, 0xd7, 0x2d, 0xb1, 0x65, 0x37, - 0x0f, 0x3d, 0xab, 0x65, 0x23, 0xc7, 0x73, 0x45, 0x17, 0xb7, 0x70, 0xdb, 0xb4, 0x0e, 0x6d, 0x07, - 0x89, 0xfd, 0xad, 0xe8, 0xb6, 0xd8, 0xe9, 0x62, 0x0f, 0xb3, 0x05, 0xbb, 0x6e, 0x15, 0xa3, 0x26, - 0xc5, 0x28, 0xa6, 0xbf, 0xc5, 0xbd, 0xe6, 0x73, 0x5a, 0xb8, 0x8b, 0x44, 0x0b, 0x3b, 0x0e, 0xb2, - 0x3c, 0x1b, 0x3b, 0x3e, 0x55, 0xb8, 0x23, 0x4c, 0xdc, 0xcb, 0x21, 0xf0, 0xd0, 0x74, 0x1c, 0xd4, - 0x0a, 0x50, 0x64, 0x49, 0x21, 0xd9, 0x26, 0x6e, 0xe2, 0x60, 0x29, 0xfa, 0x2b, 0x2a, 0xdd, 0x68, - 0x62, 0xdc, 0x6c, 0x21, 0x31, 0xd8, 0xd5, 0x7b, 0x07, 0xa2, 0xe9, 0x0c, 0x88, 0x4a, 0xf8, 0x2d, - 0x01, 0x69, 0x39, 0x88, 0xab, 0xe6, 0x99, 0x1e, 0x62, 0x39, 0x58, 0x71, 0xd1, 0xe7, 0x3d, 0xe4, - 0x58, 0x28, 0xc7, 0xf0, 0xcc, 0x66, 0x52, 0x9b, 0xed, 0x59, 0x19, 0xd6, 0x0f, 0xba, 0xf8, 0x0b, - 0xe4, 0x18, 0x33, 0x48, 0xc2, 0x87, 0x94, 0xb8, 0xc9, 0xb8, 0x70, 0x69, 0x60, 0xb6, 0x5b, 0xb7, - 0x84, 0x39, 0x80, 0xa0, 0xad, 0x11, 0x49, 0x6d, 0x4a, 0xe2, 0xc1, 0xba, 0x85, 0x1d, 0x17, 0x39, - 0x6e, 0xcf, 0x35, 0x5c, 0xdf, 0x67, 0xee, 0x0c, 0xcf, 0x6c, 0xa6, 0xb7, 0xc5, 0xe2, 0x29, 0x89, - 0x2a, 0xca, 0x53, 0xbb, 0x20, 0xd4, 0xa8, 0xd7, 0x39, 0x46, 0x41, 0x5b, 0xb3, 0x62, 0x58, 0x16, - 0xc1, 0x15, 0xb3, 0xd5, 0xc2, 0x47, 0x46, 0xaf, 0xd3, 0x30, 0x3d, 0x64, 0x98, 0x07, 0x1e, 0xea, - 0x1a, 0x9d, 0x2e, 0xee, 0x60, 0xd7, 0x6c, 0xe5, 0x92, 0x3c, 0xb3, 0xb9, 0x52, 0xba, 0x36, 0x19, - 0x17, 0x04, 0x42, 0xb8, 0x00, 0x2c, 0x68, 0xb9, 0x40, 0xbb, 0x1f, 0x28, 0x25, 0x5f, 0x57, 0xa5, - 0xaa, 0x5b, 0xc9, 0x27, 0xdf, 0x17, 0x96, 0x84, 0x1f, 0x18, 0x58, 0x8b, 0xc7, 0xca, 0xde, 0x05, - 0xe8, 0xf4, 0xea, 0x2d, 0xdb, 0x32, 0x1e, 0xa1, 0x41, 0x90, 0xd8, 0xf4, 0x76, 0xb6, 0x48, 0xca, - 0x52, 0x9c, 0x96, 0xa5, 0x28, 0x39, 0x83, 0xd2, 0xc5, 0xc9, 0xb8, 0xf0, 0x12, 0x09, 0x22, 0xb4, - 0x10, 0xb4, 0x14, 0xd9, 0xdc, 0x43, 0x03, 0x96, 0x87, 0x74, 0xc3, 0xee, 0xa3, 0xae, 0x6b, 0x1f, - 0xd8, 0xa8, 0x1b, 0x94, 0x20, 0xa5, 0x45, 0x45, 0xec, 0x55, 0x48, 0x79, 0x76, 0x1b, 0xb9, 0x9e, - 0xd9, 0xee, 0x04, 0xd9, 0x4d, 0x6a, 0xa1, 0x80, 0x06, 0xf9, 0x75, 0x02, 0x96, 0xef, 0x20, 0xb3, - 0x81, 0xba, 0x0b, 0x6b, 0x1e, 0xa3, 0x4a, 0xcc, 0x51, 0xf9, 0x5a, 0xd7, 0x6e, 0x3a, 0xa6, 0xd7, - 0xeb, 0x92, 0x32, 0xae, 0x6a, 0xa1, 0x80, 0xdd, 0x87, 0x35, 0x07, 0x1d, 0x19, 0x91, 0x83, 0x27, - 0x17, 0x1c, 0x7c, 0x63, 0x32, 0x2e, 0x5c, 0x24, 0x07, 0x8f, 0x5b, 0x09, 0xda, 0xaa, 0x83, 0x8e, - 0xaa, 0xb3, 0xf3, 0xcb, 0xb0, 0xee, 0x03, 0xa2, 0x39, 0x38, 0xeb, 0xe7, 0x20, 0xda, 0x10, 0x73, - 0x00, 0x41, 0xf3, 0x23, 0x29, 0x87, 0x02, 0x9a, 0x84, 0x5f, 0x12, 0xb0, 0xba, 0x6b, 0xbb, 0x75, - 0x74, 0x68, 0xf6, 0x6d, 0xdc, 0xeb, 0xb2, 0x5b, 0x90, 0x22, 0xcd, 0x67, 0xd8, 0x8d, 0x20, 0x17, - 0xa9, 0x52, 0x76, 0x32, 0x2e, 0x64, 0x68, 0x9b, 0x4d, 0x55, 0x82, 0xb6, 0x42, 0xd6, 0x95, 0x46, - 0x2c, 0x7b, 0x89, 0xb9, 0xec, 0x75, 0xe0, 0xfc, 0x2c, 0x1d, 0x06, 0x76, 0xa6, 0xad, 0xbe, 0x75, - 0x6a, 0xab, 0xd7, 0xa6, 0x56, 0x92, 0xd3, 0x28, 0x9b, 0x9e, 0x59, 0xca, 0x4d, 0xc6, 0x85, 0x2c, - 0x89, 0x22, 0xc6, 0x28, 0x68, 0xab, 0xb3, 0xfd, 0x9e, 0x33, 0xe7, 0xd1, 0x3b, 0xc2, 0x34, 0xe5, - 0xff, 0x95, 0x47, 0xef, 0x08, 0x47, 0x3d, 0xea, 0x47, 0x98, 0x66, 0xf2, 0x67, 0x06, 0x32, 0xf3, - 0x14, 0xf1, 0xf6, 0x60, 0xe6, 0xdb, 0xe3, 0x33, 0x48, 0x35, 0x4c, 0xcf, 0x34, 0xbc, 0x41, 0x87, - 0x64, 0x6e, 0x6d, 0xfb, 0xf5, 0x53, 0xc3, 0xf4, 0x79, 0xf5, 0x41, 0x07, 0x45, 0xcb, 0x32, 0x63, - 0x11, 0xb4, 0x95, 0x06, 0xd5, 0xb3, 0x2c, 0x24, 0xfd, 0x35, 0xed, 0xca, 0x60, 0x1d, 0x6f, 0xe6, - 0xe4, 0x8b, 0xbf, 0x8b, 0xaf, 0x18, 0xc8, 0xe9, 0x53, 0x19, 0x6a, 0xcc, 0xce, 0x14, 0x1c, 0xe8, - 0x03, 0x58, 0x0b, 0x73, 0x11, 0xd0, 0x07, 0xa7, 0x8a, 0xf6, 0x6e, 0x5c, 0x2f, 0x68, 0x61, 0x39, - 0xca, 0xc7, 0x42, 0x48, 0xbc, 0x38, 0x84, 0x3f, 0x18, 0x48, 0xf9, 0x7e, 0x4b, 0x03, 0x0f, 0xb9, - 0xff, 0xe2, 0xeb, 0x9c, 0x1b, 0x14, 0x67, 0x8e, 0x0f, 0x8a, 0x58, 0x09, 0x92, 0xff, 0x57, 0x09, - 0xce, 0x86, 0x25, 0xa0, 0x27, 0xfc, 0x89, 0x01, 0x20, 0xc3, 0x27, 0x48, 0xca, 0x0e, 0xa4, 0xe9, - 0x27, 0x7f, 0xea, 0x78, 0xbc, 0x34, 0x19, 0x17, 0xd8, 0xd8, 0x94, 0xa0, 0xf3, 0x91, 0x8c, 0x88, - 0x13, 0xe6, 0x43, 0xe2, 0x1f, 0xce, 0x87, 0x2f, 0x61, 0x3d, 0x72, 0x39, 0x06, 0xb1, 0xb2, 0x90, - 0xec, 0x98, 0xde, 0x21, 0x6d, 0xe7, 0x60, 0xcd, 0x56, 0x61, 0x95, 0x8e, 0x06, 0x72, 0xa1, 0x25, - 0x16, 0x1c, 0xe0, 0xf2, 0x64, 0x5c, 0xb8, 0x10, 0x1b, 0x27, 0xf4, 0xca, 0x4a, 0x5b, 0xa1, 0x27, - 0xea, 0xfe, 0x1b, 0x06, 0xd8, 0xf8, 0x45, 0x72, 0x62, 0x08, 0x0f, 0x8e, 0x5f, 0xab, 0x8b, 0xa2, - 0xf8, 0x1b, 0x77, 0x27, 0x8d, 0xa5, 0x0f, 0x17, 0xe4, 0xd9, 0x83, 0x64, 0x71, 0x2c, 0x0a, 0x40, - 0xf8, 0x76, 0xa1, 0x61, 0xbc, 0x1a, 0xb4, 0x95, 0xff, 0x78, 0x29, 0x46, 0xde, 0x35, 0xe4, 0x52, - 0xa7, 0x3b, 0xc5, 0x69, 0x68, 0x11, 0x43, 0xea, 0xb7, 0x01, 0x19, 0x99, 0x3c, 0x71, 0x16, 0x3b, - 0xbd, 0x09, 0xe7, 0xe8, 0x53, 0x88, 0x7a, 0xbc, 0x1a, 0xf1, 0x48, 0xdf, 0x48, 0xbe, 0x3b, 0xb2, - 0xd4, 0xa6, 0x60, 0xea, 0xe5, 0x2e, 0x64, 0xab, 0xa6, 0xf5, 0x08, 0x79, 0x32, 0x6e, 0xb7, 0x6d, - 0xaf, 0x8d, 0x1c, 0xef, 0x44, 0x4f, 0x79, 0xff, 0x78, 0x53, 0x54, 0xe0, 0x6c, 0x55, 0x8b, 0x48, - 0x84, 0x07, 0xb0, 0x41, 0xb8, 0x24, 0xeb, 0x91, 0x83, 0x8f, 0x5a, 0xa8, 0xd1, 0x44, 0x0b, 0x09, - 0x37, 0x61, 0xdd, 0x8c, 0x43, 0x29, 0xeb, 0xbc, 0x58, 0x28, 0x42, 0x8e, 0x50, 0x6b, 0xc8, 0x42, - 0x76, 0xc7, 0x93, 0xea, 0xae, 0x3f, 0x07, 0x4e, 0x62, 0x16, 0x0e, 0x21, 0xab, 0xa2, 0xc7, 0xde, - 0xf4, 0xf1, 0xa5, 0x21, 0xab, 0x7f, 0x62, 0x14, 0xef, 0xc1, 0x79, 0x07, 0x3d, 0xf6, 0xfc, 0xa7, - 0x9b, 0xd1, 0x45, 0x56, 0x9f, 0xbe, 0xed, 0x22, 0xd7, 0x40, 0x4c, 0x2d, 0x68, 0x69, 0x87, 0x50, - 0xfb, 0xac, 0x6f, 0x7c, 0x9b, 0x84, 0x95, 0xe9, 0x60, 0x60, 0xdf, 0x85, 0x57, 0xca, 0x92, 0x2e, - 0x19, 0xfa, 0x83, 0xaa, 0x62, 0xec, 0xab, 0x15, 0xb5, 0xa2, 0x57, 0xa4, 0x9d, 0xca, 0x43, 0xa5, - 0x6c, 0xec, 0xab, 0xb5, 0xaa, 0x22, 0x57, 0x3e, 0xac, 0x28, 0xe5, 0xcc, 0x12, 0xb7, 0x3e, 0x1c, - 0xf1, 0xe9, 0x88, 0x88, 0xbd, 0x06, 0x97, 0x42, 0x4b, 0x79, 0xa7, 0xa2, 0xa8, 0xba, 0x51, 0xd3, - 0x25, 0x5d, 0xc9, 0x30, 0x1c, 0x0c, 0x47, 0xfc, 0x32, 0x91, 0xb1, 0x6f, 0xc2, 0x46, 0x04, 0xb7, - 0xa7, 0xd6, 0x14, 0xb5, 0xb6, 0x5f, 0xa3, 0xd0, 0x04, 0x77, 0x7e, 0x38, 0xe2, 0x53, 0x33, 0x31, - 0x5b, 0x04, 0x2e, 0x86, 0x56, 0x15, 0x59, 0xaf, 0xec, 0xa9, 0x14, 0x7e, 0x86, 0x5b, 0x1b, 0x8e, - 0x78, 0x08, 0xe5, 0xec, 0x26, 0x5c, 0x8e, 0xe0, 0xef, 0x48, 0xaa, 0xaa, 0xec, 0x50, 0x70, 0x92, - 0x4b, 0x0f, 0x47, 0xfc, 0x39, 0x2a, 0x64, 0xdf, 0x81, 0x2b, 0x21, 0xb2, 0x2a, 0xc9, 0xf7, 0x14, - 0xdd, 0x90, 0xf7, 0x76, 0x77, 0x2b, 0xfa, 0xae, 0xa2, 0xea, 0x99, 0xb3, 0x5c, 0x76, 0x38, 0xe2, - 0x33, 0x44, 0x11, 0xca, 0xd9, 0xf7, 0x81, 0x3f, 0x66, 0x26, 0xc9, 0xf7, 0xd4, 0xbd, 0x4f, 0x77, - 0x94, 0xf2, 0x47, 0x4a, 0x60, 0xbb, 0xcc, 0x6d, 0x0c, 0x47, 0xfc, 0x45, 0xa2, 0x9d, 0x53, 0xb2, - 0xb7, 0x5f, 0x40, 0xa0, 0x29, 0xb2, 0x52, 0xa9, 0xea, 0x86, 0x54, 0xaa, 0x29, 0xaa, 0xac, 0x64, - 0xce, 0x71, 0xb9, 0xe1, 0x88, 0xcf, 0x12, 0x2d, 0x55, 0x52, 0x1d, 0x7b, 0x13, 0xae, 0x86, 0xf6, - 0xaa, 0x72, 0x5f, 0x37, 0x6a, 0xca, 0xc7, 0xfb, 0xbe, 0xca, 0xa7, 0xf9, 0x24, 0xb3, 0x42, 0x02, - 0xf7, 0x35, 0x53, 0x85, 0x2f, 0x67, 0x79, 0xc8, 0x84, 0x76, 0x77, 0x14, 0xa9, 0xac, 0x68, 0x99, - 0x14, 0xa9, 0x0c, 0xd9, 0x71, 0xc9, 0x27, 0x3f, 0xe6, 0x97, 0x4a, 0xf7, 0x7f, 0x7d, 0x96, 0x67, - 0x9e, 0x3e, 0xcb, 0x33, 0x7f, 0x3e, 0xcb, 0x33, 0xdf, 0x3d, 0xcf, 0x2f, 0x3d, 0x7d, 0x9e, 0x5f, - 0xfa, 0xfd, 0x79, 0x7e, 0xe9, 0xe1, 0xed, 0xa6, 0xed, 0x1d, 0xf6, 0xea, 0x45, 0x0b, 0xb7, 0x45, - 0x0b, 0xbb, 0x6d, 0xec, 0x8a, 0x76, 0xdd, 0xba, 0xde, 0xc4, 0x62, 0xff, 0x6d, 0xb1, 0x8d, 0x1b, - 0xbd, 0x16, 0x72, 0xc9, 0x4f, 0xce, 0x8d, 0xed, 0xeb, 0x64, 0x24, 0x8a, 0x2d, 0xd4, 0x34, 0xad, - 0x81, 0xd8, 0xdf, 0xba, 0x71, 0xa3, 0xbe, 0x1c, 0xcc, 0xb1, 0xb7, 0xfe, 0x0a, 0x00, 0x00, 0xff, - 0xff, 0x66, 0x80, 0x87, 0xc6, 0x8a, 0x0d, 0x00, 0x00, -} - -func (m *ClientState) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *ClientState) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *ClientState) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if m.AllowUpdateAfterProposal { - i-- - if m.AllowUpdateAfterProposal { - dAtA[i] = 1 - } else { - dAtA[i] = 0 - } - i-- - dAtA[i] = 0x20 - } - if m.ConsensusState != nil { - { - size, err := m.ConsensusState.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintSolomachine(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x1a - } - if m.FrozenSequence != 0 { - i = encodeVarintSolomachine(dAtA, i, uint64(m.FrozenSequence)) - i-- - dAtA[i] = 0x10 - } - if m.Sequence != 0 { - i = encodeVarintSolomachine(dAtA, i, uint64(m.Sequence)) - i-- - dAtA[i] = 0x8 - } - return len(dAtA) - i, nil -} - -func (m *ConsensusState) 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 *ConsensusState) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *ConsensusState) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if m.Timestamp != 0 { - i = encodeVarintSolomachine(dAtA, i, uint64(m.Timestamp)) - i-- - dAtA[i] = 0x18 - } - if len(m.Diversifier) > 0 { - i -= len(m.Diversifier) - copy(dAtA[i:], m.Diversifier) - i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Diversifier))) - i-- - dAtA[i] = 0x12 - } - if m.PublicKey != nil { - { - size, err := m.PublicKey.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintSolomachine(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - -func (m *Header) 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 *Header) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *Header) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if len(m.NewDiversifier) > 0 { - i -= len(m.NewDiversifier) - copy(dAtA[i:], m.NewDiversifier) - i = encodeVarintSolomachine(dAtA, i, uint64(len(m.NewDiversifier))) - i-- - dAtA[i] = 0x2a - } - if m.NewPublicKey != nil { - { - size, err := m.NewPublicKey.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintSolomachine(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x22 - } - if len(m.Signature) > 0 { - i -= len(m.Signature) - copy(dAtA[i:], m.Signature) - i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Signature))) - i-- - dAtA[i] = 0x1a - } - if m.Timestamp != 0 { - i = encodeVarintSolomachine(dAtA, i, uint64(m.Timestamp)) - i-- - dAtA[i] = 0x10 - } - if m.Sequence != 0 { - i = encodeVarintSolomachine(dAtA, i, uint64(m.Sequence)) - i-- - dAtA[i] = 0x8 - } - return len(dAtA) - i, nil -} - -func (m *Misbehaviour) 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 *Misbehaviour) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *Misbehaviour) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if m.SignatureTwo != nil { - { - size, err := m.SignatureTwo.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintSolomachine(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x22 - } - if m.SignatureOne != nil { - { - size, err := m.SignatureOne.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintSolomachine(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x1a - } - if m.Sequence != 0 { - i = encodeVarintSolomachine(dAtA, i, uint64(m.Sequence)) - i-- - dAtA[i] = 0x10 - } - if len(m.ClientId) > 0 { - i -= len(m.ClientId) - copy(dAtA[i:], m.ClientId) - i = encodeVarintSolomachine(dAtA, i, uint64(len(m.ClientId))) - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - -func (m *SignatureAndData) 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 *SignatureAndData) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *SignatureAndData) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if m.Timestamp != 0 { - i = encodeVarintSolomachine(dAtA, i, uint64(m.Timestamp)) - i-- - dAtA[i] = 0x20 - } - if len(m.Data) > 0 { - i -= len(m.Data) - copy(dAtA[i:], m.Data) - i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Data))) - i-- - dAtA[i] = 0x1a - } - if m.DataType != 0 { - i = encodeVarintSolomachine(dAtA, i, uint64(m.DataType)) - i-- - dAtA[i] = 0x10 - } - if len(m.Signature) > 0 { - i -= len(m.Signature) - copy(dAtA[i:], m.Signature) - i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Signature))) - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - -func (m *TimestampedSignatureData) 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 *TimestampedSignatureData) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *TimestampedSignatureData) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if m.Timestamp != 0 { - i = encodeVarintSolomachine(dAtA, i, uint64(m.Timestamp)) - i-- - dAtA[i] = 0x10 - } - if len(m.SignatureData) > 0 { - i -= len(m.SignatureData) - copy(dAtA[i:], m.SignatureData) - i = encodeVarintSolomachine(dAtA, i, uint64(len(m.SignatureData))) - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - -func (m *SignBytes) 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 *SignBytes) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *SignBytes) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if len(m.Data) > 0 { - i -= len(m.Data) - copy(dAtA[i:], m.Data) - i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Data))) - i-- - dAtA[i] = 0x2a - } - if m.DataType != 0 { - i = encodeVarintSolomachine(dAtA, i, uint64(m.DataType)) - i-- - dAtA[i] = 0x20 - } - if len(m.Diversifier) > 0 { - i -= len(m.Diversifier) - copy(dAtA[i:], m.Diversifier) - i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Diversifier))) - i-- - dAtA[i] = 0x1a - } - if m.Timestamp != 0 { - i = encodeVarintSolomachine(dAtA, i, uint64(m.Timestamp)) - i-- - dAtA[i] = 0x10 - } - if m.Sequence != 0 { - i = encodeVarintSolomachine(dAtA, i, uint64(m.Sequence)) - i-- - dAtA[i] = 0x8 - } - return len(dAtA) - i, nil -} - -func (m *HeaderData) 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 *HeaderData) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *HeaderData) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if len(m.NewDiversifier) > 0 { - i -= len(m.NewDiversifier) - copy(dAtA[i:], m.NewDiversifier) - i = encodeVarintSolomachine(dAtA, i, uint64(len(m.NewDiversifier))) - i-- - dAtA[i] = 0x12 - } - if m.NewPubKey != nil { - { - size, err := m.NewPubKey.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintSolomachine(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - -func (m *ClientStateData) 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 *ClientStateData) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *ClientStateData) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if m.ClientState != nil { - { - size, err := m.ClientState.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintSolomachine(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x12 - } - if len(m.Path) > 0 { - i -= len(m.Path) - copy(dAtA[i:], m.Path) - i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Path))) - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - -func (m *ConsensusStateData) 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 *ConsensusStateData) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *ConsensusStateData) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if m.ConsensusState != nil { - { - size, err := m.ConsensusState.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintSolomachine(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x12 - } - if len(m.Path) > 0 { - i -= len(m.Path) - copy(dAtA[i:], m.Path) - i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Path))) - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - -func (m *ConnectionStateData) 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 *ConnectionStateData) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *ConnectionStateData) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if m.Connection != nil { - { - size, err := m.Connection.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintSolomachine(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x12 - } - if len(m.Path) > 0 { - i -= len(m.Path) - copy(dAtA[i:], m.Path) - i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Path))) - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - -func (m *ChannelStateData) 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 *ChannelStateData) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *ChannelStateData) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if m.Channel != nil { - { - size, err := m.Channel.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintSolomachine(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x12 - } - if len(m.Path) > 0 { - i -= len(m.Path) - copy(dAtA[i:], m.Path) - i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Path))) - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - -func (m *PacketCommitmentData) 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 *PacketCommitmentData) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *PacketCommitmentData) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if len(m.Commitment) > 0 { - i -= len(m.Commitment) - copy(dAtA[i:], m.Commitment) - i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Commitment))) - i-- - dAtA[i] = 0x12 - } - if len(m.Path) > 0 { - i -= len(m.Path) - copy(dAtA[i:], m.Path) - i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Path))) - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - -func (m *PacketAcknowledgementData) 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 *PacketAcknowledgementData) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *PacketAcknowledgementData) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if len(m.Acknowledgement) > 0 { - i -= len(m.Acknowledgement) - copy(dAtA[i:], m.Acknowledgement) - i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Acknowledgement))) - i-- - dAtA[i] = 0x12 - } - if len(m.Path) > 0 { - i -= len(m.Path) - copy(dAtA[i:], m.Path) - i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Path))) - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - -func (m *PacketReceiptAbsenceData) 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 *PacketReceiptAbsenceData) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *PacketReceiptAbsenceData) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if len(m.Path) > 0 { - i -= len(m.Path) - copy(dAtA[i:], m.Path) - i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Path))) - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - -func (m *NextSequenceRecvData) 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 *NextSequenceRecvData) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *NextSequenceRecvData) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if m.NextSeqRecv != 0 { - i = encodeVarintSolomachine(dAtA, i, uint64(m.NextSeqRecv)) - i-- - dAtA[i] = 0x10 - } - if len(m.Path) > 0 { - i -= len(m.Path) - copy(dAtA[i:], m.Path) - i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Path))) - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - -func encodeVarintSolomachine(dAtA []byte, offset int, v uint64) int { - offset -= sovSolomachine(v) - base := offset - for v >= 1<<7 { - dAtA[offset] = uint8(v&0x7f | 0x80) - v >>= 7 - offset++ - } - dAtA[offset] = uint8(v) - return base -} -func (m *ClientState) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.Sequence != 0 { - n += 1 + sovSolomachine(uint64(m.Sequence)) - } - if m.FrozenSequence != 0 { - n += 1 + sovSolomachine(uint64(m.FrozenSequence)) - } - if m.ConsensusState != nil { - l = m.ConsensusState.Size() - n += 1 + l + sovSolomachine(uint64(l)) - } - if m.AllowUpdateAfterProposal { - n += 2 - } - return n -} - -func (m *ConsensusState) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.PublicKey != nil { - l = m.PublicKey.Size() - n += 1 + l + sovSolomachine(uint64(l)) - } - l = len(m.Diversifier) - if l > 0 { - n += 1 + l + sovSolomachine(uint64(l)) - } - if m.Timestamp != 0 { - n += 1 + sovSolomachine(uint64(m.Timestamp)) - } - return n -} - -func (m *Header) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.Sequence != 0 { - n += 1 + sovSolomachine(uint64(m.Sequence)) - } - if m.Timestamp != 0 { - n += 1 + sovSolomachine(uint64(m.Timestamp)) - } - l = len(m.Signature) - if l > 0 { - n += 1 + l + sovSolomachine(uint64(l)) - } - if m.NewPublicKey != nil { - l = m.NewPublicKey.Size() - n += 1 + l + sovSolomachine(uint64(l)) - } - l = len(m.NewDiversifier) - if l > 0 { - n += 1 + l + sovSolomachine(uint64(l)) - } - return n -} - -func (m *Misbehaviour) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.ClientId) - if l > 0 { - n += 1 + l + sovSolomachine(uint64(l)) - } - if m.Sequence != 0 { - n += 1 + sovSolomachine(uint64(m.Sequence)) - } - if m.SignatureOne != nil { - l = m.SignatureOne.Size() - n += 1 + l + sovSolomachine(uint64(l)) - } - if m.SignatureTwo != nil { - l = m.SignatureTwo.Size() - n += 1 + l + sovSolomachine(uint64(l)) - } - return n -} - -func (m *SignatureAndData) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Signature) - if l > 0 { - n += 1 + l + sovSolomachine(uint64(l)) - } - if m.DataType != 0 { - n += 1 + sovSolomachine(uint64(m.DataType)) - } - l = len(m.Data) - if l > 0 { - n += 1 + l + sovSolomachine(uint64(l)) - } - if m.Timestamp != 0 { - n += 1 + sovSolomachine(uint64(m.Timestamp)) - } - return n -} - -func (m *TimestampedSignatureData) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.SignatureData) - if l > 0 { - n += 1 + l + sovSolomachine(uint64(l)) - } - if m.Timestamp != 0 { - n += 1 + sovSolomachine(uint64(m.Timestamp)) - } - return n -} - -func (m *SignBytes) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.Sequence != 0 { - n += 1 + sovSolomachine(uint64(m.Sequence)) - } - if m.Timestamp != 0 { - n += 1 + sovSolomachine(uint64(m.Timestamp)) - } - l = len(m.Diversifier) - if l > 0 { - n += 1 + l + sovSolomachine(uint64(l)) - } - if m.DataType != 0 { - n += 1 + sovSolomachine(uint64(m.DataType)) - } - l = len(m.Data) - if l > 0 { - n += 1 + l + sovSolomachine(uint64(l)) - } - return n -} - -func (m *HeaderData) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.NewPubKey != nil { - l = m.NewPubKey.Size() - n += 1 + l + sovSolomachine(uint64(l)) - } - l = len(m.NewDiversifier) - if l > 0 { - n += 1 + l + sovSolomachine(uint64(l)) - } - return n -} - -func (m *ClientStateData) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Path) - if l > 0 { - n += 1 + l + sovSolomachine(uint64(l)) - } - if m.ClientState != nil { - l = m.ClientState.Size() - n += 1 + l + sovSolomachine(uint64(l)) - } - return n -} - -func (m *ConsensusStateData) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Path) - if l > 0 { - n += 1 + l + sovSolomachine(uint64(l)) - } - if m.ConsensusState != nil { - l = m.ConsensusState.Size() - n += 1 + l + sovSolomachine(uint64(l)) - } - return n -} - -func (m *ConnectionStateData) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Path) - if l > 0 { - n += 1 + l + sovSolomachine(uint64(l)) - } - if m.Connection != nil { - l = m.Connection.Size() - n += 1 + l + sovSolomachine(uint64(l)) - } - return n -} - -func (m *ChannelStateData) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Path) - if l > 0 { - n += 1 + l + sovSolomachine(uint64(l)) - } - if m.Channel != nil { - l = m.Channel.Size() - n += 1 + l + sovSolomachine(uint64(l)) - } - return n -} - -func (m *PacketCommitmentData) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Path) - if l > 0 { - n += 1 + l + sovSolomachine(uint64(l)) - } - l = len(m.Commitment) - if l > 0 { - n += 1 + l + sovSolomachine(uint64(l)) - } - return n -} - -func (m *PacketAcknowledgementData) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Path) - if l > 0 { - n += 1 + l + sovSolomachine(uint64(l)) - } - l = len(m.Acknowledgement) - if l > 0 { - n += 1 + l + sovSolomachine(uint64(l)) - } - return n -} - -func (m *PacketReceiptAbsenceData) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Path) - if l > 0 { - n += 1 + l + sovSolomachine(uint64(l)) - } - return n -} - -func (m *NextSequenceRecvData) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Path) - if l > 0 { - n += 1 + l + sovSolomachine(uint64(l)) - } - if m.NextSeqRecv != 0 { - n += 1 + sovSolomachine(uint64(m.NextSeqRecv)) - } - return n -} - -func sovSolomachine(x uint64) (n int) { - return (math_bits.Len64(x|1) + 6) / 7 -} -func sozSolomachine(x uint64) (n int) { - return sovSolomachine(uint64((x << 1) ^ uint64((int64(x) >> 63)))) -} -func (m *ClientState) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: ClientState: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: ClientState: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 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 ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Sequence |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 2: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field FrozenSequence", wireType) - } - m.FrozenSequence = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.FrozenSequence |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ConsensusState", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthSolomachine - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.ConsensusState == nil { - m.ConsensusState = &ConsensusState{} - } - if err := m.ConsensusState.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 4: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field AllowUpdateAfterProposal", wireType) - } - var v int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - v |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - m.AllowUpdateAfterProposal = bool(v != 0) - default: - iNdEx = preIndex - skippy, err := skipSolomachine(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthSolomachine - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *ConsensusState) 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 ErrIntOverflowSolomachine - } - 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: ConsensusState: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: ConsensusState: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field PublicKey", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthSolomachine - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.PublicKey == nil { - m.PublicKey = &types.Any{} - } - if err := m.PublicKey.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Diversifier", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - 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 ErrInvalidLengthSolomachine - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Diversifier = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 3: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType) - } - m.Timestamp = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Timestamp |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - default: - iNdEx = preIndex - skippy, err := skipSolomachine(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthSolomachine - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *Header) 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 ErrIntOverflowSolomachine - } - 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: Header: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: Header: 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 ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Sequence |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 2: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType) - } - m.Timestamp = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Timestamp |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Signature", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthSolomachine - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Signature = append(m.Signature[:0], dAtA[iNdEx:postIndex]...) - if m.Signature == nil { - m.Signature = []byte{} - } - iNdEx = postIndex - case 4: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field NewPublicKey", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthSolomachine - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.NewPublicKey == nil { - m.NewPublicKey = &types.Any{} - } - if err := m.NewPublicKey.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 5: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field NewDiversifier", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - 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 ErrInvalidLengthSolomachine - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.NewDiversifier = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipSolomachine(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthSolomachine - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *Misbehaviour) 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 ErrIntOverflowSolomachine - } - 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: Misbehaviour: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: Misbehaviour: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ClientId", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - 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 ErrInvalidLengthSolomachine - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.ClientId = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 2: - 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 ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Sequence |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field SignatureOne", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthSolomachine - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.SignatureOne == nil { - m.SignatureOne = &SignatureAndData{} - } - if err := m.SignatureOne.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 4: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field SignatureTwo", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthSolomachine - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.SignatureTwo == nil { - m.SignatureTwo = &SignatureAndData{} - } - if err := m.SignatureTwo.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipSolomachine(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthSolomachine - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *SignatureAndData) 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 ErrIntOverflowSolomachine - } - 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: SignatureAndData: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: SignatureAndData: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Signature", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthSolomachine - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Signature = append(m.Signature[:0], dAtA[iNdEx:postIndex]...) - if m.Signature == nil { - m.Signature = []byte{} - } - iNdEx = postIndex - case 2: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field DataType", wireType) - } - m.DataType = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.DataType |= DataType(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthSolomachine - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...) - if m.Data == nil { - m.Data = []byte{} - } - iNdEx = postIndex - case 4: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType) - } - m.Timestamp = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Timestamp |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - default: - iNdEx = preIndex - skippy, err := skipSolomachine(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthSolomachine - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *TimestampedSignatureData) 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 ErrIntOverflowSolomachine - } - 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: TimestampedSignatureData: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: TimestampedSignatureData: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field SignatureData", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthSolomachine - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.SignatureData = append(m.SignatureData[:0], dAtA[iNdEx:postIndex]...) - if m.SignatureData == nil { - m.SignatureData = []byte{} - } - iNdEx = postIndex - case 2: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType) - } - m.Timestamp = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Timestamp |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - default: - iNdEx = preIndex - skippy, err := skipSolomachine(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthSolomachine - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *SignBytes) 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 ErrIntOverflowSolomachine - } - 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: SignBytes: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: SignBytes: 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 ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Sequence |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 2: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType) - } - m.Timestamp = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Timestamp |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Diversifier", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - 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 ErrInvalidLengthSolomachine - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Diversifier = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 4: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field DataType", wireType) - } - m.DataType = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.DataType |= DataType(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 5: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthSolomachine - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...) - if m.Data == nil { - m.Data = []byte{} - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipSolomachine(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthSolomachine - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *HeaderData) 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 ErrIntOverflowSolomachine - } - 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: HeaderData: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: HeaderData: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field NewPubKey", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthSolomachine - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.NewPubKey == nil { - m.NewPubKey = &types.Any{} - } - if err := m.NewPubKey.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field NewDiversifier", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - 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 ErrInvalidLengthSolomachine - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.NewDiversifier = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipSolomachine(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthSolomachine - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *ClientStateData) 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 ErrIntOverflowSolomachine - } - 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: ClientStateData: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: ClientStateData: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthSolomachine - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Path = append(m.Path[:0], dAtA[iNdEx:postIndex]...) - if m.Path == nil { - m.Path = []byte{} - } - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ClientState", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthSolomachine - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.ClientState == nil { - m.ClientState = &types.Any{} - } - if err := m.ClientState.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipSolomachine(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthSolomachine - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *ConsensusStateData) 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 ErrIntOverflowSolomachine - } - 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: ConsensusStateData: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: ConsensusStateData: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthSolomachine - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Path = append(m.Path[:0], dAtA[iNdEx:postIndex]...) - if m.Path == nil { - m.Path = []byte{} - } - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ConsensusState", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthSolomachine - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.ConsensusState == nil { - m.ConsensusState = &types.Any{} - } - if err := m.ConsensusState.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipSolomachine(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthSolomachine - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *ConnectionStateData) 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 ErrIntOverflowSolomachine - } - 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: ConnectionStateData: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: ConnectionStateData: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthSolomachine - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Path = append(m.Path[:0], dAtA[iNdEx:postIndex]...) - if m.Path == nil { - m.Path = []byte{} - } - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Connection", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthSolomachine - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.Connection == nil { - m.Connection = &types1.ConnectionEnd{} - } - if err := m.Connection.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipSolomachine(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthSolomachine - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *ChannelStateData) 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 ErrIntOverflowSolomachine - } - 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: ChannelStateData: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: ChannelStateData: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthSolomachine - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Path = append(m.Path[:0], dAtA[iNdEx:postIndex]...) - if m.Path == nil { - m.Path = []byte{} - } - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Channel", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthSolomachine - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.Channel == nil { - m.Channel = &types2.Channel{} - } - if err := m.Channel.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipSolomachine(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthSolomachine - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *PacketCommitmentData) 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 ErrIntOverflowSolomachine - } - 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: PacketCommitmentData: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: PacketCommitmentData: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthSolomachine - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Path = append(m.Path[:0], dAtA[iNdEx:postIndex]...) - if m.Path == nil { - m.Path = []byte{} - } - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Commitment", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthSolomachine - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Commitment = append(m.Commitment[:0], dAtA[iNdEx:postIndex]...) - if m.Commitment == nil { - m.Commitment = []byte{} - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipSolomachine(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthSolomachine - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *PacketAcknowledgementData) 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 ErrIntOverflowSolomachine - } - 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: PacketAcknowledgementData: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: PacketAcknowledgementData: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthSolomachine - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Path = append(m.Path[:0], dAtA[iNdEx:postIndex]...) - if m.Path == nil { - m.Path = []byte{} - } - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Acknowledgement", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthSolomachine - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Acknowledgement = append(m.Acknowledgement[:0], dAtA[iNdEx:postIndex]...) - if m.Acknowledgement == nil { - m.Acknowledgement = []byte{} - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipSolomachine(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthSolomachine - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *PacketReceiptAbsenceData) 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 ErrIntOverflowSolomachine - } - 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: PacketReceiptAbsenceData: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: PacketReceiptAbsenceData: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthSolomachine - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Path = append(m.Path[:0], dAtA[iNdEx:postIndex]...) - if m.Path == nil { - m.Path = []byte{} - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipSolomachine(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthSolomachine - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *NextSequenceRecvData) 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 ErrIntOverflowSolomachine - } - 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: NextSequenceRecvData: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: NextSequenceRecvData: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthSolomachine - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Path = append(m.Path[:0], dAtA[iNdEx:postIndex]...) - if m.Path == nil { - m.Path = []byte{} - } - iNdEx = postIndex - case 2: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field NextSeqRecv", wireType) - } - m.NextSeqRecv = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.NextSeqRecv |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - default: - iNdEx = preIndex - skippy, err := skipSolomachine(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthSolomachine - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func skipSolomachine(dAtA []byte) (n int, err error) { - l := len(dAtA) - iNdEx := 0 - depth := 0 - for iNdEx < l { - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowSolomachine - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - wireType := int(wire & 0x7) - switch wireType { - case 0: - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowSolomachine - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - iNdEx++ - if dAtA[iNdEx-1] < 0x80 { - break - } - } - case 1: - iNdEx += 8 - case 2: - var length int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowSolomachine - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - length |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - if length < 0 { - return 0, ErrInvalidLengthSolomachine - } - iNdEx += length - case 3: - depth++ - case 4: - if depth == 0 { - return 0, ErrUnexpectedEndOfGroupSolomachine - } - depth-- - case 5: - iNdEx += 4 - default: - return 0, fmt.Errorf("proto: illegal wireType %d", wireType) - } - if iNdEx < 0 { - return 0, ErrInvalidLengthSolomachine - } - if depth == 0 { - return iNdEx, nil - } - } - return 0, io.ErrUnexpectedEOF -} - -var ( - ErrInvalidLengthSolomachine = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowSolomachine = fmt.Errorf("proto: integer overflow") - ErrUnexpectedEndOfGroupSolomachine = fmt.Errorf("proto: unexpected end of group") -) diff --git a/modules/core/02-client/legacy/v100/store.go b/modules/core/02-client/legacy/v100/store.go deleted file mode 100644 index 70827a89b6a..00000000000 --- a/modules/core/02-client/legacy/v100/store.go +++ /dev/null @@ -1,180 +0,0 @@ -package v100 - -import ( - "fmt" - "strings" - - "github.com/cosmos/cosmos-sdk/codec" - codectypes "github.com/cosmos/cosmos-sdk/codec/types" - "github.com/cosmos/cosmos-sdk/store/prefix" - sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - - "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" - "github.com/cosmos/ibc-go/v4/modules/core/exported" - smtypes "github.com/cosmos/ibc-go/v4/modules/light-clients/06-solomachine/types" - ibctmtypes "github.com/cosmos/ibc-go/v4/modules/light-clients/07-tendermint/types" -) - -// MigrateStore performs in-place store migrations from SDK v0.40 of the IBC module to v1.0.0 of ibc-go. -// The migration includes: -// -// - Migrating solo machine client states from v1 to v2 protobuf definition -// - Pruning all solo machine consensus states -// - Pruning expired tendermint consensus states -// - Adds ProcessedHeight and Iteration keys for unexpired tendermint consensus states -func MigrateStore(ctx sdk.Context, storeKey sdk.StoreKey, cdc codec.BinaryCodec) (err error) { - store := ctx.KVStore(storeKey) - iterator := sdk.KVStorePrefixIterator(store, host.KeyClientStorePrefix) - - var clients []string - - // collect all clients - defer iterator.Close() - for ; iterator.Valid(); iterator.Next() { - keySplit := strings.Split(string(iterator.Key()), "/") - if keySplit[len(keySplit)-1] != host.KeyClientState { - continue - } - - // key is clients/{clientid}/clientState - // Thus, keySplit[1] is clientID - clients = append(clients, keySplit[1]) - } - - for _, clientID := range clients { - clientType, _, err := types.ParseClientIdentifier(clientID) - if err != nil { - return err - } - - clientPrefix := []byte(fmt.Sprintf("%s/%s/", host.KeyClientStorePrefix, clientID)) - clientStore := prefix.NewStore(ctx.KVStore(storeKey), clientPrefix) - - bz := clientStore.Get(host.ClientStateKey()) - if bz == nil { - return clienttypes.ErrClientNotFound - } - - switch clientType { - case exported.Solomachine: - any := &codectypes.Any{} - if err := cdc.Unmarshal(bz, any); err != nil { - return sdkerrors.Wrap(err, "failed to unmarshal client state bytes into solo machine client state") - } - - clientState := &ClientState{} - if err := cdc.Unmarshal(any.Value, clientState); err != nil { - return sdkerrors.Wrap(err, "failed to unmarshal client state bytes into solo machine client state") - } - - updatedClientState := migrateSolomachine(clientState) - - bz, err := clienttypes.MarshalClientState(cdc, updatedClientState) - if err != nil { - return sdkerrors.Wrap(err, "failed to unmarshal client state bytes into solo machine client state") - } - - // update solomachine in store - clientStore.Set(host.ClientStateKey(), bz) - - pruneSolomachineConsensusStates(clientStore) - - case exported.Tendermint: - var clientState exported.ClientState - if err := cdc.UnmarshalInterface(bz, &clientState); err != nil { - return sdkerrors.Wrap(err, "failed to unmarshal client state bytes into tendermint client state") - } - - tmClientState, ok := clientState.(*ibctmtypes.ClientState) - if !ok { - return sdkerrors.Wrap(types.ErrInvalidClient, "client state is not tendermint even though client id contains 07-tendermint") - } - - // add iteration keys so pruning will be successful - if err = addConsensusMetadata(ctx, clientStore, cdc, tmClientState); err != nil { - return err - } - - if err = ibctmtypes.PruneAllExpiredConsensusStates(ctx, clientStore, cdc, tmClientState); err != nil { - return err - } - - default: - continue - } - } - - return nil -} - -// migrateSolomachine migrates the solomachine from v1 to v2 solo machine protobuf defintion. -func migrateSolomachine(clientState *ClientState) *smtypes.ClientState { - isFrozen := clientState.FrozenSequence != 0 - consensusState := &smtypes.ConsensusState{ - PublicKey: clientState.ConsensusState.PublicKey, - Diversifier: clientState.ConsensusState.Diversifier, - Timestamp: clientState.ConsensusState.Timestamp, - } - - return &smtypes.ClientState{ - Sequence: clientState.Sequence, - IsFrozen: isFrozen, - ConsensusState: consensusState, - AllowUpdateAfterProposal: clientState.AllowUpdateAfterProposal, - } -} - -// pruneSolomachineConsensusStates removes all solomachine consensus states from the -// client store. -func pruneSolomachineConsensusStates(clientStore sdk.KVStore) { - iterator := sdk.KVStorePrefixIterator(clientStore, []byte(host.KeyConsensusStatePrefix)) - var heights []exported.Height - - defer iterator.Close() - for ; iterator.Valid(); iterator.Next() { - keySplit := strings.Split(string(iterator.Key()), "/") - // key is in the format "consensusStates/" - if len(keySplit) != 2 || keySplit[0] != string(host.KeyConsensusStatePrefix) { - continue - } - - // collect consensus states to be pruned - heights = append(heights, types.MustParseHeight(keySplit[1])) - } - - // delete all consensus states - for _, height := range heights { - clientStore.Delete(host.ConsensusStateKey(height)) - } -} - -// addConsensusMetadata adds the iteration key and processed height for all tendermint consensus states -// These keys were not included in the previous release of the IBC module. Adding the iteration keys allows -// for pruning iteration. -func addConsensusMetadata(ctx sdk.Context, clientStore sdk.KVStore, cdc codec.BinaryCodec, clientState *ibctmtypes.ClientState) error { - var heights []exported.Height - iterator := sdk.KVStorePrefixIterator(clientStore, []byte(host.KeyConsensusStatePrefix)) - - defer iterator.Close() - for ; iterator.Valid(); iterator.Next() { - keySplit := strings.Split(string(iterator.Key()), "/") - // consensus key is in the format "consensusStates/" - if len(keySplit) != 2 { - continue - } - - heights = append(heights, types.MustParseHeight(keySplit[1])) - } - - for _, height := range heights { - // set the iteration key and processed height - // these keys were not included in the SDK v0.42.0 release - ibctmtypes.SetProcessedHeight(clientStore, height, clienttypes.GetSelfHeight(ctx)) - ibctmtypes.SetIterationKey(clientStore, height) - } - - return nil -} diff --git a/modules/core/02-client/legacy/v100/store_test.go b/modules/core/02-client/legacy/v100/store_test.go deleted file mode 100644 index cb51908f05f..00000000000 --- a/modules/core/02-client/legacy/v100/store_test.go +++ /dev/null @@ -1,231 +0,0 @@ -package v100_test - -import ( - "testing" - "time" - - "github.com/stretchr/testify/suite" - - v100 "github.com/cosmos/ibc-go/v4/modules/core/02-client/legacy/v100" - "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" - "github.com/cosmos/ibc-go/v4/modules/core/exported" - ibctmtypes "github.com/cosmos/ibc-go/v4/modules/light-clients/07-tendermint/types" - ibctesting "github.com/cosmos/ibc-go/v4/testing" -) - -type LegacyTestSuite struct { - suite.Suite - - coordinator *ibctesting.Coordinator - - // testing chains used for convenience and readability - chainA *ibctesting.TestChain - chainB *ibctesting.TestChain -} - -// TestLegacyTestSuite runs all the tests within this package. -func TestLegacyTestSuite(t *testing.T) { - suite.Run(t, new(LegacyTestSuite)) -} - -// SetupTest creates a coordinator with 2 test chains. -func (suite *LegacyTestSuite) SetupTest() { - suite.coordinator = ibctesting.NewCoordinator(suite.T(), 2) - suite.chainA = suite.coordinator.GetChain(ibctesting.GetChainID(1)) - suite.chainB = suite.coordinator.GetChain(ibctesting.GetChainID(2)) - // commit some blocks so that QueryProof returns valid proof (cannot return valid query if height <= 1) - suite.coordinator.CommitNBlocks(suite.chainA, 2) - suite.coordinator.CommitNBlocks(suite.chainB, 2) -} - -// only test migration for solo machines -// ensure all client states are migrated and all consensus states -// are removed -func (suite *LegacyTestSuite) TestMigrateStoreSolomachine() { - path := ibctesting.NewPath(suite.chainA, suite.chainB) - - // create multiple legacy solo machine clients - solomachine := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "06-solomachine-0", "testing", 1) - solomachineMulti := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "06-solomachine-1", "testing", 4) - - // manually generate old proto buf definitions and set in store - // NOTE: we cannot use 'CreateClient' and 'UpdateClient' functions since we are - // using client states and consensus states which do not implement the exported.ClientState - // and exported.ConsensusState interface - for _, sm := range []*ibctesting.Solomachine{solomachine, solomachineMulti} { - clientStore := path.EndpointA.Chain.App.GetIBCKeeper().ClientKeeper.ClientStore(path.EndpointA.Chain.GetContext(), sm.ClientID) - clientState := sm.ClientState() - - var seq uint64 - if clientState.IsFrozen { - seq = 1 - } - - // generate old client state proto defintion - legacyClientState := &v100.ClientState{ - Sequence: clientState.Sequence, - FrozenSequence: seq, - ConsensusState: &v100.ConsensusState{ - PublicKey: clientState.ConsensusState.PublicKey, - Diversifier: clientState.ConsensusState.Diversifier, - Timestamp: clientState.ConsensusState.Timestamp, - }, - AllowUpdateAfterProposal: clientState.AllowUpdateAfterProposal, - } - - // set client state - bz, err := path.EndpointA.Chain.App.AppCodec().MarshalInterface(legacyClientState) - suite.Require().NoError(err) - clientStore.Set(host.ClientStateKey(), bz) - - // set some consensus states - height1 := types.NewHeight(0, 1) - height2 := types.NewHeight(1, 2) - height3 := types.NewHeight(0, 123) - - bz, err = path.EndpointA.Chain.App.AppCodec().MarshalInterface(legacyClientState.ConsensusState) - suite.Require().NoError(err) - clientStore.Set(host.ConsensusStateKey(height1), bz) - clientStore.Set(host.ConsensusStateKey(height2), bz) - clientStore.Set(host.ConsensusStateKey(height3), bz) - } - - // create tendermint clients - suite.coordinator.SetupClients(path) - - err := v100.MigrateStore(path.EndpointA.Chain.GetContext(), path.EndpointA.Chain.GetSimApp().GetKey(host.StoreKey), path.EndpointA.Chain.App.AppCodec()) - suite.Require().NoError(err) - - // verify client state has been migrated - for _, sm := range []*ibctesting.Solomachine{solomachine, solomachineMulti} { - clientState, ok := path.EndpointA.Chain.App.GetIBCKeeper().ClientKeeper.GetClientState(path.EndpointA.Chain.GetContext(), sm.ClientID) - suite.Require().True(ok) - suite.Require().Equal(sm.ClientState(), clientState) - } - - // verify consensus states have been removed - for _, sm := range []*ibctesting.Solomachine{solomachine, solomachineMulti} { - clientConsensusStates := path.EndpointA.Chain.App.GetIBCKeeper().ClientKeeper.GetAllConsensusStates(path.EndpointA.Chain.GetContext()) - for _, client := range clientConsensusStates { - // GetAllConsensusStates should not return consensus states for our solo machine clients - suite.Require().NotEqual(sm.ClientID, client.ClientId) - } - } -} - -// only test migration for tendermint clients -// ensure all expired consensus states are removed from tendermint client stores -func (suite *LegacyTestSuite) TestMigrateStoreTendermint() { - // create path and setup clients - path1 := ibctesting.NewPath(suite.chainA, suite.chainB) - suite.coordinator.SetupClients(path1) - - path2 := ibctesting.NewPath(suite.chainA, suite.chainB) - suite.coordinator.SetupClients(path2) - pruneHeightMap := make(map[*ibctesting.Path][]exported.Height) - unexpiredHeightMap := make(map[*ibctesting.Path][]exported.Height) - - for _, path := range []*ibctesting.Path{path1, path2} { - // collect all heights expected to be pruned - var pruneHeights []exported.Height - pruneHeights = append(pruneHeights, path.EndpointA.GetClientState().GetLatestHeight()) - - // these heights will be expired and also pruned - for i := 0; i < 3; i++ { - path.EndpointA.UpdateClient() - pruneHeights = append(pruneHeights, path.EndpointA.GetClientState().GetLatestHeight()) - } - - // double chedck all information is currently stored - for _, pruneHeight := range pruneHeights { - consState, ok := path.EndpointA.Chain.GetConsensusState(path.EndpointA.ClientID, pruneHeight) - suite.Require().True(ok) - suite.Require().NotNil(consState) - - ctx := path.EndpointA.Chain.GetContext() - clientStore := path.EndpointA.Chain.App.GetIBCKeeper().ClientKeeper.ClientStore(ctx, path.EndpointA.ClientID) - - processedTime, ok := ibctmtypes.GetProcessedTime(clientStore, pruneHeight) - suite.Require().True(ok) - suite.Require().NotNil(processedTime) - - processedHeight, ok := ibctmtypes.GetProcessedHeight(clientStore, pruneHeight) - suite.Require().True(ok) - suite.Require().NotNil(processedHeight) - - expectedConsKey := ibctmtypes.GetIterationKey(clientStore, pruneHeight) - suite.Require().NotNil(expectedConsKey) - } - pruneHeightMap[path] = pruneHeights - } - - // Increment the time by a week - suite.coordinator.IncrementTimeBy(7 * 24 * time.Hour) - - for _, path := range []*ibctesting.Path{path1, path2} { - // create the consensus state that can be used as trusted height for next update - var unexpiredHeights []exported.Height - path.EndpointA.UpdateClient() - unexpiredHeights = append(unexpiredHeights, path.EndpointA.GetClientState().GetLatestHeight()) - path.EndpointA.UpdateClient() - unexpiredHeights = append(unexpiredHeights, path.EndpointA.GetClientState().GetLatestHeight()) - - // remove processed height and iteration keys since these were missing from previous version of ibc module - clientStore := path.EndpointA.Chain.App.GetIBCKeeper().ClientKeeper.ClientStore(path.EndpointA.Chain.GetContext(), path.EndpointA.ClientID) - for _, height := range unexpiredHeights { - clientStore.Delete(ibctmtypes.ProcessedHeightKey(height)) - clientStore.Delete(ibctmtypes.IterationKey(height)) - } - - unexpiredHeightMap[path] = unexpiredHeights - } - - // Increment the time by another week, then update the client. - // This will cause the consensus states created before the first time increment - // to be expired - suite.coordinator.IncrementTimeBy(7 * 24 * time.Hour) - err := v100.MigrateStore(path1.EndpointA.Chain.GetContext(), path1.EndpointA.Chain.GetSimApp().GetKey(host.StoreKey), path1.EndpointA.Chain.App.AppCodec()) - suite.Require().NoError(err) - - for _, path := range []*ibctesting.Path{path1, path2} { - ctx := path.EndpointA.Chain.GetContext() - clientStore := path.EndpointA.Chain.App.GetIBCKeeper().ClientKeeper.ClientStore(ctx, path.EndpointA.ClientID) - - // ensure everything has been pruned - for i, pruneHeight := range pruneHeightMap[path] { - consState, ok := path.EndpointA.Chain.GetConsensusState(path.EndpointA.ClientID, pruneHeight) - suite.Require().False(ok, i) - suite.Require().Nil(consState, i) - - processedTime, ok := ibctmtypes.GetProcessedTime(clientStore, pruneHeight) - suite.Require().False(ok, i) - suite.Require().Equal(uint64(0), processedTime, i) - - processedHeight, ok := ibctmtypes.GetProcessedHeight(clientStore, pruneHeight) - suite.Require().False(ok, i) - suite.Require().Nil(processedHeight, i) - - expectedConsKey := ibctmtypes.GetIterationKey(clientStore, pruneHeight) - suite.Require().Nil(expectedConsKey, i) - } - - // ensure metadata is set for unexpired consensus state - for _, height := range unexpiredHeightMap[path] { - consState, ok := path.EndpointA.Chain.GetConsensusState(path.EndpointA.ClientID, height) - suite.Require().True(ok) - suite.Require().NotNil(consState) - - processedTime, ok := ibctmtypes.GetProcessedTime(clientStore, height) - suite.Require().True(ok) - suite.Require().NotEqual(uint64(0), processedTime) - - processedHeight, ok := ibctmtypes.GetProcessedHeight(clientStore, height) - suite.Require().True(ok) - suite.Require().Equal(types.GetSelfHeight(path.EndpointA.Chain.GetContext()), processedHeight) - - consKey := ibctmtypes.GetIterationKey(clientStore, height) - suite.Require().Equal(host.ConsensusStateKey(height), consKey) - } - } -} diff --git a/modules/core/02-client/migrations/v7/expected_keepers.go b/modules/core/02-client/migrations/v7/expected_keepers.go new file mode 100644 index 00000000000..6d424cc0370 --- /dev/null +++ b/modules/core/02-client/migrations/v7/expected_keepers.go @@ -0,0 +1,14 @@ +package v7 + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/cosmos/ibc-go/v7/modules/core/exported" +) + +// ClientKeeper expected IBC client keeper +type ClientKeeper interface { + GetClientState(ctx sdk.Context, clientID string) (exported.ClientState, bool) + SetClientState(ctx sdk.Context, clientID string, clientState exported.ClientState) + ClientStore(ctx sdk.Context, clientID string) sdk.KVStore +} diff --git a/modules/core/02-client/migrations/v7/genesis.go b/modules/core/02-client/migrations/v7/genesis.go new file mode 100644 index 00000000000..e2f803dd35d --- /dev/null +++ b/modules/core/02-client/migrations/v7/genesis.go @@ -0,0 +1,77 @@ +package v7 + +import ( + errorsmod "cosmossdk.io/errors" + "github.com/cosmos/cosmos-sdk/codec" + + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v7/modules/core/exported" +) + +// MigrateGenesis accepts an exported IBC client genesis file and migrates it to: +// +// - Update solo machine client state protobuf definition (v2 to v3) +// - Remove all solo machine consensus states +// - Remove localhost client +func MigrateGenesis(clientGenState *clienttypes.GenesisState, cdc codec.ProtoCodecMarshaler) (*clienttypes.GenesisState, error) { + // To prune the client and consensus states, we will create new slices to fill up + // with information we want to keep. + var ( + clientsConsensus []clienttypes.ClientConsensusStates + clients []clienttypes.IdentifiedClientState + ) + + for _, client := range clientGenState.Clients { + clientType, _, err := clienttypes.ParseClientIdentifier(client.ClientId) + if err != nil { + return nil, err + } + + switch clientType { + case exported.Solomachine: + var clientState ClientState + if err := cdc.Unmarshal(client.ClientState.Value, &clientState); err != nil { + return nil, errorsmod.Wrap(err, "failed to unmarshal client state bytes into solo machine client state") + } + + updatedClientState := migrateSolomachine(clientState) + + protoAny, err := clienttypes.PackClientState(&updatedClientState) + if err != nil { + return nil, err + } + + clients = append(clients, clienttypes.IdentifiedClientState{ + ClientId: client.ClientId, + ClientState: protoAny, + }) + + case Localhost: + // remove localhost client state by not adding client state + + default: + // add all other client states + clients = append(clients, client) + } + + // iterate consensus states by client + for _, clientConsensusStates := range clientGenState.ClientsConsensus { + // look for consensus states for the current client + if clientConsensusStates.ClientId == client.ClientId { + switch clientType { + case exported.Solomachine, Localhost: + // remove all consensus states for the solo machine and localhost + // do not add to new clientsConsensus + + default: + // ensure all consensus states added for other client types + clientsConsensus = append(clientsConsensus, clientConsensusStates) + } + } + } + } + + clientGenState.Clients = clients + clientGenState.ClientsConsensus = clientsConsensus + return clientGenState, nil +} diff --git a/modules/core/02-client/migrations/v7/genesis_test.go b/modules/core/02-client/migrations/v7/genesis_test.go new file mode 100644 index 00000000000..79e285ba58a --- /dev/null +++ b/modules/core/02-client/migrations/v7/genesis_test.go @@ -0,0 +1,139 @@ +package v7_test + +import ( + "encoding/json" + + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + + ibcclient "github.com/cosmos/ibc-go/v7/modules/core/02-client" + v7 "github.com/cosmos/ibc-go/v7/modules/core/02-client/migrations/v7" + "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + ibcexported "github.com/cosmos/ibc-go/v7/modules/core/exported" + ibctesting "github.com/cosmos/ibc-go/v7/testing" +) + +func (suite *MigrationsV7TestSuite) TestMigrateGenesisSolomachine() { + // create tendermint clients + for i := 0; i < 3; i++ { + path := ibctesting.NewPath(suite.chainA, suite.chainB) + + suite.coordinator.SetupClients(path) + + err := path.EndpointA.UpdateClient() + suite.Require().NoError(err) + + // update a second time to add more state + err = path.EndpointA.UpdateClient() + suite.Require().NoError(err) + } + + // create multiple legacy solo machine clients + solomachine := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "06-solomachine-0", "testing", 1) + solomachineMulti := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "06-solomachine-1", "testing", 4) + + clientGenState := ibcclient.ExportGenesis(suite.chainA.GetContext(), suite.chainA.App.GetIBCKeeper().ClientKeeper) + + // manually generate old proto buf definitions and set in genesis + // NOTE: we cannot use 'ExportGenesis' for the solo machines since we are + // using client states and consensus states which do not implement the exported.ClientState + // and exported.ConsensusState interface + var clients []types.IdentifiedClientState + for _, sm := range []*ibctesting.Solomachine{solomachine, solomachineMulti} { + clientState := sm.ClientState() + + // generate old client state proto definition + legacyClientState := &v7.ClientState{ + Sequence: clientState.Sequence, + ConsensusState: &v7.ConsensusState{ + PublicKey: clientState.ConsensusState.PublicKey, + Diversifier: clientState.ConsensusState.Diversifier, + Timestamp: clientState.ConsensusState.Timestamp, + }, + AllowUpdateAfterProposal: true, + } + + // set client state + protoAny, err := codectypes.NewAnyWithValue(legacyClientState) + suite.Require().NoError(err) + suite.Require().NotNil(protoAny) + + clients = append(clients, types.IdentifiedClientState{ + ClientId: sm.ClientID, + ClientState: protoAny, + }) + + // set in store for ease of determining expected genesis + clientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), sm.ClientID) + + cdc := suite.chainA.App.AppCodec().(*codec.ProtoCodec) + v7.RegisterInterfaces(cdc.InterfaceRegistry()) + + bz, err := cdc.MarshalInterface(legacyClientState) + suite.Require().NoError(err) + clientStore.Set(host.ClientStateKey(), bz) + + protoAny, err = codectypes.NewAnyWithValue(legacyClientState.ConsensusState) + suite.Require().NoError(err) + suite.Require().NotNil(protoAny) + + // obtain marshalled bytes to set in client store + bz, err = cdc.MarshalInterface(legacyClientState.ConsensusState) + suite.Require().NoError(err) + + var consensusStates []types.ConsensusStateWithHeight + + // set consensus states in store and genesis + for i := uint64(0); i < numCreations; i++ { + height := types.NewHeight(1, i) + clientStore.Set(host.ConsensusStateKey(height), bz) + consensusStates = append(consensusStates, types.ConsensusStateWithHeight{ + Height: height, + ConsensusState: protoAny, + }) + } + + clientGenState.ClientsConsensus = append(clientGenState.ClientsConsensus, types.ClientConsensusStates{ + ClientId: sm.ClientID, + ConsensusStates: consensusStates, + }) + } + + // solo machine clients must come before tendermint in expected + clientGenState.Clients = append(clients, clientGenState.Clients...) + + // migrate store get expected genesis + // store migration and genesis migration should produce identical results + // NOTE: tendermint clients are not pruned in genesis so the test should not have expired tendermint clients + err := v7.MigrateStore(suite.chainA.GetContext(), suite.chainA.GetSimApp().GetKey(ibcexported.StoreKey), suite.chainA.App.AppCodec(), suite.chainA.GetSimApp().IBCKeeper.ClientKeeper) + suite.Require().NoError(err) + expectedClientGenState := ibcclient.ExportGenesis(suite.chainA.GetContext(), suite.chainA.App.GetIBCKeeper().ClientKeeper) + + cdc, ok := suite.chainA.App.AppCodec().(codec.ProtoCodecMarshaler) + suite.Require().True(ok) + + migrated, err := v7.MigrateGenesis(&clientGenState, cdc) + suite.Require().NoError(err) + + bz, err := cdc.MarshalJSON(&expectedClientGenState) + suite.Require().NoError(err) + + // Indent the JSON bz correctly. + var jsonObj map[string]interface{} + err = json.Unmarshal(bz, &jsonObj) + suite.Require().NoError(err) + expectedIndentedBz, err := json.MarshalIndent(jsonObj, "", "\t") + suite.Require().NoError(err) + + bz, err = cdc.MarshalJSON(migrated) + suite.Require().NoError(err) + + // Indent the JSON bz correctly. + err = json.Unmarshal(bz, &jsonObj) + suite.Require().NoError(err) + indentedBz, err := json.MarshalIndent(jsonObj, "", "\t") + suite.Require().NoError(err) + + suite.Require().Equal(string(expectedIndentedBz), string(indentedBz)) +} diff --git a/modules/core/02-client/legacy/v100/solomachine.go b/modules/core/02-client/migrations/v7/solomachine.go similarity index 75% rename from modules/core/02-client/legacy/v100/solomachine.go rename to modules/core/02-client/migrations/v7/solomachine.go index a9b8fa49340..8996bae2e5f 100644 --- a/modules/core/02-client/legacy/v100/solomachine.go +++ b/modules/core/02-client/migrations/v7/solomachine.go @@ -1,13 +1,13 @@ -package v100 +package v7 import ( - ics23 "github.com/confio/ics23/go" "github.com/cosmos/cosmos-sdk/codec" codectypes "github.com/cosmos/cosmos-sdk/codec/types" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" sdk "github.com/cosmos/cosmos-sdk/types" + ics23 "github.com/cosmos/ics23/go" - "github.com/cosmos/ibc-go/v4/modules/core/exported" + "github.com/cosmos/ibc-go/v7/modules/core/exported" ) // NOTE: this is a mock implmentation for exported.ClientState. This implementation @@ -16,7 +16,7 @@ import ( // client state and consensus state and migrate them to the new implementations. When the proto // codec unmarshals, it calls UnpackInterfaces() to create a cached value of the any. The // UnpackInterfaces function for IdenitifiedClientState will attempt to unpack the any to -// exported.ClientState. If the solomachine v1 type is not registered against the exported.ClientState +// exported.ClientState. If the solomachine v2 type is not registered against the exported.ClientState // the unmarshal will fail. This implementation will panic on every interface function. // The same is done for the ConsensusState. @@ -27,6 +27,7 @@ var ( _ exported.ConsensusState = &ConsensusState{} ) +// RegisterInterfaces registers the solomachine v2 ClientState and ConsensusState types in the interface registry. func RegisterInterfaces(registry codectypes.InterfaceRegistry) { registry.RegisterImplementations( (*exported.ClientState)(nil), @@ -88,16 +89,40 @@ func (cs ClientState) ExportMetadata(_ sdk.KVStore) []exported.GenesisMetadata { panic("legacy solo machine is deprecated!") } +// CheckForMisbehaviour panics! +func (cs ClientState) CheckForMisbehaviour(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, msg exported.ClientMessage) bool { + panic("legacy solo machine is deprecated!") +} + +// UpdateStateOnMisbehaviour panics! +func (cs *ClientState) UpdateStateOnMisbehaviour( + _ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, _ exported.ClientMessage, +) { + panic("legacy solo machine is deprecated!") +} + +// VerifyClientMessage panics! +func (cs *ClientState) VerifyClientMessage( + _ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, _ exported.ClientMessage, +) error { + panic("legacy solo machine is deprecated!") +} + +// UpdateState panis! +func (cs *ClientState) UpdateState(_ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, _ exported.ClientMessage) []exported.Height { + panic("legacy solo machine is deprecated!") +} + // CheckHeaderAndUpdateState panics! func (cs *ClientState) CheckHeaderAndUpdateState( - _ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, _ exported.Header, + _ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, _ exported.ClientMessage, ) (exported.ClientState, exported.ConsensusState, error) { panic("legacy solo machine is deprecated!") } // CheckMisbehaviourAndUpdateState panics! func (cs ClientState) CheckMisbehaviourAndUpdateState( - _ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, _ exported.Misbehaviour, + _ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, _ exported.ClientMessage, ) (exported.ClientState, error) { panic("legacy solo machine is deprecated!") } @@ -106,7 +131,7 @@ func (cs ClientState) CheckMisbehaviourAndUpdateState( func (cs ClientState) CheckSubstituteAndUpdateState( ctx sdk.Context, _ codec.BinaryCodec, _, _ sdk.KVStore, _ exported.ClientState, -) (exported.ClientState, error) { +) error { panic("legacy solo machine is deprecated!") } @@ -114,7 +139,7 @@ func (cs ClientState) CheckSubstituteAndUpdateState( func (cs ClientState) VerifyUpgradeAndUpdateState( _ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, _ exported.ClientState, _ exported.ConsensusState, _, _ []byte, -) (exported.ClientState, exported.ConsensusState, error) { +) error { panic("legacy solo machine is deprecated!") } @@ -187,6 +212,42 @@ func (cs ClientState) VerifyNextSequenceRecv( panic("legacy solo machine is deprecated!") } +// GetTimestampAtHeight panics! +func (cs ClientState) GetTimestampAtHeight( + sdk.Context, sdk.KVStore, codec.BinaryCodec, exported.Height, +) (uint64, error) { + panic("legacy solo machine is deprecated!") +} + +// VerifyMembership panics! +func (cs *ClientState) VerifyMembership( + ctx sdk.Context, + clientStore sdk.KVStore, + cdc codec.BinaryCodec, + height exported.Height, + delayTimePeriod uint64, + delayBlockPeriod uint64, + proof []byte, + path exported.Path, + value []byte, +) error { + panic("legacy solo machine is deprecated!") +} + +// VerifyNonMembership panics! +func (cs *ClientState) VerifyNonMembership( + ctx sdk.Context, + clientStore sdk.KVStore, + cdc codec.BinaryCodec, + height exported.Height, + delayTimePeriod uint64, + delayBlockPeriod uint64, + proof []byte, + path exported.Path, +) error { + panic("legacy solo machine is deprecated") +} + // ClientType panics! func (ConsensusState) ClientType() string { panic("legacy solo machine is deprecated!") @@ -197,11 +258,6 @@ func (cs ConsensusState) GetTimestamp() uint64 { panic("legacy solo machine is deprecated!") } -// GetRoot panics! -func (cs ConsensusState) GetRoot() exported.Root { - panic("legacy solo machine is deprecated!") -} - // ValidateBasic panics! func (cs ConsensusState) ValidateBasic() error { panic("legacy solo machine is deprecated!") diff --git a/modules/light-clients/06-solomachine/types/solomachine.pb.go b/modules/core/02-client/migrations/v7/solomachine.pb.go similarity index 92% rename from modules/light-clients/06-solomachine/types/solomachine.pb.go rename to modules/core/02-client/migrations/v7/solomachine.pb.go index 238f9849ede..c51210cf69d 100644 --- a/modules/light-clients/06-solomachine/types/solomachine.pb.go +++ b/modules/core/02-client/migrations/v7/solomachine.pb.go @@ -1,15 +1,15 @@ // Code generated by protoc-gen-gogo. DO NOT EDIT. // source: ibc/lightclients/solomachine/v2/solomachine.proto -package types +package v7 import ( fmt "fmt" types "github.com/cosmos/cosmos-sdk/codec/types" - types1 "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types" - types2 "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" - _ "github.com/gogo/protobuf/gogoproto" - proto "github.com/gogo/protobuf/proto" + _ "github.com/cosmos/gogoproto/gogoproto" + proto "github.com/cosmos/gogoproto/proto" + types1 "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" + types2 "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" io "io" math "math" math_bits "math/bits" @@ -823,93 +823,93 @@ func init() { } var fileDescriptor_141333b361aae010 = []byte{ - // 1370 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x57, 0x5f, 0x8f, 0xdb, 0x44, - 0x10, 0x3f, 0xa7, 0xe9, 0xf5, 0x32, 0xb9, 0xde, 0x05, 0x37, 0x6d, 0x73, 0x6e, 0x95, 0x18, 0x23, - 0xca, 0x81, 0x68, 0xc2, 0x1d, 0x50, 0xa1, 0x0a, 0x01, 0x8e, 0xe3, 0xd2, 0xb4, 0x77, 0xbe, 0xe0, - 0xf8, 0x80, 0x56, 0x48, 0xc6, 0x71, 0xf6, 0x12, 0xab, 0x89, 0x37, 0x8d, 0x9d, 0xa4, 0x41, 0x42, - 0x42, 0x3c, 0x95, 0x88, 0x07, 0xbe, 0x40, 0x24, 0x04, 0xe2, 0x73, 0xf0, 0x06, 0x3c, 0xf6, 0x91, - 0xa7, 0x80, 0xda, 0x6f, 0x90, 0x4f, 0x80, 0xec, 0xdd, 0xc4, 0x76, 0xae, 0x97, 0x13, 0xff, 0xde, - 0x76, 0xe7, 0x37, 0xf3, 0x9b, 0xd9, 0x99, 0xf1, 0xec, 0x1a, 0x76, 0xac, 0x9a, 0x59, 0x68, 0x59, - 0x8d, 0xa6, 0x6b, 0xb6, 0x2c, 0x64, 0xbb, 0x4e, 0xc1, 0xc1, 0x2d, 0xdc, 0x36, 0xcc, 0xa6, 0x65, - 0xa3, 0x42, 0x7f, 0x37, 0xbc, 0xcd, 0x77, 0xba, 0xd8, 0xc5, 0x6c, 0xce, 0xaa, 0x99, 0xf9, 0xb0, - 0x49, 0x3e, 0xac, 0xd3, 0xdf, 0xe5, 0x5e, 0xf1, 0x38, 0x4d, 0xdc, 0x45, 0x05, 0x13, 0xdb, 0x36, - 0x32, 0x5d, 0x0b, 0xdb, 0x85, 0xfe, 0x4e, 0x68, 0x47, 0x98, 0xb8, 0x17, 0x03, 0xc5, 0xa6, 0x61, - 0xdb, 0xa8, 0xe5, 0x6b, 0x91, 0x25, 0x55, 0x49, 0x37, 0x70, 0x03, 0xfb, 0xcb, 0x82, 0xb7, 0xa2, - 0xd2, 0xad, 0x06, 0xc6, 0x8d, 0x16, 0x2a, 0xf8, 0xbb, 0x5a, 0xef, 0xa8, 0x60, 0xd8, 0x43, 0x02, - 0x09, 0x3f, 0xc7, 0x20, 0x29, 0xf9, 0x71, 0x55, 0x5d, 0xc3, 0x45, 0x2c, 0x07, 0x6b, 0x0e, 0x7a, - 0xd8, 0x43, 0xb6, 0x89, 0x32, 0x0c, 0xcf, 0x6c, 0xc7, 0xd5, 0xf9, 0x9e, 0xdd, 0x81, 0x84, 0xe5, - 0xe8, 0x47, 0x5d, 0xfc, 0x05, 0xb2, 0x33, 0x31, 0x9e, 0xd9, 0x5e, 0x2b, 0xa6, 0xa7, 0x93, 0x5c, - 0x6a, 0x68, 0xb4, 0x5b, 0x37, 0x85, 0x39, 0x24, 0xa8, 0x6b, 0x96, 0x73, 0xcb, 0x5f, 0xb2, 0x2e, - 0x6c, 0x9a, 0xd8, 0x76, 0x90, 0xed, 0xf4, 0x1c, 0xdd, 0xf1, 0x3c, 0x64, 0xce, 0xf0, 0xcc, 0x76, - 0x72, 0xb7, 0x90, 0x3f, 0x25, 0x2d, 0x79, 0x69, 0x66, 0xe7, 0x07, 0x56, 0xe4, 0xa6, 0x93, 0xdc, - 0x25, 0xe2, 0x69, 0x81, 0x51, 0x50, 0x37, 0xcc, 0x88, 0x2e, 0x8b, 0xe0, 0x8a, 0xd1, 0x6a, 0xe1, - 0x81, 0xde, 0xeb, 0xd4, 0x0d, 0x17, 0xe9, 0xc6, 0x91, 0x8b, 0xba, 0x7a, 0xa7, 0x8b, 0x3b, 0xd8, - 0x31, 0x5a, 0x99, 0xb8, 0x1f, 0xfa, 0xb5, 0xe9, 0x24, 0x27, 0x10, 0xc2, 0x25, 0xca, 0x82, 0x9a, - 0xf1, 0xd1, 0x43, 0x1f, 0x14, 0x3d, 0xac, 0x42, 0xa1, 0x9b, 0xf1, 0xc7, 0xdf, 0xe7, 0x56, 0x84, - 0x1f, 0x18, 0xd8, 0x88, 0xc6, 0xca, 0xde, 0x01, 0xe8, 0xf4, 0x6a, 0x2d, 0xcb, 0xd4, 0x1f, 0xa0, - 0xa1, 0x9f, 0xc6, 0xe4, 0x6e, 0x3a, 0x4f, 0x8a, 0x90, 0x9f, 0x15, 0x21, 0x2f, 0xda, 0xc3, 0xe2, - 0xc5, 0xe9, 0x24, 0xf7, 0x02, 0x09, 0x22, 0xb0, 0x10, 0xd4, 0x04, 0xd9, 0xdc, 0x45, 0x43, 0x96, - 0x87, 0x64, 0xdd, 0xea, 0xa3, 0xae, 0x63, 0x1d, 0x59, 0xa8, 0xeb, 0xa7, 0x3d, 0xa1, 0x86, 0x45, - 0xec, 0x55, 0x48, 0xb8, 0x56, 0x1b, 0x39, 0xae, 0xd1, 0xee, 0xf8, 0xd9, 0x8d, 0xab, 0x81, 0x80, - 0x06, 0xf9, 0x75, 0x0c, 0x56, 0x6f, 0x23, 0xa3, 0x8e, 0xba, 0x4b, 0x2b, 0x1c, 0xa1, 0x8a, 0x2d, - 0x50, 0x79, 0xa8, 0x63, 0x35, 0x6c, 0xc3, 0xed, 0x75, 0x49, 0x19, 0xd7, 0xd5, 0x40, 0xc0, 0x1e, - 0xc2, 0x86, 0x8d, 0x06, 0x7a, 0xe8, 0xe0, 0xf1, 0x25, 0x07, 0xdf, 0x9a, 0x4e, 0x72, 0x17, 0xc9, - 0xc1, 0xa3, 0x56, 0x82, 0xba, 0x6e, 0xa3, 0x41, 0x65, 0x7e, 0x7e, 0x09, 0x36, 0x3d, 0x85, 0x70, - 0x0e, 0xce, 0x7a, 0x39, 0x08, 0x37, 0xc4, 0x82, 0x82, 0xa0, 0x7a, 0x91, 0x94, 0x02, 0x01, 0x4d, - 0xc2, 0xaf, 0x31, 0x58, 0xdf, 0xb7, 0x9c, 0x1a, 0x6a, 0x1a, 0x7d, 0x0b, 0xf7, 0xba, 0x5e, 0x43, - 0x93, 0xe6, 0xd3, 0xad, 0xba, 0x9f, 0x8b, 0x44, 0xb8, 0xa1, 0xe7, 0x90, 0xa0, 0xae, 0x91, 0x75, - 0xb9, 0x1e, 0xc9, 0x5e, 0x6c, 0x21, 0x7b, 0x1d, 0x38, 0x3f, 0x4f, 0x87, 0x8e, 0xed, 0x59, 0xab, - 0xef, 0x9c, 0xda, 0xea, 0xd5, 0x99, 0x95, 0x68, 0xd7, 0x4b, 0x86, 0x6b, 0x14, 0x33, 0xd3, 0x49, - 0x2e, 0x4d, 0xa2, 0x88, 0x30, 0x0a, 0xea, 0xfa, 0x7c, 0x7f, 0x60, 0x2f, 0x78, 0x74, 0x07, 0x98, - 0xa6, 0xfc, 0xbf, 0xf2, 0xe8, 0x0e, 0x70, 0xd8, 0xa3, 0x36, 0xc0, 0x34, 0x93, 0xbf, 0x30, 0x90, - 0x5a, 0xa4, 0x88, 0xb6, 0x07, 0xb3, 0xd8, 0x1e, 0x9f, 0x41, 0xa2, 0x6e, 0xb8, 0x86, 0xee, 0x0e, - 0x3b, 0x24, 0x73, 0x1b, 0xbb, 0xaf, 0x9e, 0x1a, 0xa6, 0xc7, 0xab, 0x0d, 0x3b, 0x28, 0x5c, 0x96, - 0x39, 0x8b, 0xa0, 0xae, 0xd5, 0x29, 0xce, 0xb2, 0x10, 0xf7, 0xd6, 0xb4, 0x2b, 0xfd, 0x75, 0xb4, - 0x99, 0xe3, 0xcf, 0xff, 0x2e, 0xbe, 0x62, 0x20, 0xa3, 0xcd, 0x64, 0xa8, 0x3e, 0x3f, 0x93, 0x7f, - 0xa0, 0x0f, 0x60, 0x23, 0xc8, 0x85, 0x4f, 0xef, 0x9f, 0x2a, 0xdc, 0xbb, 0x51, 0x5c, 0x50, 0x83, - 0x72, 0x94, 0x8e, 0x85, 0x10, 0x7b, 0x7e, 0x08, 0x7f, 0x30, 0x90, 0xf0, 0xfc, 0x16, 0x87, 0x2e, - 0x72, 0xfe, 0xc5, 0xd7, 0xb9, 0x30, 0x28, 0xce, 0x1c, 0x1f, 0x14, 0x91, 0x12, 0xc4, 0xff, 0xaf, - 0x12, 0x9c, 0x0d, 0x4a, 0x40, 0x4f, 0xf8, 0x13, 0x03, 0x40, 0x86, 0x8f, 0x9f, 0x94, 0x3d, 0x48, - 0xd2, 0x4f, 0xfe, 0xd4, 0xf1, 0x78, 0x69, 0x3a, 0xc9, 0xb1, 0x91, 0x29, 0x41, 0xe7, 0x23, 0x19, - 0x11, 0x27, 0xcc, 0x87, 0xd8, 0x3f, 0x9c, 0x0f, 0x5f, 0xc2, 0x66, 0xe8, 0x2a, 0xf4, 0x63, 0x65, - 0x21, 0xde, 0x31, 0xdc, 0x26, 0x6d, 0x67, 0x7f, 0xcd, 0x56, 0x60, 0x9d, 0x8e, 0x06, 0x72, 0xa1, - 0xc5, 0x96, 0x1c, 0xe0, 0xf2, 0x74, 0x92, 0xbb, 0x10, 0x19, 0x27, 0xf4, 0xca, 0x4a, 0x9a, 0x81, - 0x27, 0xea, 0xfe, 0x1b, 0x06, 0xd8, 0xe8, 0x45, 0x72, 0x62, 0x08, 0xf7, 0x8e, 0x5f, 0xab, 0xcb, - 0xa2, 0xf8, 0x1b, 0x77, 0x27, 0x8d, 0xa5, 0x0f, 0x17, 0xa4, 0xf9, 0xf3, 0x63, 0x79, 0x2c, 0x32, - 0x40, 0xf0, 0x52, 0xa1, 0x61, 0xbc, 0xec, 0xb7, 0x95, 0xf7, 0x54, 0xc9, 0x87, 0x5e, 0x31, 0xfd, - 0x9d, 0x7c, 0x40, 0x2a, 0xdb, 0x75, 0x35, 0x64, 0x48, 0xfd, 0xd6, 0x21, 0x25, 0x91, 0x07, 0xcd, - 0x72, 0xa7, 0x37, 0xe0, 0x1c, 0x7d, 0xf8, 0x50, 0x8f, 0x57, 0x43, 0x1e, 0xe9, 0x8b, 0xc8, 0x73, - 0x47, 0x96, 0xea, 0x4c, 0x99, 0x7a, 0xb9, 0x03, 0xe9, 0x8a, 0x61, 0x3e, 0x40, 0xae, 0x84, 0xdb, - 0x6d, 0xcb, 0x6d, 0x23, 0xdb, 0x3d, 0xd1, 0x53, 0xd6, 0x3b, 0xde, 0x4c, 0xcb, 0x77, 0xb6, 0xae, - 0x86, 0x24, 0xc2, 0x3d, 0xd8, 0x22, 0x5c, 0xa2, 0xf9, 0xc0, 0xc6, 0x83, 0x16, 0xaa, 0x37, 0xd0, - 0x52, 0xc2, 0x6d, 0xd8, 0x34, 0xa2, 0xaa, 0x94, 0x75, 0x51, 0x2c, 0xe4, 0x21, 0x43, 0xa8, 0x55, - 0x64, 0x22, 0xab, 0xe3, 0x8a, 0x35, 0xc7, 0x9b, 0x03, 0x27, 0x31, 0x0b, 0x4d, 0x48, 0x2b, 0xe8, - 0x91, 0x5b, 0xa5, 0xf3, 0x42, 0x45, 0x66, 0xff, 0xc4, 0x28, 0xde, 0x85, 0xf3, 0x36, 0x7a, 0xe4, - 0xea, 0x0e, 0x7a, 0xa8, 0x77, 0x91, 0xd9, 0x27, 0xf3, 0x24, 0x7c, 0x0d, 0x44, 0x60, 0x41, 0x4d, - 0xda, 0x84, 0xda, 0x63, 0x7d, 0xed, 0xdb, 0x38, 0xac, 0xcd, 0x06, 0x03, 0xfb, 0x0e, 0xbc, 0x54, - 0x12, 0x35, 0x51, 0xd7, 0xee, 0x55, 0x64, 0xfd, 0x50, 0x29, 0x2b, 0x65, 0xad, 0x2c, 0xee, 0x95, - 0xef, 0xcb, 0x25, 0xfd, 0x50, 0xa9, 0x56, 0x64, 0xa9, 0x7c, 0xab, 0x2c, 0x97, 0x52, 0x2b, 0xdc, - 0xe6, 0x68, 0xcc, 0x27, 0x43, 0x22, 0xf6, 0x1a, 0x5c, 0x0a, 0x2c, 0xa5, 0xbd, 0xb2, 0xac, 0x68, - 0x7a, 0x55, 0x13, 0x35, 0x39, 0xc5, 0x70, 0x30, 0x1a, 0xf3, 0xab, 0x44, 0xc6, 0xbe, 0x0e, 0x5b, - 0x21, 0xbd, 0x03, 0xa5, 0x2a, 0x2b, 0xd5, 0xc3, 0x2a, 0x55, 0x8d, 0x71, 0xe7, 0x47, 0x63, 0x3e, - 0x31, 0x17, 0xb3, 0x79, 0xe0, 0x22, 0xda, 0x8a, 0x2c, 0x69, 0xe5, 0x03, 0x85, 0xaa, 0x9f, 0xe1, - 0x36, 0x46, 0x63, 0x1e, 0x02, 0x39, 0xbb, 0x0d, 0x97, 0x43, 0xfa, 0xb7, 0x45, 0x45, 0x91, 0xf7, - 0xa8, 0x72, 0x9c, 0x4b, 0x8e, 0xc6, 0xfc, 0x39, 0x2a, 0x64, 0xdf, 0x86, 0x2b, 0x81, 0x66, 0x45, - 0x94, 0xee, 0xca, 0x9a, 0x2e, 0x1d, 0xec, 0xef, 0x97, 0xb5, 0x7d, 0x59, 0xd1, 0x52, 0x67, 0xb9, - 0xf4, 0x68, 0xcc, 0xa7, 0x08, 0x10, 0xc8, 0xd9, 0xf7, 0x81, 0x3f, 0x66, 0x26, 0x4a, 0x77, 0x95, - 0x83, 0x4f, 0xf6, 0xe4, 0xd2, 0x87, 0xb2, 0x6f, 0xbb, 0xca, 0x6d, 0x8d, 0xc6, 0xfc, 0x45, 0x82, - 0x2e, 0x80, 0xec, 0x7b, 0xcf, 0x21, 0x50, 0x65, 0x49, 0x2e, 0x57, 0x34, 0x5d, 0x2c, 0x56, 0x65, - 0x45, 0x92, 0x53, 0xe7, 0xb8, 0xcc, 0x68, 0xcc, 0xa7, 0x09, 0x4a, 0x41, 0x8a, 0xb1, 0x37, 0xe0, - 0x6a, 0x60, 0xaf, 0xc8, 0x9f, 0x6a, 0x7a, 0x55, 0xfe, 0xe8, 0xd0, 0x83, 0x3c, 0x9a, 0x8f, 0x53, - 0x6b, 0x24, 0x70, 0x0f, 0x99, 0x01, 0x9e, 0x9c, 0xe5, 0x21, 0x15, 0xd8, 0xdd, 0x96, 0xc5, 0x92, - 0xac, 0xa6, 0x12, 0xa4, 0x32, 0x64, 0xc7, 0xc5, 0x1f, 0xff, 0x98, 0x5d, 0x29, 0x7e, 0xfe, 0xdb, - 0xd3, 0x2c, 0xf3, 0xe4, 0x69, 0x96, 0xf9, 0xf3, 0x69, 0x96, 0xf9, 0xee, 0x59, 0x76, 0xe5, 0xc9, - 0xb3, 0xec, 0xca, 0xef, 0xcf, 0xb2, 0x2b, 0xf7, 0x6f, 0x35, 0x2c, 0xb7, 0xd9, 0xab, 0xe5, 0x4d, - 0xdc, 0x2e, 0x98, 0xd8, 0x69, 0x63, 0xa7, 0x60, 0xd5, 0xcc, 0xeb, 0x0d, 0x5c, 0xe8, 0xbf, 0x55, - 0x68, 0xe3, 0x7a, 0xaf, 0x85, 0x1c, 0xf2, 0x3f, 0x75, 0x7d, 0xf6, 0x43, 0xf5, 0xc6, 0x8d, 0xeb, - 0xe1, 0x7f, 0x2a, 0xef, 0x9a, 0x71, 0x6a, 0xab, 0xfe, 0x3c, 0x7b, 0xf3, 0xaf, 0x00, 0x00, 0x00, - 0xff, 0xff, 0x3f, 0x26, 0x7b, 0x3d, 0x80, 0x0d, 0x00, 0x00, + // 1374 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x57, 0xdf, 0x8e, 0xdb, 0x44, + 0x17, 0x5f, 0xa7, 0xe9, 0x76, 0x33, 0xd9, 0xee, 0xe6, 0x73, 0xd3, 0x36, 0xeb, 0x56, 0x89, 0x3f, + 0x23, 0xca, 0x82, 0x68, 0xcc, 0x2e, 0xa2, 0x45, 0x15, 0x82, 0x3a, 0x8e, 0x4b, 0xd3, 0xee, 0x7a, + 0x83, 0xe3, 0x05, 0x5a, 0x21, 0x59, 0x8e, 0x3d, 0x9b, 0x58, 0x4d, 0x3c, 0x69, 0x3c, 0x49, 0x1a, + 0x24, 0x24, 0xc4, 0x55, 0x89, 0xb8, 0xe0, 0x05, 0x22, 0x21, 0x10, 0xcf, 0xc1, 0x1d, 0x70, 0xd9, + 0x4b, 0xae, 0x02, 0x6a, 0xdf, 0x20, 0x4f, 0x80, 0xec, 0x99, 0xc4, 0x76, 0xb6, 0x9b, 0x15, 0xff, + 0xee, 0x66, 0xce, 0xef, 0x9c, 0xdf, 0x39, 0x73, 0xce, 0xf1, 0x99, 0x31, 0xd8, 0x71, 0xea, 0x96, + 0xd8, 0x72, 0x1a, 0x4d, 0x6c, 0xb5, 0x1c, 0xe8, 0x62, 0x4f, 0xf4, 0x50, 0x0b, 0xb5, 0x4d, 0xab, + 0xe9, 0xb8, 0x50, 0xec, 0xef, 0x46, 0xb7, 0xc5, 0x4e, 0x17, 0x61, 0xc4, 0x16, 0x9c, 0xba, 0x55, + 0x8c, 0x9a, 0x14, 0xa3, 0x3a, 0xfd, 0x5d, 0xee, 0x35, 0x9f, 0xd3, 0x42, 0x5d, 0x28, 0x5a, 0xc8, + 0x75, 0xa1, 0x85, 0x1d, 0xe4, 0x8a, 0xfd, 0x9d, 0xc8, 0x8e, 0x30, 0x71, 0xff, 0x0f, 0x15, 0x9b, + 0xa6, 0xeb, 0xc2, 0x56, 0xa0, 0x45, 0x96, 0x54, 0x25, 0xdb, 0x40, 0x0d, 0x14, 0x2c, 0x45, 0x7f, + 0x45, 0xa5, 0x5b, 0x0d, 0x84, 0x1a, 0x2d, 0x28, 0x06, 0xbb, 0x7a, 0xef, 0x48, 0x34, 0xdd, 0x21, + 0x81, 0x84, 0x9f, 0x12, 0x20, 0x2d, 0x07, 0x71, 0xd5, 0xb0, 0x89, 0x21, 0xcb, 0x81, 0x35, 0x0f, + 0x3e, 0xee, 0x41, 0xd7, 0x82, 0x39, 0x86, 0x67, 0xb6, 0x93, 0xda, 0x7c, 0xcf, 0xee, 0x80, 0x94, + 0xe3, 0x19, 0x47, 0x5d, 0xf4, 0x39, 0x74, 0x73, 0x09, 0x9e, 0xd9, 0x5e, 0x2b, 0x65, 0xa7, 0x93, + 0x42, 0x66, 0x68, 0xb6, 0x5b, 0xb7, 0x84, 0x39, 0x24, 0x68, 0x6b, 0x8e, 0x77, 0x27, 0x58, 0xb2, + 0x18, 0x6c, 0x5a, 0xc8, 0xf5, 0xa0, 0xeb, 0xf5, 0x3c, 0xc3, 0xf3, 0x3d, 0xe4, 0xce, 0xf0, 0xcc, + 0x76, 0x7a, 0x57, 0x2c, 0x9e, 0x92, 0x96, 0xa2, 0x3c, 0xb3, 0x0b, 0x02, 0x2b, 0x71, 0xd3, 0x49, + 0xe1, 0x12, 0xf1, 0xb4, 0xc0, 0x28, 0x68, 0x1b, 0x56, 0x4c, 0x97, 0x85, 0xe0, 0x8a, 0xd9, 0x6a, + 0xa1, 0x81, 0xd1, 0xeb, 0xd8, 0x26, 0x86, 0x86, 0x79, 0x84, 0x61, 0xd7, 0xe8, 0x74, 0x51, 0x07, + 0x79, 0x66, 0x2b, 0x97, 0x0c, 0x42, 0xbf, 0x36, 0x9d, 0x14, 0x04, 0x42, 0xb8, 0x44, 0x59, 0xd0, + 0x72, 0x01, 0x7a, 0x18, 0x80, 0x92, 0x8f, 0x55, 0x29, 0x74, 0x2b, 0xf9, 0xf4, 0xbb, 0xc2, 0x8a, + 0xf0, 0x3d, 0x03, 0x36, 0xe2, 0xb1, 0xb2, 0xf7, 0x00, 0xe8, 0xf4, 0xea, 0x2d, 0xc7, 0x32, 0x1e, + 0xc1, 0x61, 0x90, 0xc6, 0xf4, 0x6e, 0xb6, 0x48, 0x8a, 0x50, 0x9c, 0x15, 0xa1, 0x28, 0xb9, 0xc3, + 0xd2, 0xc5, 0xe9, 0xa4, 0xf0, 0x3f, 0x12, 0x44, 0x68, 0x21, 0x68, 0x29, 0xb2, 0xb9, 0x0f, 0x87, + 0x2c, 0x0f, 0xd2, 0xb6, 0xd3, 0x87, 0x5d, 0xcf, 0x39, 0x72, 0x60, 0x37, 0x48, 0x7b, 0x4a, 0x8b, + 0x8a, 0xd8, 0xab, 0x20, 0x85, 0x9d, 0x36, 0xf4, 0xb0, 0xd9, 0xee, 0x04, 0xd9, 0x4d, 0x6a, 0xa1, + 0x80, 0x06, 0xf9, 0x55, 0x02, 0xac, 0xde, 0x85, 0xa6, 0x0d, 0xbb, 0x4b, 0x2b, 0x1c, 0xa3, 0x4a, + 0x2c, 0x50, 0xf9, 0xa8, 0xe7, 0x34, 0x5c, 0x13, 0xf7, 0xba, 0xa4, 0x8c, 0xeb, 0x5a, 0x28, 0x60, + 0x0f, 0xc1, 0x86, 0x0b, 0x07, 0x46, 0xe4, 0xe0, 0xc9, 0x25, 0x07, 0xdf, 0x9a, 0x4e, 0x0a, 0x17, + 0xc9, 0xc1, 0xe3, 0x56, 0x82, 0xb6, 0xee, 0xc2, 0x41, 0x75, 0x7e, 0x7e, 0x19, 0x6c, 0xfa, 0x0a, + 0xd1, 0x1c, 0x9c, 0xf5, 0x73, 0x10, 0x6d, 0x88, 0x05, 0x05, 0x41, 0xf3, 0x23, 0x29, 0x87, 0x02, + 0x9a, 0x84, 0x5f, 0x12, 0x60, 0x7d, 0xdf, 0xf1, 0xea, 0xb0, 0x69, 0xf6, 0x1d, 0xd4, 0xeb, 0xfa, + 0x0d, 0x4d, 0x9a, 0xcf, 0x70, 0xec, 0x20, 0x17, 0xa9, 0x68, 0x43, 0xcf, 0x21, 0x41, 0x5b, 0x23, + 0xeb, 0x8a, 0x1d, 0xcb, 0x5e, 0x62, 0x21, 0x7b, 0x1d, 0x70, 0x7e, 0x9e, 0x0e, 0x03, 0xb9, 0xb3, + 0x56, 0xdf, 0x39, 0xb5, 0xd5, 0x6b, 0x33, 0x2b, 0xc9, 0xb5, 0xcb, 0x26, 0x36, 0x4b, 0xb9, 0xe9, + 0xa4, 0x90, 0x25, 0x51, 0xc4, 0x18, 0x05, 0x6d, 0x7d, 0xbe, 0x3f, 0x70, 0x17, 0x3c, 0xe2, 0x01, + 0xa2, 0x29, 0xff, 0xb7, 0x3c, 0xe2, 0x01, 0x8a, 0x7a, 0xd4, 0x07, 0x88, 0x66, 0xf2, 0x67, 0x06, + 0x64, 0x16, 0x29, 0xe2, 0xed, 0xc1, 0x2c, 0xb6, 0xc7, 0x67, 0x20, 0x65, 0x9b, 0xd8, 0x34, 0xf0, + 0xb0, 0x43, 0x32, 0xb7, 0xb1, 0xfb, 0xfa, 0xa9, 0x61, 0xfa, 0xbc, 0xfa, 0xb0, 0x03, 0xa3, 0x65, + 0x99, 0xb3, 0x08, 0xda, 0x9a, 0x4d, 0x71, 0x96, 0x05, 0x49, 0x7f, 0x4d, 0xbb, 0x32, 0x58, 0xc7, + 0x9b, 0x39, 0xf9, 0xf2, 0xef, 0xe2, 0x4b, 0x06, 0xe4, 0xf4, 0x99, 0x0c, 0xda, 0xf3, 0x33, 0x05, + 0x07, 0xba, 0x0d, 0x36, 0xc2, 0x5c, 0x04, 0xf4, 0xc1, 0xa9, 0xa2, 0xbd, 0x1b, 0xc7, 0x05, 0x2d, + 0x2c, 0x47, 0xf9, 0x58, 0x08, 0x89, 0x97, 0x87, 0xf0, 0x3b, 0x03, 0x52, 0xbe, 0xdf, 0xd2, 0x10, + 0x43, 0xef, 0x1f, 0x7c, 0x9d, 0x0b, 0x83, 0xe2, 0xcc, 0xf1, 0x41, 0x11, 0x2b, 0x41, 0xf2, 0xbf, + 0x2a, 0xc1, 0xd9, 0xb0, 0x04, 0xf4, 0x84, 0x3f, 0x32, 0x00, 0x90, 0xe1, 0x13, 0x24, 0x65, 0x0f, + 0xa4, 0xe9, 0x27, 0x7f, 0xea, 0x78, 0xbc, 0x34, 0x9d, 0x14, 0xd8, 0xd8, 0x94, 0xa0, 0xf3, 0x91, + 0x8c, 0x88, 0x13, 0xe6, 0x43, 0xe2, 0x6f, 0xce, 0x87, 0x2f, 0xc0, 0x66, 0xe4, 0x2a, 0x0c, 0x62, + 0x65, 0x41, 0xb2, 0x63, 0xe2, 0x26, 0x6d, 0xe7, 0x60, 0xcd, 0x56, 0xc1, 0x3a, 0x1d, 0x0d, 0xe4, + 0x42, 0x4b, 0x2c, 0x39, 0xc0, 0xe5, 0xe9, 0xa4, 0x70, 0x21, 0x36, 0x4e, 0xe8, 0x95, 0x95, 0xb6, + 0x42, 0x4f, 0xd4, 0xfd, 0xd7, 0x0c, 0x60, 0xe3, 0x17, 0xc9, 0x89, 0x21, 0x3c, 0x38, 0x7e, 0xad, + 0x2e, 0x8b, 0xe2, 0x2f, 0xdc, 0x9d, 0x34, 0x96, 0x3e, 0xb8, 0x20, 0xcf, 0x9f, 0x1f, 0xcb, 0x63, + 0x51, 0x00, 0x08, 0x5f, 0x2a, 0x34, 0x8c, 0x57, 0x83, 0xb6, 0xf2, 0x9f, 0x2a, 0xc5, 0xc8, 0x2b, + 0xa6, 0xbf, 0x53, 0x0c, 0x49, 0x15, 0xd7, 0xd6, 0x22, 0x86, 0xd4, 0xaf, 0x0d, 0x32, 0x32, 0x79, + 0xd0, 0x2c, 0x77, 0x7a, 0x03, 0x9c, 0xa3, 0x0f, 0x1f, 0xea, 0xf1, 0x6a, 0xc4, 0x23, 0x7d, 0x11, + 0xf9, 0xee, 0xc8, 0x52, 0x9b, 0x29, 0x53, 0x2f, 0xf7, 0x40, 0xb6, 0x6a, 0x5a, 0x8f, 0x20, 0x96, + 0x51, 0xbb, 0xed, 0xe0, 0x36, 0x74, 0xf1, 0x89, 0x9e, 0xf2, 0xfe, 0xf1, 0x66, 0x5a, 0x81, 0xb3, + 0x75, 0x2d, 0x22, 0x11, 0x1e, 0x80, 0x2d, 0xc2, 0x25, 0x59, 0x8f, 0x5c, 0x34, 0x68, 0x41, 0xbb, + 0x01, 0x97, 0x12, 0x6e, 0x83, 0x4d, 0x33, 0xae, 0x4a, 0x59, 0x17, 0xc5, 0x42, 0x11, 0xe4, 0x08, + 0xb5, 0x06, 0x2d, 0xe8, 0x74, 0xb0, 0x54, 0xf7, 0xfc, 0x39, 0x70, 0x12, 0xb3, 0xd0, 0x04, 0x59, + 0x15, 0x3e, 0xc1, 0x35, 0x3a, 0x2f, 0x34, 0x68, 0xf5, 0x4f, 0x8c, 0xe2, 0x3d, 0x70, 0xde, 0x85, + 0x4f, 0xb0, 0xe1, 0xc1, 0xc7, 0x46, 0x17, 0x5a, 0x7d, 0x32, 0x4f, 0xa2, 0xd7, 0x40, 0x0c, 0x16, + 0xb4, 0xb4, 0x4b, 0xa8, 0x7d, 0xd6, 0x37, 0xbe, 0x49, 0x82, 0xb5, 0xd9, 0x60, 0x60, 0xdf, 0x05, + 0xaf, 0x94, 0x25, 0x5d, 0x32, 0xf4, 0x07, 0x55, 0xc5, 0x38, 0x54, 0x2b, 0x6a, 0x45, 0xaf, 0x48, + 0x7b, 0x95, 0x87, 0x4a, 0xd9, 0x38, 0x54, 0x6b, 0x55, 0x45, 0xae, 0xdc, 0xa9, 0x28, 0xe5, 0xcc, + 0x0a, 0xb7, 0x39, 0x1a, 0xf3, 0xe9, 0x88, 0x88, 0xbd, 0x06, 0x2e, 0x85, 0x96, 0xf2, 0x5e, 0x45, + 0x51, 0x75, 0xa3, 0xa6, 0x4b, 0xba, 0x92, 0x61, 0x38, 0x30, 0x1a, 0xf3, 0xab, 0x44, 0xc6, 0xbe, + 0x09, 0xb6, 0x22, 0x7a, 0x07, 0x6a, 0x4d, 0x51, 0x6b, 0x87, 0x35, 0xaa, 0x9a, 0xe0, 0xce, 0x8f, + 0xc6, 0x7c, 0x6a, 0x2e, 0x66, 0x8b, 0x80, 0x8b, 0x69, 0xab, 0x8a, 0xac, 0x57, 0x0e, 0x54, 0xaa, + 0x7e, 0x86, 0xdb, 0x18, 0x8d, 0x79, 0x10, 0xca, 0xd9, 0x6d, 0x70, 0x39, 0xa2, 0x7f, 0x57, 0x52, + 0x55, 0x65, 0x8f, 0x2a, 0x27, 0xb9, 0xf4, 0x68, 0xcc, 0x9f, 0xa3, 0x42, 0xf6, 0x1d, 0x70, 0x25, + 0xd4, 0xac, 0x4a, 0xf2, 0x7d, 0x45, 0x37, 0xe4, 0x83, 0xfd, 0xfd, 0x8a, 0xbe, 0xaf, 0xa8, 0x7a, + 0xe6, 0x2c, 0x97, 0x1d, 0x8d, 0xf9, 0x0c, 0x01, 0x42, 0x39, 0xfb, 0x01, 0xe0, 0x8f, 0x99, 0x49, + 0xf2, 0x7d, 0xf5, 0xe0, 0x93, 0x3d, 0xa5, 0xfc, 0xa1, 0x12, 0xd8, 0xae, 0x72, 0x5b, 0xa3, 0x31, + 0x7f, 0x91, 0xa0, 0x0b, 0x20, 0xfb, 0xfe, 0x4b, 0x08, 0x34, 0x45, 0x56, 0x2a, 0x55, 0xdd, 0x90, + 0x4a, 0x35, 0x45, 0x95, 0x95, 0xcc, 0x39, 0x2e, 0x37, 0x1a, 0xf3, 0x59, 0x82, 0x52, 0x90, 0x62, + 0xec, 0x0d, 0x70, 0x35, 0xb4, 0x57, 0x95, 0x4f, 0x75, 0xa3, 0xa6, 0x7c, 0x74, 0xe8, 0x43, 0x3e, + 0xcd, 0xc7, 0x99, 0x35, 0x12, 0xb8, 0x8f, 0xcc, 0x00, 0x5f, 0xce, 0xf2, 0x20, 0x13, 0xda, 0xdd, + 0x55, 0xa4, 0xb2, 0xa2, 0x65, 0x52, 0xa4, 0x32, 0x64, 0xc7, 0x25, 0x9f, 0xfe, 0x90, 0x5f, 0x29, + 0x3d, 0xfc, 0xf5, 0x79, 0x9e, 0x79, 0xf6, 0x3c, 0xcf, 0xfc, 0xf1, 0x3c, 0xcf, 0x7c, 0xfb, 0x22, + 0xbf, 0xf2, 0xec, 0x45, 0x7e, 0xe5, 0xb7, 0x17, 0xf9, 0x95, 0x87, 0xb7, 0x1b, 0x0e, 0x6e, 0xf6, + 0xea, 0x45, 0x0b, 0xb5, 0x45, 0x0b, 0x79, 0x6d, 0xe4, 0x89, 0x4e, 0xdd, 0xba, 0xde, 0x40, 0x62, + 0xff, 0xa6, 0xd8, 0x46, 0x76, 0xaf, 0x05, 0x3d, 0xf2, 0x4b, 0xf3, 0xd6, 0xee, 0x75, 0x32, 0x12, + 0xc5, 0xb6, 0xd3, 0xe8, 0x9a, 0xfe, 0x4c, 0xf0, 0xc4, 0xfe, 0xcd, 0xfa, 0x6a, 0x30, 0xc9, 0xde, + 0xfe, 0x33, 0x00, 0x00, 0xff, 0xff, 0xf9, 0x8d, 0x81, 0x56, 0x7a, 0x0d, 0x00, 0x00, } func (m *ClientState) Marshal() (dAtA []byte, err error) { diff --git a/modules/core/02-client/migrations/v7/store.go b/modules/core/02-client/migrations/v7/store.go new file mode 100644 index 00000000000..56afc9e0c8b --- /dev/null +++ b/modules/core/02-client/migrations/v7/store.go @@ -0,0 +1,206 @@ +package v7 + +import ( + "fmt" + "strings" + + errorsmod "cosmossdk.io/errors" + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + storetypes "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" + + ibcerrors "github.com/cosmos/ibc-go/v7/internal/errors" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + "github.com/cosmos/ibc-go/v7/modules/core/exported" + solomachine "github.com/cosmos/ibc-go/v7/modules/light-clients/06-solomachine" + ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" +) + +// Localhost is the client type for a localhost client. It is also used as the clientID +// for the localhost client. +const Localhost string = "09-localhost" + +// MigrateStore performs in-place store migrations from ibc-go v6 to ibc-go v7. +// The migration includes: +// +// - Migrating solo machine client states from v2 to v3 protobuf definition +// - Pruning all solo machine consensus states +// - Removing the localhost client +// - Asserting existing tendermint clients are properly registered on the chain codec +func MigrateStore(ctx sdk.Context, storeKey storetypes.StoreKey, cdc codec.BinaryCodec, clientKeeper ClientKeeper) error { + store := ctx.KVStore(storeKey) + + if err := handleSolomachineMigration(ctx, store, cdc, clientKeeper); err != nil { + return err + } + + if err := handleTendermintMigration(ctx, store, cdc, clientKeeper); err != nil { + return err + } + + if err := handleLocalhostMigration(ctx, store, cdc, clientKeeper); err != nil { + return err + } + + return nil +} + +// handleSolomachineMigration iterates over the solo machine clients and migrates client state from +// protobuf definition v2 to v3. All consensus states stored outside of the client state are pruned. +func handleSolomachineMigration(ctx sdk.Context, store sdk.KVStore, cdc codec.BinaryCodec, clientKeeper ClientKeeper) error { + clients, err := collectClients(ctx, store, exported.Solomachine) + if err != nil { + return err + } + + for _, clientID := range clients { + clientStore := clientKeeper.ClientStore(ctx, clientID) + + bz := clientStore.Get(host.ClientStateKey()) + if len(bz) == 0 { + return errorsmod.Wrapf(clienttypes.ErrClientNotFound, "clientID %s", clientID) + } + + var any codectypes.Any + if err := cdc.Unmarshal(bz, &any); err != nil { + return errorsmod.Wrap(err, "failed to unmarshal client state bytes into solo machine client state") + } + + var clientState ClientState + if err := cdc.Unmarshal(any.Value, &clientState); err != nil { + return errorsmod.Wrap(err, "failed to unmarshal client state bytes into solo machine client state") + } + + updatedClientState := migrateSolomachine(clientState) + + // update solomachine in store + clientKeeper.SetClientState(ctx, clientID, &updatedClientState) + + removeAllClientConsensusStates(clientStore) + } + + return nil +} + +// handlerTendermintMigration asserts that the tendermint client in state can be decoded properly. +// This ensures the upgrading chain properly registered the tendermint client types on the chain codec. +func handleTendermintMigration(ctx sdk.Context, store sdk.KVStore, cdc codec.BinaryCodec, clientKeeper ClientKeeper) error { + clients, err := collectClients(ctx, store, exported.Tendermint) + if err != nil { + return err + } + + if len(clients) == 0 { + return nil // no-op if no tm clients exist + } + + if len(clients) > 1 { + return errorsmod.Wrap(ibcerrors.ErrLogic, "more than one Tendermint client collected") + } + + clientID := clients[0] + + // unregistered tendermint client types will panic when unmarshaling the client state + // in GetClientState + clientState, ok := clientKeeper.GetClientState(ctx, clientID) + if !ok { + return errorsmod.Wrapf(clienttypes.ErrClientNotFound, "clientID %s", clientID) + } + + _, ok = clientState.(*ibctm.ClientState) + if !ok { + return errorsmod.Wrap(clienttypes.ErrInvalidClient, "client state is not tendermint even though client id contains 07-tendermint") + } + + return nil +} + +// handleLocalhostMigration removes all client and consensus states associated with the localhost client type. +func handleLocalhostMigration(ctx sdk.Context, store sdk.KVStore, cdc codec.BinaryCodec, clientKeeper ClientKeeper) error { + clients, err := collectClients(ctx, store, Localhost) + if err != nil { + return err + } + + for _, clientID := range clients { + clientStore := clientKeeper.ClientStore(ctx, clientID) + + // delete the client state + clientStore.Delete(host.ClientStateKey()) + + removeAllClientConsensusStates(clientStore) + } + + return nil +} + +// collectClients will iterate over the provided client type prefix in the client store +// and return a list of clientIDs associated with the client type. This is necessary to +// avoid state corruption as modifying state during iteration is unsafe. A special case +// for tendermint clients is included as only one tendermint clientID is required for +// v7 migrations. +func collectClients(ctx sdk.Context, store sdk.KVStore, clientType string) (clients []string, err error) { + clientPrefix := []byte(fmt.Sprintf("%s/%s", host.KeyClientStorePrefix, clientType)) + iterator := sdk.KVStorePrefixIterator(store, clientPrefix) + + defer sdk.LogDeferred(ctx.Logger(), func() error { return iterator.Close() }) + for ; iterator.Valid(); iterator.Next() { + path := string(iterator.Key()) + if !strings.Contains(path, host.KeyClientState) { + // skip non client state keys + continue + } + + clientID := host.MustParseClientStatePath(path) + clients = append(clients, clientID) + + // optimization: exit after a single tendermint client iteration + if strings.Contains(clientID, exported.Tendermint) { + return clients, nil + } + } + + return clients, nil +} + +// removeAllClientConsensusStates removes all client consensus states from the associated +// client store. +func removeAllClientConsensusStates(clientStore sdk.KVStore) { + iterator := sdk.KVStorePrefixIterator(clientStore, []byte(host.KeyConsensusStatePrefix)) + var heights []exported.Height + + defer iterator.Close() + for ; iterator.Valid(); iterator.Next() { + keySplit := strings.Split(string(iterator.Key()), "/") + // key is in the format "consensusStates/" + if len(keySplit) != 2 || keySplit[0] != string(host.KeyConsensusStatePrefix) { + continue + } + + // collect consensus states to be pruned + heights = append(heights, clienttypes.MustParseHeight(keySplit[1])) + } + + // delete all consensus states + for _, height := range heights { + clientStore.Delete(host.ConsensusStateKey(height)) + } +} + +// migrateSolomachine migrates the solomachine from v2 to v3 solo machine protobuf definition. +// Notably it drops the AllowUpdateAfterProposal field. +func migrateSolomachine(clientState ClientState) solomachine.ClientState { + consensusState := &solomachine.ConsensusState{ + PublicKey: clientState.ConsensusState.PublicKey, + Diversifier: clientState.ConsensusState.Diversifier, + Timestamp: clientState.ConsensusState.Timestamp, + } + + return solomachine.ClientState{ + Sequence: clientState.Sequence, + IsFrozen: clientState.IsFrozen, + ConsensusState: consensusState, + } +} diff --git a/modules/core/02-client/migrations/v7/store_test.go b/modules/core/02-client/migrations/v7/store_test.go new file mode 100644 index 00000000000..a098530af50 --- /dev/null +++ b/modules/core/02-client/migrations/v7/store_test.go @@ -0,0 +1,168 @@ +package v7_test + +import ( + "strconv" + "testing" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/stretchr/testify/suite" + + v7 "github.com/cosmos/ibc-go/v7/modules/core/02-client/migrations/v7" + "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + ibcexported "github.com/cosmos/ibc-go/v7/modules/core/exported" + ibctesting "github.com/cosmos/ibc-go/v7/testing" +) + +// numCreations is the number of clients/consensus states created for +// solo machine and localhost clients +const numCreations = 10 + +type MigrationsV7TestSuite struct { + suite.Suite + + coordinator *ibctesting.Coordinator + + chainA *ibctesting.TestChain + chainB *ibctesting.TestChain +} + +func (suite *MigrationsV7TestSuite) SetupTest() { + suite.coordinator = ibctesting.NewCoordinator(suite.T(), 2) + + suite.chainA = suite.coordinator.GetChain(ibctesting.GetChainID(1)) + suite.chainB = suite.coordinator.GetChain(ibctesting.GetChainID(2)) +} + +func TestIBCTestSuite(t *testing.T) { + suite.Run(t, new(MigrationsV7TestSuite)) +} + +// create multiple solo machine clients, tendermint and localhost clients +// ensure that solo machine clients are migrated and their consensus states are removed +// ensure the localhost is deleted entirely. +func (suite *MigrationsV7TestSuite) TestMigrateStore() { + paths := []*ibctesting.Path{ + ibctesting.NewPath(suite.chainA, suite.chainB), + ibctesting.NewPath(suite.chainA, suite.chainB), + } + + // create tendermint clients + for _, path := range paths { + suite.coordinator.SetupClients(path) + } + + solomachines := []*ibctesting.Solomachine{ + ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "06-solomachine-0", "testing", 1), + ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "06-solomachine-1", "testing", 4), + } + + suite.createSolomachineClients(solomachines) + suite.createLocalhostClients() + + err := v7.MigrateStore(suite.chainA.GetContext(), suite.chainA.GetSimApp().GetKey(ibcexported.StoreKey), suite.chainA.App.AppCodec(), suite.chainA.GetSimApp().IBCKeeper.ClientKeeper) + suite.Require().NoError(err) + + suite.assertSolomachineClients(solomachines) + suite.assertNoLocalhostClients() +} + +func (suite *MigrationsV7TestSuite) TestMigrateStoreNoTendermintClients() { + solomachines := []*ibctesting.Solomachine{ + ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "06-solomachine-0", "testing", 1), + ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "06-solomachine-1", "testing", 4), + } + + suite.createSolomachineClients(solomachines) + suite.createLocalhostClients() + + err := v7.MigrateStore(suite.chainA.GetContext(), suite.chainA.GetSimApp().GetKey(ibcexported.StoreKey), suite.chainA.App.AppCodec(), suite.chainA.GetSimApp().IBCKeeper.ClientKeeper) + suite.Require().NoError(err) + + suite.assertSolomachineClients(solomachines) + suite.assertNoLocalhostClients() +} + +func (suite *MigrationsV7TestSuite) createSolomachineClients(solomachines []*ibctesting.Solomachine) { + // manually generate old protobuf definitions and set in store + // NOTE: we cannot use 'CreateClient' and 'UpdateClient' functions since we are + // using client states and consensus states which do not implement the exported.ClientState + // and exported.ConsensusState interface + for _, sm := range solomachines { + clientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), sm.ClientID) + clientState := sm.ClientState() + + // generate old client state proto definition + legacyClientState := &v7.ClientState{ + Sequence: clientState.Sequence, + ConsensusState: &v7.ConsensusState{ + PublicKey: clientState.ConsensusState.PublicKey, + Diversifier: clientState.ConsensusState.Diversifier, + Timestamp: clientState.ConsensusState.Timestamp, + }, + AllowUpdateAfterProposal: true, + } + + cdc := suite.chainA.App.AppCodec().(*codec.ProtoCodec) + v7.RegisterInterfaces(cdc.InterfaceRegistry()) + + bz, err := cdc.MarshalInterface(legacyClientState) + suite.Require().NoError(err) + clientStore.Set(host.ClientStateKey(), bz) + + bz, err = cdc.MarshalInterface(legacyClientState.ConsensusState) + suite.Require().NoError(err) + + // set some consensus states + for i := uint64(0); i < numCreations; i++ { + height := types.NewHeight(1, i) + clientStore.Set(host.ConsensusStateKey(height), bz) + } + + } +} + +func (suite *MigrationsV7TestSuite) assertSolomachineClients(solomachines []*ibctesting.Solomachine) { + // verify client state has been migrated + for _, sm := range solomachines { + clientState, ok := suite.chainA.App.GetIBCKeeper().ClientKeeper.GetClientState(suite.chainA.GetContext(), sm.ClientID) + suite.Require().True(ok) + suite.Require().Equal(sm.ClientState(), clientState) + + for i := uint64(0); i < numCreations; i++ { + height := types.NewHeight(1, i) + + consState, ok := suite.chainA.App.GetIBCKeeper().ClientKeeper.GetClientConsensusState(suite.chainA.GetContext(), sm.ClientID, height) + suite.Require().False(ok) + suite.Require().Empty(consState) + } + } +} + +// createLocalhostClients clients creates multiple localhost clients and multiple consensus states for each +func (suite *MigrationsV7TestSuite) createLocalhostClients() { + for numClients := uint64(0); numClients < numCreations; numClients++ { + clientID := v7.Localhost + "-" + strconv.FormatUint(numClients, 10) + clientStore := suite.chainA.GetSimApp().IBCKeeper.ClientKeeper.ClientStore(suite.chainA.GetContext(), clientID) + + clientStore.Set(host.ClientStateKey(), []byte("clientState")) + + for i := 0; i < numCreations; i++ { + clientStore.Set(host.ConsensusStateKey(types.NewHeight(1, uint64(i))), []byte("consensusState")) + } + } +} + +// assertLocalhostClients asserts that all localhost information has been deleted +func (suite *MigrationsV7TestSuite) assertNoLocalhostClients() { + for numClients := uint64(0); numClients < numCreations; numClients++ { + clientID := v7.Localhost + "-" + strconv.FormatUint(numClients, 10) + clientStore := suite.chainA.GetSimApp().IBCKeeper.ClientKeeper.ClientStore(suite.chainA.GetContext(), clientID) + + suite.Require().False(clientStore.Has(host.ClientStateKey())) + + for i := uint64(0); i < numCreations; i++ { + suite.Require().False(clientStore.Has(host.ConsensusStateKey(types.NewHeight(1, i)))) + } + } +} diff --git a/modules/core/02-client/module.go b/modules/core/02-client/module.go index 6cef3aac5c0..a9690dcd374 100644 --- a/modules/core/02-client/module.go +++ b/modules/core/02-client/module.go @@ -1,11 +1,11 @@ package client import ( - "github.com/gogo/protobuf/grpc" + "github.com/cosmos/gogoproto/grpc" "github.com/spf13/cobra" - "github.com/cosmos/ibc-go/v4/modules/core/02-client/client/cli" - "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v7/modules/core/02-client/client/cli" + "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" ) // Name returns the IBC client name diff --git a/modules/core/02-client/proposal_handler.go b/modules/core/02-client/proposal_handler.go index fbbdf311b7f..3d9f6002118 100644 --- a/modules/core/02-client/proposal_handler.go +++ b/modules/core/02-client/proposal_handler.go @@ -1,12 +1,13 @@ package client import ( + errorsmod "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" - "github.com/cosmos/ibc-go/v4/modules/core/02-client/keeper" - "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" + ibcerrors "github.com/cosmos/ibc-go/v7/internal/errors" + "github.com/cosmos/ibc-go/v7/modules/core/02-client/keeper" + "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" ) // NewClientProposalHandler defines the 02-client proposal handler @@ -19,7 +20,7 @@ func NewClientProposalHandler(k keeper.Keeper) govtypes.Handler { return k.HandleUpgradeProposal(ctx, c) default: - return sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized ibc proposal content type: %T", c) + return errorsmod.Wrapf(ibcerrors.ErrUnknownRequest, "unrecognized ibc proposal content type: %T", c) } } } diff --git a/modules/core/02-client/proposal_handler_test.go b/modules/core/02-client/proposal_handler_test.go index d86794b7534..3a417197f71 100644 --- a/modules/core/02-client/proposal_handler_test.go +++ b/modules/core/02-client/proposal_handler_test.go @@ -3,12 +3,12 @@ package client_test import ( sdk "github.com/cosmos/cosmos-sdk/types" distributiontypes "github.com/cosmos/cosmos-sdk/x/distribution/types" - govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" - client "github.com/cosmos/ibc-go/v4/modules/core/02-client" - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - ibctmtypes "github.com/cosmos/ibc-go/v4/modules/light-clients/07-tendermint/types" - ibctesting "github.com/cosmos/ibc-go/v4/testing" + client "github.com/cosmos/ibc-go/v7/modules/core/02-client" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" + ibctesting "github.com/cosmos/ibc-go/v7/testing" ) func (suite *ClientTestSuite) TestNewClientUpdateProposalHandler() { @@ -38,14 +38,14 @@ func (suite *ClientTestSuite) TestNewClientUpdateProposalHandler() { suite.Require().NoError(err) substituteClientState := suite.chainA.GetClientState(substitutePath.EndpointA.ClientID) - tmClientState, ok := subjectClientState.(*ibctmtypes.ClientState) + tmClientState, ok := subjectClientState.(*ibctm.ClientState) suite.Require().True(ok) tmClientState.AllowUpdateAfterMisbehaviour = true tmClientState.FrozenHeight = tmClientState.LatestHeight suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), subjectPath.EndpointA.ClientID, tmClientState) // replicate changes to substitute (they must match) - tmClientState, ok = substituteClientState.(*ibctmtypes.ClientState) + tmClientState, ok = substituteClientState.(*ibctm.ClientState) suite.Require().True(ok) tmClientState.AllowUpdateAfterMisbehaviour = true suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), substitutePath.EndpointA.ClientID, tmClientState) @@ -60,7 +60,12 @@ func (suite *ClientTestSuite) TestNewClientUpdateProposalHandler() { }, { "unsupported proposal type", func() { - content = distributiontypes.NewCommunityPoolSpendProposal(ibctesting.Title, ibctesting.Description, suite.chainA.SenderAccount.GetAddress(), sdk.NewCoins(sdk.NewCoin("communityfunds", sdk.NewInt(10)))) + content = &distributiontypes.CommunityPoolSpendProposal{ //nolint:staticcheck + Title: ibctesting.Title, + Description: ibctesting.Description, + Recipient: suite.chainA.SenderAccount.GetAddress().String(), + Amount: sdk.NewCoins(sdk.NewCoin("communityfunds", sdk.NewInt(10))), + } }, false, }, } diff --git a/modules/core/02-client/simulation/decoder.go b/modules/core/02-client/simulation/decoder.go index 9334d1ca01a..2a85cf4a4c4 100644 --- a/modules/core/02-client/simulation/decoder.go +++ b/modules/core/02-client/simulation/decoder.go @@ -6,9 +6,9 @@ import ( "github.com/cosmos/cosmos-sdk/types/kv" - "github.com/cosmos/ibc-go/v4/modules/core/02-client/keeper" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" - "github.com/cosmos/ibc-go/v4/modules/core/exported" + "github.com/cosmos/ibc-go/v7/modules/core/02-client/keeper" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + "github.com/cosmos/ibc-go/v7/modules/core/exported" ) var _ ClientUnmarshaler = (*keeper.Keeper)(nil) diff --git a/modules/core/02-client/simulation/decoder_test.go b/modules/core/02-client/simulation/decoder_test.go index fcd6e847475..0fbaef2693c 100644 --- a/modules/core/02-client/simulation/decoder_test.go +++ b/modules/core/02-client/simulation/decoder_test.go @@ -8,11 +8,11 @@ import ( "github.com/cosmos/cosmos-sdk/types/kv" "github.com/stretchr/testify/require" - "github.com/cosmos/ibc-go/v4/modules/core/02-client/simulation" - "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" - ibctmtypes "github.com/cosmos/ibc-go/v4/modules/light-clients/07-tendermint/types" - "github.com/cosmos/ibc-go/v4/testing/simapp" + "github.com/cosmos/ibc-go/v7/modules/core/02-client/simulation" + "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" + "github.com/cosmos/ibc-go/v7/testing/simapp" ) func TestDecodeStore(t *testing.T) { @@ -21,11 +21,11 @@ func TestDecodeStore(t *testing.T) { height := types.NewHeight(0, 10) - clientState := &ibctmtypes.ClientState{ + clientState := &ibctm.ClientState{ FrozenHeight: height, } - consState := &ibctmtypes.ConsensusState{ + consState := &ibctm.ConsensusState{ Timestamp: time.Now().UTC(), } diff --git a/modules/core/02-client/simulation/genesis.go b/modules/core/02-client/simulation/genesis.go index f4c6082628a..161efd74fcf 100644 --- a/modules/core/02-client/simulation/genesis.go +++ b/modules/core/02-client/simulation/genesis.go @@ -5,7 +5,7 @@ import ( simtypes "github.com/cosmos/cosmos-sdk/types/simulation" - "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" ) // GenClientGenesis returns the default client genesis state. diff --git a/modules/core/02-client/types/client.go b/modules/core/02-client/types/client.go index a09a910e815..86039dc5847 100644 --- a/modules/core/02-client/types/client.go +++ b/modules/core/02-client/types/client.go @@ -6,12 +6,12 @@ import ( "sort" "strings" + errorsmod "cosmossdk.io/errors" codectypes "github.com/cosmos/cosmos-sdk/codec/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - proto "github.com/gogo/protobuf/proto" + proto "github.com/cosmos/gogoproto/proto" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" - "github.com/cosmos/ibc-go/v4/modules/core/exported" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + "github.com/cosmos/ibc-go/v7/modules/core/exported" ) var ( @@ -89,7 +89,7 @@ func (cswh ConsensusStateWithHeight) UnpackInterfaces(unpacker codectypes.AnyUnp // client identifier when used with '0' or the maximum uint64 as the sequence. func ValidateClientType(clientType string) error { if strings.TrimSpace(clientType) == "" { - return sdkerrors.Wrap(ErrInvalidClientType, "client type cannot be blank") + return errorsmod.Wrap(ErrInvalidClientType, "client type cannot be blank") } smallestPossibleClientID := FormatClientIdentifier(clientType, 0) @@ -97,14 +97,14 @@ func ValidateClientType(clientType string) error { // IsValidClientID will check client type format and if the sequence is a uint64 if !IsValidClientID(smallestPossibleClientID) { - return sdkerrors.Wrap(ErrInvalidClientType, "") + return errorsmod.Wrap(ErrInvalidClientType, "") } if err := host.ClientIdentifierValidator(smallestPossibleClientID); err != nil { - return sdkerrors.Wrap(err, "client type results in smallest client identifier being invalid") + return errorsmod.Wrap(err, "client type results in smallest client identifier being invalid") } if err := host.ClientIdentifierValidator(largestPossibleClientID); err != nil { - return sdkerrors.Wrap(err, "client type results in largest client identifier being invalid") + return errorsmod.Wrap(err, "client type results in largest client identifier being invalid") } return nil diff --git a/modules/core/02-client/types/client.pb.go b/modules/core/02-client/types/client.pb.go index cf3d0fd93a9..6b4f4351245 100644 --- a/modules/core/02-client/types/client.pb.go +++ b/modules/core/02-client/types/client.pb.go @@ -5,10 +5,11 @@ package types import ( fmt "fmt" + _ "github.com/cosmos/cosmos-proto" types "github.com/cosmos/cosmos-sdk/codec/types" types1 "github.com/cosmos/cosmos-sdk/x/upgrade/types" - _ "github.com/gogo/protobuf/gogoproto" - proto "github.com/gogo/protobuf/proto" + _ "github.com/cosmos/gogoproto/gogoproto" + proto "github.com/cosmos/gogoproto/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, 0x48, 0x10, 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, 0x44, 0x95, 0x92, 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, 0xdc, 0x07, 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, 0x7e, 0x60, 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, 0x3d, 0x69, 0x32, 0x4f, 0x37, 0x06, 0x00, 0x00, } func (this *UpgradeProposal) Equal(that interface{}) bool { diff --git a/modules/core/02-client/types/client_test.go b/modules/core/02-client/types/client_test.go index dfc52f2190b..ecdfd1a31e3 100644 --- a/modules/core/02-client/types/client_test.go +++ b/modules/core/02-client/types/client_test.go @@ -5,8 +5,8 @@ import ( "github.com/stretchr/testify/require" - "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - ibctesting "github.com/cosmos/ibc-go/v4/testing" + "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + ibctesting "github.com/cosmos/ibc-go/v7/testing" ) func (suite *TypesTestSuite) TestMarshalConsensusStateWithHeight() { diff --git a/modules/core/02-client/types/codec.go b/modules/core/02-client/types/codec.go index 0aec6bc1d7c..766f414e376 100644 --- a/modules/core/02-client/types/codec.go +++ b/modules/core/02-client/types/codec.go @@ -1,14 +1,15 @@ package types import ( + errorsmod "cosmossdk.io/errors" codectypes "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/types/msgservice" - govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" - proto "github.com/gogo/protobuf/proto" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" + proto "github.com/cosmos/gogoproto/proto" - "github.com/cosmos/ibc-go/v4/modules/core/exported" + ibcerrors "github.com/cosmos/ibc-go/v7/internal/errors" + "github.com/cosmos/ibc-go/v7/modules/core/exported" ) // RegisterInterfaces registers the client interfaces to protobuf Any. @@ -23,7 +24,7 @@ func RegisterInterfaces(registry codectypes.InterfaceRegistry) { ) registry.RegisterInterface( "ibc.core.client.v1.Header", - (*exported.Header)(nil), + (*exported.ClientMessage)(nil), ) registry.RegisterInterface( "ibc.core.client.v1.Height", @@ -32,7 +33,7 @@ func RegisterInterfaces(registry codectypes.InterfaceRegistry) { ) registry.RegisterInterface( "ibc.core.client.v1.Misbehaviour", - (*exported.Misbehaviour)(nil), + (*exported.ClientMessage)(nil), ) registry.RegisterImplementations( (*govtypes.Content)(nil), @@ -56,12 +57,12 @@ func RegisterInterfaces(registry codectypes.InterfaceRegistry) { func PackClientState(clientState exported.ClientState) (*codectypes.Any, error) { msg, ok := clientState.(proto.Message) if !ok { - return nil, sdkerrors.Wrapf(sdkerrors.ErrPackAny, "cannot proto marshal %T", clientState) + return nil, errorsmod.Wrapf(ibcerrors.ErrPackAny, "cannot proto marshal %T", clientState) } anyClientState, err := codectypes.NewAnyWithValue(msg) if err != nil { - return nil, sdkerrors.Wrap(sdkerrors.ErrPackAny, err.Error()) + return nil, errorsmod.Wrap(ibcerrors.ErrPackAny, err.Error()) } return anyClientState, nil @@ -71,12 +72,12 @@ func PackClientState(clientState exported.ClientState) (*codectypes.Any, error) // client state can't be unpacked into a ClientState. func UnpackClientState(any *codectypes.Any) (exported.ClientState, error) { if any == nil { - return nil, sdkerrors.Wrap(sdkerrors.ErrUnpackAny, "protobuf Any message cannot be nil") + return nil, errorsmod.Wrap(ibcerrors.ErrUnpackAny, "protobuf Any message cannot be nil") } clientState, ok := any.GetCachedValue().(exported.ClientState) if !ok { - return nil, sdkerrors.Wrapf(sdkerrors.ErrUnpackAny, "cannot unpack Any into ClientState %T", any) + return nil, errorsmod.Wrapf(ibcerrors.ErrUnpackAny, "cannot unpack Any into ClientState %T", any) } return clientState, nil @@ -88,12 +89,12 @@ func UnpackClientState(any *codectypes.Any) (exported.ClientState, error) { func PackConsensusState(consensusState exported.ConsensusState) (*codectypes.Any, error) { msg, ok := consensusState.(proto.Message) if !ok { - return nil, sdkerrors.Wrapf(sdkerrors.ErrPackAny, "cannot proto marshal %T", consensusState) + return nil, errorsmod.Wrapf(ibcerrors.ErrPackAny, "cannot proto marshal %T", consensusState) } anyConsensusState, err := codectypes.NewAnyWithValue(msg) if err != nil { - return nil, sdkerrors.Wrap(sdkerrors.ErrPackAny, err.Error()) + return nil, errorsmod.Wrap(ibcerrors.ErrPackAny, err.Error()) } return anyConsensusState, nil @@ -113,77 +114,45 @@ func MustPackConsensusState(consensusState exported.ConsensusState) *codectypes. // consensus state can't be unpacked into a ConsensusState. func UnpackConsensusState(any *codectypes.Any) (exported.ConsensusState, error) { if any == nil { - return nil, sdkerrors.Wrap(sdkerrors.ErrUnpackAny, "protobuf Any message cannot be nil") + return nil, errorsmod.Wrap(ibcerrors.ErrUnpackAny, "protobuf Any message cannot be nil") } consensusState, ok := any.GetCachedValue().(exported.ConsensusState) if !ok { - return nil, sdkerrors.Wrapf(sdkerrors.ErrUnpackAny, "cannot unpack Any into ConsensusState %T", any) + return nil, errorsmod.Wrapf(ibcerrors.ErrUnpackAny, "cannot unpack Any into ConsensusState %T", any) } return consensusState, nil } -// PackHeader constructs a new Any packed with the given header value. It returns -// an error if the header can't be casted to a protobuf message or if the concrete +// PackClientMessage constructs a new Any packed with the given value. It returns +// an error if the value can't be casted to a protobuf message or if the concrete // implemention is not registered to the protobuf codec. -func PackHeader(header exported.Header) (*codectypes.Any, error) { - msg, ok := header.(proto.Message) +func PackClientMessage(clientMessage exported.ClientMessage) (*codectypes.Any, error) { + msg, ok := clientMessage.(proto.Message) if !ok { - return nil, sdkerrors.Wrapf(sdkerrors.ErrPackAny, "cannot proto marshal %T", header) + return nil, errorsmod.Wrapf(ibcerrors.ErrPackAny, "cannot proto marshal %T", clientMessage) } - anyHeader, err := codectypes.NewAnyWithValue(msg) + protoAny, err := codectypes.NewAnyWithValue(msg) if err != nil { - return nil, sdkerrors.Wrap(sdkerrors.ErrPackAny, err.Error()) + return nil, errorsmod.Wrap(ibcerrors.ErrPackAny, err.Error()) } - return anyHeader, nil + return protoAny, nil } -// UnpackHeader unpacks an Any into a Header. It returns an error if the -// consensus state can't be unpacked into a Header. -func UnpackHeader(any *codectypes.Any) (exported.Header, error) { +// UnpackClientMessage unpacks an Any into a ClientMessage. It returns an error if the +// consensus state can't be unpacked into a ClientMessage. +func UnpackClientMessage(any *codectypes.Any) (exported.ClientMessage, error) { if any == nil { - return nil, sdkerrors.Wrap(sdkerrors.ErrUnpackAny, "protobuf Any message cannot be nil") + return nil, errorsmod.Wrap(ibcerrors.ErrUnpackAny, "protobuf Any message cannot be nil") } - header, ok := any.GetCachedValue().(exported.Header) + clientMessage, ok := any.GetCachedValue().(exported.ClientMessage) if !ok { - return nil, sdkerrors.Wrapf(sdkerrors.ErrUnpackAny, "cannot unpack Any into Header %T", any) + return nil, errorsmod.Wrapf(ibcerrors.ErrUnpackAny, "cannot unpack Any into Header %T", any) } - return header, nil -} - -// PackMisbehaviour constructs a new Any packed with the given misbehaviour value. It returns -// an error if the misbehaviour can't be casted to a protobuf message or if the concrete -// implemention is not registered to the protobuf codec. -func PackMisbehaviour(misbehaviour exported.Misbehaviour) (*codectypes.Any, error) { - msg, ok := misbehaviour.(proto.Message) - if !ok { - return nil, sdkerrors.Wrapf(sdkerrors.ErrPackAny, "cannot proto marshal %T", misbehaviour) - } - - anyMisbhaviour, err := codectypes.NewAnyWithValue(msg) - if err != nil { - return nil, sdkerrors.Wrap(sdkerrors.ErrPackAny, err.Error()) - } - - return anyMisbhaviour, nil -} - -// UnpackMisbehaviour unpacks an Any into a Misbehaviour. It returns an error if the -// Any can't be unpacked into a Misbehaviour. -func UnpackMisbehaviour(any *codectypes.Any) (exported.Misbehaviour, error) { - if any == nil { - return nil, sdkerrors.Wrap(sdkerrors.ErrUnpackAny, "protobuf Any message cannot be nil") - } - - misbehaviour, ok := any.GetCachedValue().(exported.Misbehaviour) - if !ok { - return nil, sdkerrors.Wrapf(sdkerrors.ErrUnpackAny, "cannot unpack Any into Misbehaviour %T", any) - } - - return misbehaviour, nil + return clientMessage, nil } diff --git a/modules/core/02-client/types/codec_test.go b/modules/core/02-client/types/codec_test.go index da970cd181a..28e22b04e0d 100644 --- a/modules/core/02-client/types/codec_test.go +++ b/modules/core/02-client/types/codec_test.go @@ -3,12 +3,11 @@ package types_test import ( codectypes "github.com/cosmos/cosmos-sdk/codec/types" - "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - commitmenttypes "github.com/cosmos/ibc-go/v4/modules/core/23-commitment/types" - "github.com/cosmos/ibc-go/v4/modules/core/exported" - ibctmtypes "github.com/cosmos/ibc-go/v4/modules/light-clients/07-tendermint/types" - localhosttypes "github.com/cosmos/ibc-go/v4/modules/light-clients/09-localhost/types" - ibctesting "github.com/cosmos/ibc-go/v4/testing" + "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + commitmenttypes "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/types" + "github.com/cosmos/ibc-go/v7/modules/core/exported" + ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" + ibctesting "github.com/cosmos/ibc-go/v7/testing" ) type caseAny struct { @@ -30,12 +29,7 @@ func (suite *TypesTestSuite) TestPackClientState() { }, { "tendermint client", - ibctmtypes.NewClientState(chainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), - true, - }, - { - "localhost client", - localhosttypes.NewClientState(chainID, clientHeight), + ibctm.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath), true, }, { @@ -48,14 +42,14 @@ func (suite *TypesTestSuite) TestPackClientState() { testCasesAny := []caseAny{} for _, tc := range testCases { - clientAny, err := types.PackClientState(tc.clientState) + protoAny, err := types.PackClientState(tc.clientState) if tc.expPass { suite.Require().NoError(err, tc.name) } else { suite.Require().Error(err, tc.name) } - testCasesAny = append(testCasesAny, caseAny{tc.name, clientAny, tc.expPass}) + testCasesAny = append(testCasesAny, caseAny{tc.name, protoAny, tc.expPass}) } for i, tc := range testCasesAny { @@ -95,13 +89,13 @@ func (suite *TypesTestSuite) TestPackConsensusState() { testCasesAny := []caseAny{} for _, tc := range testCases { - clientAny, err := types.PackConsensusState(tc.consensusState) + protoAny, err := types.PackConsensusState(tc.consensusState) if tc.expPass { suite.Require().NoError(err, tc.name) } else { suite.Require().Error(err, tc.name) } - testCasesAny = append(testCasesAny, caseAny{tc.name, clientAny, tc.expPass}) + testCasesAny = append(testCasesAny, caseAny{tc.name, protoAny, tc.expPass}) } for i, tc := range testCasesAny { @@ -115,15 +109,15 @@ func (suite *TypesTestSuite) TestPackConsensusState() { } } -func (suite *TypesTestSuite) TestPackHeader() { +func (suite *TypesTestSuite) TestPackClientMessage() { testCases := []struct { - name string - header exported.Header - expPass bool + name string + clientMessage exported.ClientMessage + expPass bool }{ { "solo machine header", - ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachine", "", 2).CreateHeader(), + ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachine", "", 2).CreateHeader("solomachine"), true, }, { @@ -141,68 +135,21 @@ func (suite *TypesTestSuite) TestPackHeader() { testCasesAny := []caseAny{} for _, tc := range testCases { - clientAny, err := types.PackHeader(tc.header) - if tc.expPass { - suite.Require().NoError(err, tc.name) - } else { - suite.Require().Error(err, tc.name) - } - - testCasesAny = append(testCasesAny, caseAny{tc.name, clientAny, tc.expPass}) - } - - for i, tc := range testCasesAny { - cs, err := types.UnpackHeader(tc.any) - if tc.expPass { - suite.Require().NoError(err, tc.name) - suite.Require().Equal(testCases[i].header, cs, tc.name) - } else { - suite.Require().Error(err, tc.name) - } - } -} - -func (suite *TypesTestSuite) TestPackMisbehaviour() { - testCases := []struct { - name string - misbehaviour exported.Misbehaviour - expPass bool - }{ - { - "solo machine misbehaviour", - ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachine", "", 2).CreateMisbehaviour(), - true, - }, - { - "tendermint misbehaviour", - ibctmtypes.NewMisbehaviour("tendermint", suite.chainA.LastHeader, suite.chainA.LastHeader), - true, - }, - { - "nil", - nil, - false, - }, - } - - testCasesAny := []caseAny{} - - for _, tc := range testCases { - clientAny, err := types.PackMisbehaviour(tc.misbehaviour) + protoAny, err := types.PackClientMessage(tc.clientMessage) if tc.expPass { suite.Require().NoError(err, tc.name) } else { suite.Require().Error(err, tc.name) } - testCasesAny = append(testCasesAny, caseAny{tc.name, clientAny, tc.expPass}) + testCasesAny = append(testCasesAny, caseAny{tc.name, protoAny, tc.expPass}) } for i, tc := range testCasesAny { - cs, err := types.UnpackMisbehaviour(tc.any) + cs, err := types.UnpackClientMessage(tc.any) if tc.expPass { suite.Require().NoError(err, tc.name) - suite.Require().Equal(testCases[i].misbehaviour, cs, tc.name) + suite.Require().Equal(testCases[i].clientMessage, cs, tc.name) } else { suite.Require().Error(err, tc.name) } diff --git a/modules/core/02-client/types/encoding.go b/modules/core/02-client/types/encoding.go index 26a6bf32626..63a544ec85a 100644 --- a/modules/core/02-client/types/encoding.go +++ b/modules/core/02-client/types/encoding.go @@ -5,7 +5,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/ibc-go/v4/modules/core/exported" + "github.com/cosmos/ibc-go/v7/modules/core/exported" ) // MustUnmarshalClientState attempts to decode and return an ClientState object from @@ -86,29 +86,29 @@ func UnmarshalConsensusState(cdc codec.BinaryCodec, bz []byte) (exported.Consens return consensusState, nil } -// MarshalHeader protobuf serializes a Header interface -func MarshalHeader(cdc codec.BinaryCodec, h exported.Header) ([]byte, error) { - return cdc.MarshalInterface(h) +// MarshalClientMessage protobuf serializes a ClientMessage interface +func MarshalClientMessage(cdc codec.BinaryCodec, clientMessage exported.ClientMessage) ([]byte, error) { + return cdc.MarshalInterface(clientMessage) } -// MustMarshalHeader attempts to encode a Header object and returns the +// MustMarshalClientMessage attempts to encode a ClientMessage object and returns the // raw encoded bytes. It panics on error. -func MustMarshalHeader(cdc codec.BinaryCodec, header exported.Header) []byte { - bz, err := MarshalHeader(cdc, header) +func MustMarshalClientMessage(cdc codec.BinaryCodec, clientMessage exported.ClientMessage) []byte { + bz, err := MarshalClientMessage(cdc, clientMessage) if err != nil { - panic(fmt.Errorf("failed to encode header: %w", err)) + panic(fmt.Errorf("failed to encode ClientMessage: %w", err)) } return bz } -// UnmarshalHeader returns a Header interface from raw proto encoded header bytes. +// UnmarshalClientMessage returns a ClientMessage interface from raw proto encoded header bytes. // An error is returned upon decoding failure. -func UnmarshalHeader(cdc codec.BinaryCodec, bz []byte) (exported.Header, error) { - var header exported.Header - if err := cdc.UnmarshalInterface(bz, &header); err != nil { +func UnmarshalClientMessage(cdc codec.BinaryCodec, bz []byte) (exported.ClientMessage, error) { + var clientMessage exported.ClientMessage + if err := cdc.UnmarshalInterface(bz, &clientMessage); err != nil { return nil, err } - return header, nil + return clientMessage, nil } diff --git a/modules/core/02-client/types/encoding_test.go b/modules/core/02-client/types/encoding_test.go index 298285a828f..4611d4cf945 100644 --- a/modules/core/02-client/types/encoding_test.go +++ b/modules/core/02-client/types/encoding_test.go @@ -1,28 +1,28 @@ package types_test import ( - "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - ibctmtypes "github.com/cosmos/ibc-go/v4/modules/light-clients/07-tendermint/types" + "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" ) func (suite *TypesTestSuite) TestMarshalHeader() { cdc := suite.chainA.App.AppCodec() - h := &ibctmtypes.Header{ + h := &ibctm.Header{ TrustedHeight: types.NewHeight(4, 100), } // marshal header - bz, err := types.MarshalHeader(cdc, h) + bz, err := types.MarshalClientMessage(cdc, h) suite.Require().NoError(err) // unmarshal header - newHeader, err := types.UnmarshalHeader(cdc, bz) + newHeader, err := types.UnmarshalClientMessage(cdc, bz) suite.Require().NoError(err) suite.Require().Equal(h, newHeader) // use invalid bytes - invalidHeader, err := types.UnmarshalHeader(cdc, []byte("invalid bytes")) + invalidHeader, err := types.UnmarshalClientMessage(cdc, []byte("invalid bytes")) suite.Require().Error(err) suite.Require().Nil(invalidHeader) } diff --git a/modules/core/02-client/types/errors.go b/modules/core/02-client/types/errors.go index c40bae878cc..a0fa91e8dac 100644 --- a/modules/core/02-client/types/errors.go +++ b/modules/core/02-client/types/errors.go @@ -1,37 +1,37 @@ package types import ( - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + errorsmod "cosmossdk.io/errors" ) // IBC client sentinel errors var ( - ErrClientExists = sdkerrors.Register(SubModuleName, 2, "light client already exists") - ErrInvalidClient = sdkerrors.Register(SubModuleName, 3, "light client is invalid") - ErrClientNotFound = sdkerrors.Register(SubModuleName, 4, "light client not found") - ErrClientFrozen = sdkerrors.Register(SubModuleName, 5, "light client is frozen due to misbehaviour") - ErrInvalidClientMetadata = sdkerrors.Register(SubModuleName, 6, "invalid client metadata") - ErrConsensusStateNotFound = sdkerrors.Register(SubModuleName, 7, "consensus state not found") - ErrInvalidConsensus = sdkerrors.Register(SubModuleName, 8, "invalid consensus state") - ErrClientTypeNotFound = sdkerrors.Register(SubModuleName, 9, "client type not found") - ErrInvalidClientType = sdkerrors.Register(SubModuleName, 10, "invalid client type") - ErrRootNotFound = sdkerrors.Register(SubModuleName, 11, "commitment root not found") - ErrInvalidHeader = sdkerrors.Register(SubModuleName, 12, "invalid client header") - ErrInvalidMisbehaviour = sdkerrors.Register(SubModuleName, 13, "invalid light client misbehaviour") - ErrFailedClientStateVerification = sdkerrors.Register(SubModuleName, 14, "client state verification failed") - ErrFailedClientConsensusStateVerification = sdkerrors.Register(SubModuleName, 15, "client consensus state verification failed") - ErrFailedConnectionStateVerification = sdkerrors.Register(SubModuleName, 16, "connection state verification failed") - ErrFailedChannelStateVerification = sdkerrors.Register(SubModuleName, 17, "channel state verification failed") - ErrFailedPacketCommitmentVerification = sdkerrors.Register(SubModuleName, 18, "packet commitment verification failed") - ErrFailedPacketAckVerification = sdkerrors.Register(SubModuleName, 19, "packet acknowledgement verification failed") - ErrFailedPacketReceiptVerification = sdkerrors.Register(SubModuleName, 20, "packet receipt verification failed") - ErrFailedNextSeqRecvVerification = sdkerrors.Register(SubModuleName, 21, "next sequence receive verification failed") - ErrSelfConsensusStateNotFound = sdkerrors.Register(SubModuleName, 22, "self consensus state not found") - ErrUpdateClientFailed = sdkerrors.Register(SubModuleName, 23, "unable to update light client") - ErrInvalidUpdateClientProposal = sdkerrors.Register(SubModuleName, 24, "invalid update client proposal") - ErrInvalidUpgradeClient = sdkerrors.Register(SubModuleName, 25, "invalid client upgrade") - ErrInvalidHeight = sdkerrors.Register(SubModuleName, 26, "invalid height") - ErrInvalidSubstitute = sdkerrors.Register(SubModuleName, 27, "invalid client state substitute") - ErrInvalidUpgradeProposal = sdkerrors.Register(SubModuleName, 28, "invalid upgrade proposal") - ErrClientNotActive = sdkerrors.Register(SubModuleName, 29, "client is not active") + ErrClientExists = errorsmod.Register(SubModuleName, 2, "light client already exists") + ErrInvalidClient = errorsmod.Register(SubModuleName, 3, "light client is invalid") + ErrClientNotFound = errorsmod.Register(SubModuleName, 4, "light client not found") + ErrClientFrozen = errorsmod.Register(SubModuleName, 5, "light client is frozen due to misbehaviour") + ErrInvalidClientMetadata = errorsmod.Register(SubModuleName, 6, "invalid client metadata") + ErrConsensusStateNotFound = errorsmod.Register(SubModuleName, 7, "consensus state not found") + ErrInvalidConsensus = errorsmod.Register(SubModuleName, 8, "invalid consensus state") + ErrClientTypeNotFound = errorsmod.Register(SubModuleName, 9, "client type not found") + ErrInvalidClientType = errorsmod.Register(SubModuleName, 10, "invalid client type") + ErrRootNotFound = errorsmod.Register(SubModuleName, 11, "commitment root not found") + ErrInvalidHeader = errorsmod.Register(SubModuleName, 12, "invalid client header") + ErrInvalidMisbehaviour = errorsmod.Register(SubModuleName, 13, "invalid light client misbehaviour") + ErrFailedClientStateVerification = errorsmod.Register(SubModuleName, 14, "client state verification failed") + ErrFailedClientConsensusStateVerification = errorsmod.Register(SubModuleName, 15, "client consensus state verification failed") + ErrFailedConnectionStateVerification = errorsmod.Register(SubModuleName, 16, "connection state verification failed") + ErrFailedChannelStateVerification = errorsmod.Register(SubModuleName, 17, "channel state verification failed") + ErrFailedPacketCommitmentVerification = errorsmod.Register(SubModuleName, 18, "packet commitment verification failed") + ErrFailedPacketAckVerification = errorsmod.Register(SubModuleName, 19, "packet acknowledgement verification failed") + ErrFailedPacketReceiptVerification = errorsmod.Register(SubModuleName, 20, "packet receipt verification failed") + ErrFailedNextSeqRecvVerification = errorsmod.Register(SubModuleName, 21, "next sequence receive verification failed") + ErrSelfConsensusStateNotFound = errorsmod.Register(SubModuleName, 22, "self consensus state not found") + ErrUpdateClientFailed = errorsmod.Register(SubModuleName, 23, "unable to update light client") + ErrInvalidUpdateClientProposal = errorsmod.Register(SubModuleName, 24, "invalid update client proposal") + ErrInvalidUpgradeClient = errorsmod.Register(SubModuleName, 25, "invalid client upgrade") + ErrInvalidHeight = errorsmod.Register(SubModuleName, 26, "invalid height") + ErrInvalidSubstitute = errorsmod.Register(SubModuleName, 27, "invalid client state substitute") + ErrInvalidUpgradeProposal = errorsmod.Register(SubModuleName, 28, "invalid upgrade proposal") + ErrClientNotActive = errorsmod.Register(SubModuleName, 29, "client state is not active") ) diff --git a/modules/core/02-client/types/events.go b/modules/core/02-client/types/events.go index d23f6d24e02..9671cf9dcb9 100644 --- a/modules/core/02-client/types/events.go +++ b/modules/core/02-client/types/events.go @@ -3,7 +3,7 @@ package types import ( "fmt" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" + ibcexported "github.com/cosmos/ibc-go/v7/modules/core/exported" ) // IBC client events @@ -12,9 +12,11 @@ const ( AttributeKeySubjectClientID = "subject_client_id" AttributeKeyClientType = "client_type" AttributeKeyConsensusHeight = "consensus_height" + AttributeKeyConsensusHeights = "consensus_heights" AttributeKeyHeader = "header" + AttributeKeyUpgradeStore = "upgrade_store" + AttributeKeyUpgradePlanHeight = "upgrade_plan_height" AttributeKeyUpgradePlanTitle = "title" - AttributeKeyUpgradePlanHeight = "height" ) // IBC client events vars @@ -24,7 +26,8 @@ var ( EventTypeUpgradeClient = "upgrade_client" EventTypeSubmitMisbehaviour = "client_misbehaviour" EventTypeUpdateClientProposal = "update_client_proposal" + EventTypeUpgradeChain = "upgrade_chain" EventTypeUpgradeClientProposal = "upgrade_client_proposal" - AttributeValueCategory = fmt.Sprintf("%s_%s", host.ModuleName, SubModuleName) + AttributeValueCategory = fmt.Sprintf("%s_%s", ibcexported.ModuleName, SubModuleName) ) diff --git a/modules/core/02-client/types/genesis.go b/modules/core/02-client/types/genesis.go index 4dd61fdc7a2..f2ada0f55de 100644 --- a/modules/core/02-client/types/genesis.go +++ b/modules/core/02-client/types/genesis.go @@ -6,8 +6,8 @@ import ( codectypes "github.com/cosmos/cosmos-sdk/codec/types" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" - "github.com/cosmos/ibc-go/v4/modules/core/exported" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + "github.com/cosmos/ibc-go/v7/modules/core/exported" ) var ( @@ -110,7 +110,7 @@ func (gs GenesisState) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { func (gs GenesisState) Validate() error { // keep track of the max sequence to ensure it is less than // the next sequence used in creating client identifers. - var maxSequence uint64 = 0 + var maxSequence uint64 if err := gs.Params.Validate(); err != nil { return err @@ -200,10 +200,6 @@ func (gs GenesisState) Validate() error { } - if gs.CreateLocalhost && !gs.Params.IsAllowedClient(exported.Localhost) { - return fmt.Errorf("localhost client is not registered on the allowlist") - } - if maxSequence != 0 && maxSequence >= gs.NextClientSequence { return fmt.Errorf("next client identifier sequence %d must be greater than the maximum sequence used in the provided client identifiers %d", gs.NextClientSequence, maxSequence) } diff --git a/modules/core/02-client/types/genesis.pb.go b/modules/core/02-client/types/genesis.pb.go index 4edc4f302b8..d6d4fad8cd5 100644 --- a/modules/core/02-client/types/genesis.pb.go +++ b/modules/core/02-client/types/genesis.pb.go @@ -5,8 +5,8 @@ package types import ( fmt "fmt" - _ "github.com/gogo/protobuf/gogoproto" - proto "github.com/gogo/protobuf/proto" + _ "github.com/cosmos/gogoproto/gogoproto" + proto "github.com/cosmos/gogoproto/proto" io "io" math "math" math_bits "math/bits" @@ -221,7 +221,7 @@ var fileDescriptor_bcd0c0f1f2e6a91a = []byte{ // 539 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x53, 0x41, 0x6e, 0xd3, 0x40, 0x14, 0xcd, 0x34, 0x69, 0x68, 0xa7, 0x15, 0x0d, 0xa3, 0xa8, 0x98, 0x54, 0xb2, 0x2d, 0xb3, 0x09, - 0x8b, 0xd8, 0x24, 0xb0, 0xa8, 0xb2, 0x41, 0x72, 0x25, 0x50, 0x25, 0x90, 0xc0, 0xec, 0xd8, 0x58, + 0x8b, 0xd8, 0x24, 0x2c, 0xa8, 0xb2, 0x41, 0x72, 0x25, 0x50, 0x25, 0x90, 0xc0, 0xec, 0xd8, 0x58, 0x93, 0xf1, 0x90, 0x8e, 0xb0, 0x3d, 0x21, 0x33, 0x89, 0xc8, 0x0d, 0x58, 0x22, 0x4e, 0xc0, 0x9a, 0x33, 0x70, 0x80, 0x2e, 0xbb, 0xec, 0x2a, 0xa0, 0xe4, 0x06, 0x39, 0x01, 0xf2, 0xcc, 0x98, 0xb6, 0x69, 0xca, 0xee, 0xe7, 0xf9, 0xbd, 0xf7, 0x9f, 0xde, 0xcf, 0x40, 0x97, 0x0d, 0x48, 0x40, 0xf8, @@ -252,7 +252,7 @@ var fileDescriptor_bcd0c0f1f2e6a91a = []byte{ 0x2b, 0x1f, 0x8e, 0x87, 0x4c, 0x9e, 0x4d, 0x06, 0x3e, 0xe1, 0x59, 0x40, 0xb8, 0xc8, 0xb8, 0x08, 0xd8, 0x80, 0x74, 0x86, 0x3c, 0x98, 0x3e, 0x0f, 0x32, 0x9e, 0x4c, 0x52, 0x2a, 0xf4, 0x5b, 0x7e, 0xda, 0xeb, 0x98, 0xe7, 0x2c, 0x67, 0x23, 0x2a, 0x06, 0x75, 0xf5, 0x6a, 0x9f, 0xfd, 0x0d, 0x00, - 0x00, 0xff, 0xff, 0x05, 0xb6, 0x71, 0xe6, 0x24, 0x04, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x62, 0xb6, 0x99, 0xab, 0x24, 0x04, 0x00, 0x00, } func (m *GenesisState) Marshal() (dAtA []byte, err error) { diff --git a/modules/core/02-client/types/genesis_test.go b/modules/core/02-client/types/genesis_test.go index c627d173327..16c87f58411 100644 --- a/modules/core/02-client/types/genesis_test.go +++ b/modules/core/02-client/types/genesis_test.go @@ -5,27 +5,27 @@ import ( tmtypes "github.com/tendermint/tendermint/types" - client "github.com/cosmos/ibc-go/v4/modules/core/02-client" - "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - commitmenttypes "github.com/cosmos/ibc-go/v4/modules/core/23-commitment/types" - "github.com/cosmos/ibc-go/v4/modules/core/exported" - ibctmtypes "github.com/cosmos/ibc-go/v4/modules/light-clients/07-tendermint/types" - localhosttypes "github.com/cosmos/ibc-go/v4/modules/light-clients/09-localhost/types" - ibctesting "github.com/cosmos/ibc-go/v4/testing" - ibctestingmock "github.com/cosmos/ibc-go/v4/testing/mock" + client "github.com/cosmos/ibc-go/v7/modules/core/02-client" + "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + commitmenttypes "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/types" + "github.com/cosmos/ibc-go/v7/modules/core/exported" + solomachine "github.com/cosmos/ibc-go/v7/modules/light-clients/06-solomachine" + ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" + ibctesting "github.com/cosmos/ibc-go/v7/testing" + ibctestingmock "github.com/cosmos/ibc-go/v7/testing/mock" ) const ( - chainID = "chainID" - tmClientID0 = "07-tendermint-0" - tmClientID1 = "07-tendermint-1" - invalidClientID = "myclient-0" - clientID = tmClientID0 + tmClientID0 = "07-tendermint-0" + tmClientID1 = "07-tendermint-1" + invalidClientID = "myclient-0" + soloMachineClientID = "06-solomachine-0" + clientID = tmClientID0 height = 10 ) -var clientHeight = types.NewHeight(0, 10) +var clientHeight = types.NewHeight(1, 10) func (suite *TypesTestSuite) TestMarshalGenesisState() { cdc := suite.chainA.App.AppCodec() @@ -58,8 +58,8 @@ func (suite *TypesTestSuite) TestValidateGenesis() { signers := make(map[string]tmtypes.PrivValidator) signers[val.Address.String()] = privVal - heightMinus1 := types.NewHeight(0, height-1) - header := suite.chainA.CreateTMClientHeader(chainID, int64(clientHeight.RevisionHeight), heightMinus1, now, valSet, valSet, valSet, signers) + heightMinus1 := types.NewHeight(1, height-1) + header := suite.chainA.CreateTMClientHeader(suite.chainA.ChainID, int64(clientHeight.RevisionHeight), heightMinus1, now, valSet, valSet, valSet, signers) testCases := []struct { name string @@ -76,10 +76,7 @@ func (suite *TypesTestSuite) TestValidateGenesis() { genState: types.NewGenesisState( []types.IdentifiedClientState{ types.NewIdentifiedClientState( - tmClientID0, ibctmtypes.NewClientState(chainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), - ), - types.NewIdentifiedClientState( - exported.Localhost+"-1", localhosttypes.NewClientState("chainID", clientHeight), + tmClientID0, ibctm.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath), ), }, []types.ClientConsensusStates{ @@ -88,7 +85,7 @@ func (suite *TypesTestSuite) TestValidateGenesis() { []types.ConsensusStateWithHeight{ types.NewConsensusStateWithHeight( header.GetHeight().(types.Height), - ibctmtypes.NewConsensusState( + ibctm.NewConsensusState( header.GetTime(), commitmenttypes.NewMerkleRoot(header.Header.GetAppHash()), header.Header.NextValidatorsHash, ), ), @@ -104,21 +101,35 @@ func (suite *TypesTestSuite) TestValidateGenesis() { }, ), }, - types.NewParams(exported.Tendermint, exported.Localhost), + types.NewParams(exported.Tendermint), false, 2, ), expPass: true, }, { - name: "invalid clientid", + name: "invalid client type", genState: types.NewGenesisState( []types.IdentifiedClientState{ types.NewIdentifiedClientState( - invalidClientID, ibctmtypes.NewClientState(chainID, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + soloMachineClientID, ibctm.NewClientState(suite.chainA.ChainID, ibctm.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath), ), + types.NewIdentifiedClientState(tmClientID0, solomachine.NewClientState(0, &solomachine.ConsensusState{PublicKey: suite.solomachine.ConsensusState().PublicKey, Diversifier: suite.solomachine.Diversifier, Timestamp: suite.solomachine.Time})), + }, + nil, + nil, + types.NewParams(exported.Tendermint), + false, + 0, + ), + expPass: false, + }, + { + name: "invalid clientid", + genState: types.NewGenesisState( + []types.IdentifiedClientState{ types.NewIdentifiedClientState( - exported.Localhost, localhosttypes.NewClientState("chainID", clientHeight), + invalidClientID, ibctm.NewClientState(suite.chainA.ChainID, ibctm.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath), ), }, []types.ClientConsensusStates{ @@ -127,7 +138,7 @@ func (suite *TypesTestSuite) TestValidateGenesis() { []types.ConsensusStateWithHeight{ types.NewConsensusStateWithHeight( header.GetHeight().(types.Height), - ibctmtypes.NewConsensusState( + ibctm.NewConsensusState( header.GetTime(), commitmenttypes.NewMerkleRoot(header.Header.GetAppHash()), header.Header.NextValidatorsHash, ), ), @@ -141,32 +152,12 @@ func (suite *TypesTestSuite) TestValidateGenesis() { ), expPass: false, }, - { - name: "invalid client", - genState: types.NewGenesisState( - []types.IdentifiedClientState{ - types.NewIdentifiedClientState( - tmClientID0, ibctmtypes.NewClientState(chainID, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), - ), - types.NewIdentifiedClientState(exported.Localhost, localhosttypes.NewClientState("chaindID", types.ZeroHeight())), - }, - nil, - nil, - types.NewParams(exported.Tendermint), - false, - 0, - ), - expPass: false, - }, { name: "consensus state client id does not match client id in genesis clients", genState: types.NewGenesisState( []types.IdentifiedClientState{ types.NewIdentifiedClientState( - tmClientID0, ibctmtypes.NewClientState(chainID, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), - ), - types.NewIdentifiedClientState( - exported.Localhost, localhosttypes.NewClientState("chaindID", clientHeight), + tmClientID0, ibctm.NewClientState(suite.chainA.ChainID, ibctm.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath), ), }, []types.ClientConsensusStates{ @@ -174,8 +165,8 @@ func (suite *TypesTestSuite) TestValidateGenesis() { tmClientID1, []types.ConsensusStateWithHeight{ types.NewConsensusStateWithHeight( - types.NewHeight(0, 1), - ibctmtypes.NewConsensusState( + types.NewHeight(1, 1), + ibctm.NewConsensusState( header.GetTime(), commitmenttypes.NewMerkleRoot(header.Header.GetAppHash()), header.Header.NextValidatorsHash, ), ), @@ -194,10 +185,7 @@ func (suite *TypesTestSuite) TestValidateGenesis() { genState: types.NewGenesisState( []types.IdentifiedClientState{ types.NewIdentifiedClientState( - tmClientID0, ibctmtypes.NewClientState(chainID, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), - ), - types.NewIdentifiedClientState( - exported.Localhost, localhosttypes.NewClientState("chaindID", clientHeight), + tmClientID0, ibctm.NewClientState(suite.chainA.ChainID, ibctm.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath), ), }, []types.ClientConsensusStates{ @@ -206,7 +194,7 @@ func (suite *TypesTestSuite) TestValidateGenesis() { []types.ConsensusStateWithHeight{ types.NewConsensusStateWithHeight( types.ZeroHeight(), - ibctmtypes.NewConsensusState( + ibctm.NewConsensusState( header.GetTime(), commitmenttypes.NewMerkleRoot(header.Header.GetAppHash()), header.Header.NextValidatorsHash, ), ), @@ -225,10 +213,7 @@ func (suite *TypesTestSuite) TestValidateGenesis() { genState: types.NewGenesisState( []types.IdentifiedClientState{ types.NewIdentifiedClientState( - tmClientID0, ibctmtypes.NewClientState(chainID, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), - ), - types.NewIdentifiedClientState( - exported.Localhost, localhosttypes.NewClientState("chaindID", clientHeight), + tmClientID0, ibctm.NewClientState(suite.chainA.ChainID, ibctm.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath), ), }, []types.ClientConsensusStates{ @@ -236,8 +221,8 @@ func (suite *TypesTestSuite) TestValidateGenesis() { tmClientID0, []types.ConsensusStateWithHeight{ types.NewConsensusStateWithHeight( - types.NewHeight(0, 1), - ibctmtypes.NewConsensusState( + types.NewHeight(1, 1), + ibctm.NewConsensusState( time.Time{}, commitmenttypes.NewMerkleRoot(header.Header.GetAppHash()), header.Header.NextValidatorsHash, ), ), @@ -256,10 +241,7 @@ func (suite *TypesTestSuite) TestValidateGenesis() { genState: types.NewGenesisState( []types.IdentifiedClientState{ types.NewIdentifiedClientState( - tmClientID0, ibctmtypes.NewClientState(chainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), - ), - types.NewIdentifiedClientState( - exported.Localhost, localhosttypes.NewClientState("chainID", clientHeight), + tmClientID0, ibctm.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath), ), }, []types.ClientConsensusStates{ @@ -268,7 +250,7 @@ func (suite *TypesTestSuite) TestValidateGenesis() { []types.ConsensusStateWithHeight{ types.NewConsensusStateWithHeight( header.GetHeight().(types.Height), - ibctmtypes.NewConsensusState( + ibctm.NewConsensusState( header.GetTime(), commitmenttypes.NewMerkleRoot(header.Header.GetAppHash()), header.Header.NextValidatorsHash, ), ), @@ -287,10 +269,7 @@ func (suite *TypesTestSuite) TestValidateGenesis() { genState: types.NewGenesisState( []types.IdentifiedClientState{ types.NewIdentifiedClientState( - clientID, ibctmtypes.NewClientState(chainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), - ), - types.NewIdentifiedClientState( - exported.Localhost, localhosttypes.NewClientState("chainID", clientHeight), + clientID, ibctm.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath), ), }, []types.ClientConsensusStates{ @@ -299,7 +278,7 @@ func (suite *TypesTestSuite) TestValidateGenesis() { []types.ConsensusStateWithHeight{ types.NewConsensusStateWithHeight( header.GetHeight().(types.Height), - ibctmtypes.NewConsensusState( + ibctm.NewConsensusState( header.GetTime(), commitmenttypes.NewMerkleRoot(header.Header.GetAppHash()), header.Header.NextValidatorsHash, ), ), @@ -315,7 +294,7 @@ func (suite *TypesTestSuite) TestValidateGenesis() { }, ), }, - types.NewParams(exported.Tendermint, exported.Localhost), + types.NewParams(exported.Tendermint), false, 0, ), @@ -326,7 +305,7 @@ func (suite *TypesTestSuite) TestValidateGenesis() { genState: types.NewGenesisState( []types.IdentifiedClientState{ types.NewIdentifiedClientState( - clientID, ibctmtypes.NewClientState(chainID, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + clientID, ibctm.NewClientState(suite.chainA.ChainID, ibctm.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath), ), }, []types.ClientConsensusStates{ @@ -335,7 +314,7 @@ func (suite *TypesTestSuite) TestValidateGenesis() { []types.ConsensusStateWithHeight{ types.NewConsensusStateWithHeight( header.GetHeight().(types.Height), - ibctmtypes.NewConsensusState( + ibctm.NewConsensusState( header.GetTime(), commitmenttypes.NewMerkleRoot(header.Header.GetAppHash()), header.Header.NextValidatorsHash, ), ), @@ -361,10 +340,7 @@ func (suite *TypesTestSuite) TestValidateGenesis() { genState: types.NewGenesisState( []types.IdentifiedClientState{ types.NewIdentifiedClientState( - tmClientID0, ibctmtypes.NewClientState(chainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), - ), - types.NewIdentifiedClientState( - exported.Localhost, localhosttypes.NewClientState("chainID", clientHeight), + tmClientID0, ibctm.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath), ), }, []types.ClientConsensusStates{ @@ -373,7 +349,7 @@ func (suite *TypesTestSuite) TestValidateGenesis() { []types.ConsensusStateWithHeight{ types.NewConsensusStateWithHeight( header.GetHeight().(types.Height), - ibctmtypes.NewConsensusState( + ibctm.NewConsensusState( header.GetTime(), commitmenttypes.NewMerkleRoot(header.Header.GetAppHash()), header.Header.NextValidatorsHash, ), ), @@ -392,10 +368,7 @@ func (suite *TypesTestSuite) TestValidateGenesis() { genState: types.NewGenesisState( []types.IdentifiedClientState{ types.NewIdentifiedClientState( - tmClientID0, ibctmtypes.NewClientState(chainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), - ), - types.NewIdentifiedClientState( - exported.Localhost, localhosttypes.NewClientState("chainID", clientHeight), + tmClientID0, ibctm.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath), ), }, []types.ClientConsensusStates{ @@ -404,7 +377,7 @@ func (suite *TypesTestSuite) TestValidateGenesis() { []types.ConsensusStateWithHeight{ types.NewConsensusStateWithHeight( header.GetHeight().(types.Height), - ibctmtypes.NewConsensusState( + ibctm.NewConsensusState( header.GetTime(), commitmenttypes.NewMerkleRoot(header.Header.GetAppHash()), header.Header.NextValidatorsHash, ), ), @@ -413,51 +386,20 @@ func (suite *TypesTestSuite) TestValidateGenesis() { }, nil, types.NewParams(" "), - true, + false, 0, ), expPass: false, }, - { - name: "localhost client not registered on allowlist", - genState: types.NewGenesisState( - []types.IdentifiedClientState{ - types.NewIdentifiedClientState( - tmClientID1, ibctmtypes.NewClientState(chainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), - ), - types.NewIdentifiedClientState( - exported.Localhost+"-0", localhosttypes.NewClientState("chainID", clientHeight), - ), - }, - []types.ClientConsensusStates{ - types.NewClientConsensusStates( - tmClientID1, - []types.ConsensusStateWithHeight{ - types.NewConsensusStateWithHeight( - header.GetHeight().(types.Height), - ibctmtypes.NewConsensusState( - header.GetTime(), commitmenttypes.NewMerkleRoot(header.Header.GetAppHash()), header.Header.NextValidatorsHash, - ), - ), - }, - ), - }, - nil, - types.NewParams(exported.Tendermint), - true, - 2, - ), - expPass: false, - }, { name: "next sequence too small", genState: types.NewGenesisState( []types.IdentifiedClientState{ types.NewIdentifiedClientState( - tmClientID0, ibctmtypes.NewClientState(chainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + tmClientID0, ibctm.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath), ), types.NewIdentifiedClientState( - exported.Localhost+"-1", localhosttypes.NewClientState("chainID", clientHeight), + tmClientID1, ibctm.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath), ), }, []types.ClientConsensusStates{ @@ -466,7 +408,7 @@ func (suite *TypesTestSuite) TestValidateGenesis() { []types.ConsensusStateWithHeight{ types.NewConsensusStateWithHeight( header.GetHeight().(types.Height), - ibctmtypes.NewConsensusState( + ibctm.NewConsensusState( header.GetTime(), commitmenttypes.NewMerkleRoot(header.Header.GetAppHash()), header.Header.NextValidatorsHash, ), ), @@ -474,7 +416,7 @@ func (suite *TypesTestSuite) TestValidateGenesis() { ), }, nil, - types.NewParams(exported.Tendermint, exported.Localhost), + types.NewParams(exported.Tendermint), false, 0, ), @@ -485,10 +427,7 @@ func (suite *TypesTestSuite) TestValidateGenesis() { genState: types.NewGenesisState( []types.IdentifiedClientState{ types.NewIdentifiedClientState( - "my-client", ibctmtypes.NewClientState(chainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), - ), - types.NewIdentifiedClientState( - exported.Localhost+"-1", localhosttypes.NewClientState("chainID", clientHeight), + "my-client", ibctm.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath), ), }, []types.ClientConsensusStates{ @@ -497,7 +436,7 @@ func (suite *TypesTestSuite) TestValidateGenesis() { []types.ConsensusStateWithHeight{ types.NewConsensusStateWithHeight( header.GetHeight().(types.Height), - ibctmtypes.NewConsensusState( + ibctm.NewConsensusState( header.GetTime(), commitmenttypes.NewMerkleRoot(header.Header.GetAppHash()), header.Header.NextValidatorsHash, ), ), @@ -505,7 +444,7 @@ func (suite *TypesTestSuite) TestValidateGenesis() { ), }, nil, - types.NewParams(exported.Tendermint, exported.Localhost), + types.NewParams(exported.Tendermint), false, 5, ), @@ -514,18 +453,14 @@ func (suite *TypesTestSuite) TestValidateGenesis() { { name: "consensus state different than client state type", genState: types.NewGenesisState( - []types.IdentifiedClientState{ - types.NewIdentifiedClientState( - exported.Localhost+"-1", localhosttypes.NewClientState("chainID", clientHeight), - ), - }, + []types.IdentifiedClientState{}, []types.ClientConsensusStates{ types.NewClientConsensusStates( - exported.Localhost+"-1", + tmClientID0, []types.ConsensusStateWithHeight{ types.NewConsensusStateWithHeight( header.GetHeight().(types.Height), - ibctmtypes.NewConsensusState( + ibctm.NewConsensusState( header.GetTime(), commitmenttypes.NewMerkleRoot(header.Header.GetAppHash()), header.Header.NextValidatorsHash, ), ), @@ -533,7 +468,7 @@ func (suite *TypesTestSuite) TestValidateGenesis() { ), }, nil, - types.NewParams(exported.Tendermint, exported.Localhost), + types.NewParams(exported.Tendermint), false, 5, ), diff --git a/modules/core/02-client/types/height.go b/modules/core/02-client/types/height.go index 152c052cf1b..b4df09e5324 100644 --- a/modules/core/02-client/types/height.go +++ b/modules/core/02-client/types/height.go @@ -7,10 +7,11 @@ import ( "strconv" "strings" + errorsmod "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/cosmos/ibc-go/v4/modules/core/exported" + ibcerrors "github.com/cosmos/ibc-go/v7/internal/errors" + "github.com/cosmos/ibc-go/v7/modules/core/exported" ) var _ exported.Height = (*Height)(nil) @@ -135,15 +136,15 @@ func MustParseHeight(heightStr string) Height { func ParseHeight(heightStr string) (Height, error) { splitStr := strings.Split(heightStr, "-") if len(splitStr) != 2 { - return Height{}, sdkerrors.Wrapf(sdkerrors.ErrInvalidHeight, "expected height string format: {revision}-{height}. Got: %s", heightStr) + return Height{}, errorsmod.Wrapf(ibcerrors.ErrInvalidHeight, "expected height string format: {revision}-{height}. Got: %s", heightStr) } revisionNumber, err := strconv.ParseUint(splitStr[0], 10, 64) if err != nil { - return Height{}, sdkerrors.Wrapf(sdkerrors.ErrInvalidHeight, "invalid revision number. parse err: %s", err) + return Height{}, errorsmod.Wrapf(ibcerrors.ErrInvalidHeight, "invalid revision number. parse err: %s", err) } revisionHeight, err := strconv.ParseUint(splitStr[1], 10, 64) if err != nil { - return Height{}, sdkerrors.Wrapf(sdkerrors.ErrInvalidHeight, "invalid revision height. parse err: %s", err) + return Height{}, errorsmod.Wrapf(ibcerrors.ErrInvalidHeight, "invalid revision height. parse err: %s", err) } return NewHeight(revisionNumber, revisionHeight), nil } @@ -152,8 +153,8 @@ func ParseHeight(heightStr string) (Height, error) { // in the chainID with the given revision number. func SetRevisionNumber(chainID string, revision uint64) (string, error) { if !IsRevisionFormat(chainID) { - return "", sdkerrors.Wrapf( - sdkerrors.ErrInvalidChainID, "chainID is not in revision format: %s", chainID, + return "", errorsmod.Wrapf( + ibcerrors.ErrInvalidChainID, "chainID is not in revision format: %s", chainID, ) } diff --git a/modules/core/02-client/types/height_test.go b/modules/core/02-client/types/height_test.go index d7e2c3650d2..fa4ae59700a 100644 --- a/modules/core/02-client/types/height_test.go +++ b/modules/core/02-client/types/height_test.go @@ -6,7 +6,7 @@ import ( "github.com/stretchr/testify/require" - "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" ) func TestZeroHeight(t *testing.T) { diff --git a/modules/core/02-client/types/keys.go b/modules/core/02-client/types/keys.go index c90e3784a1b..12345c46e6c 100644 --- a/modules/core/02-client/types/keys.go +++ b/modules/core/02-client/types/keys.go @@ -6,9 +6,9 @@ import ( "strconv" "strings" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + errorsmod "cosmossdk.io/errors" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" ) const ( @@ -34,7 +34,11 @@ func FormatClientIdentifier(clientType string, sequence uint64) string { // IsClientIDFormat checks if a clientID is in the format required on the SDK for // parsing client identifiers. The client identifier must be in the form: `{client-type}-{N} -var IsClientIDFormat = regexp.MustCompile(`^.*[^\n-]-[0-9]{1,20}$`).MatchString +// which per the specification only permits ASCII for the {client-type} segment and +// 1 to 20 digits for the {N} segment. +// `([\w-]+\w)?` allows for a letter or hyphen, with the {client-type} starting with a letter +// and ending with a letter, i.e. `letter+(letter|hypen+letter)?`. +var IsClientIDFormat = regexp.MustCompile(`^\w+([\w-]+\w)?-[0-9]{1,20}$`).MatchString // IsValidClientID checks if the clientID is valid and can be parsed into the client // identifier format. @@ -46,7 +50,7 @@ func IsValidClientID(clientID string) bool { // ParseClientIdentifier parses the client type and sequence from the client identifier. func ParseClientIdentifier(clientID string) (string, uint64, error) { if !IsClientIDFormat(clientID) { - return "", 0, sdkerrors.Wrapf(host.ErrInvalidID, "invalid client identifier %s is not in format: `{client-type}-{N}`", clientID) + return "", 0, errorsmod.Wrapf(host.ErrInvalidID, "invalid client identifier %s is not in format: `{client-type}-{N}`", clientID) } splitStr := strings.Split(clientID, "-") @@ -54,12 +58,12 @@ func ParseClientIdentifier(clientID string) (string, uint64, error) { clientType := strings.Join(splitStr[:lastIndex], "-") if strings.TrimSpace(clientType) == "" { - return "", 0, sdkerrors.Wrap(host.ErrInvalidID, "client identifier must be in format: `{client-type}-{N}` and client type cannot be blank") + return "", 0, errorsmod.Wrap(host.ErrInvalidID, "client identifier must be in format: `{client-type}-{N}` and client type cannot be blank") } sequence, err := strconv.ParseUint(splitStr[lastIndex], 10, 64) if err != nil { - return "", 0, sdkerrors.Wrap(err, "failed to parse client identifier sequence") + return "", 0, errorsmod.Wrap(err, "failed to parse client identifier sequence") } return clientType, sequence, nil diff --git a/modules/core/02-client/types/keys_test.go b/modules/core/02-client/types/keys_test.go index 2a042b82eae..93c295df627 100644 --- a/modules/core/02-client/types/keys_test.go +++ b/modules/core/02-client/types/keys_test.go @@ -6,7 +6,7 @@ import ( "github.com/stretchr/testify/require" - "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" ) // tests ParseClientIdentifier and IsValidClientID @@ -35,22 +35,31 @@ func TestParseClientIdentifier(t *testing.T) { {"negative sequence", "tendermint--1", "tendermint", 0, false}, {"invalid format", "tendermint-tm", "tendermint", 0, false}, {"empty clientype", " -100", "tendermint", 0, false}, + {"with in the middle tabs", "a\t\t\t-100", "tendermint", 0, false}, + {"leading tabs", "\t\t\ta-100", "tendermint", 0, false}, + {"with whitespace", " a-100", "tendermint", 0, false}, + {"leading hyphens", "-----a-100", "tendermint", 0, false}, + {"with slash", "tendermint/-100", "tendermint", 0, false}, + {"non-ASCII:: emoji", "🚨😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎-100", "tendermint", 0, false}, + {"non-ASCII:: others", "世界-100", "tendermint", 0, false}, } for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + clientType, seq, err := types.ParseClientIdentifier(tc.clientID) + valid := types.IsValidClientID(tc.clientID) + require.Equal(t, tc.expSeq, seq, tc.clientID) - clientType, seq, err := types.ParseClientIdentifier(tc.clientID) - valid := types.IsValidClientID(tc.clientID) - require.Equal(t, tc.expSeq, seq, tc.clientID) - - if tc.expPass { - require.NoError(t, err, tc.name) - require.True(t, valid) - require.Equal(t, tc.clientType, clientType) - } else { - require.Error(t, err, tc.name, tc.clientID) - require.False(t, valid) - require.Equal(t, "", clientType) - } + if tc.expPass { + require.NoError(t, err, tc.name) + require.True(t, valid) + require.Equal(t, tc.clientType, clientType) + } else { + require.Error(t, err, tc.name, tc.clientID) + require.False(t, valid) + require.Equal(t, "", clientType) + } + }) } } diff --git a/modules/core/02-client/types/msgs.go b/modules/core/02-client/types/msgs.go index 58586887b51..9f2c76f4141 100644 --- a/modules/core/02-client/types/msgs.go +++ b/modules/core/02-client/types/msgs.go @@ -1,20 +1,13 @@ package types import ( + errorsmod "cosmossdk.io/errors" codectypes "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" - "github.com/cosmos/ibc-go/v4/modules/core/exported" -) - -// message types for the IBC client -const ( - TypeMsgCreateClient string = "create_client" - TypeMsgUpdateClient string = "update_client" - TypeMsgUpgradeClient string = "upgrade_client" - TypeMsgSubmitMisbehaviour string = "submit_misbehaviour" + ibcerrors "github.com/cosmos/ibc-go/v7/internal/errors" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + "github.com/cosmos/ibc-go/v7/modules/core/exported" ) var ( @@ -30,6 +23,7 @@ var ( ) // NewMsgCreateClient creates a new MsgCreateClient instance +// //nolint:interfacer func NewMsgCreateClient( clientState exported.ClientState, consensusState exported.ConsensusState, signer string, @@ -55,7 +49,7 @@ func NewMsgCreateClient( func (msg MsgCreateClient) ValidateBasic() error { _, err := sdk.AccAddressFromBech32(msg.Signer) if err != nil { - return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) + return errorsmod.Wrapf(ibcerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) } clientState, err := UnpackClientState(msg.ClientState) if err != nil { @@ -64,18 +58,15 @@ func (msg MsgCreateClient) ValidateBasic() error { if err := clientState.Validate(); err != nil { return err } - if clientState.ClientType() == exported.Localhost { - return sdkerrors.Wrap(ErrInvalidClient, "localhost client can only be created on chain initialization") - } consensusState, err := UnpackConsensusState(msg.ConsensusState) if err != nil { return err } if clientState.ClientType() != consensusState.ClientType() { - return sdkerrors.Wrap(ErrInvalidClientType, "client type for client state and consensus state do not match") + return errorsmod.Wrap(ErrInvalidClientType, "client type for client state and consensus state do not match") } if err := ValidateClientType(clientState.ClientType()); err != nil { - return sdkerrors.Wrap(err, "client type does not meet naming constraints") + return errorsmod.Wrap(err, "client type does not meet naming constraints") } return consensusState.ValidateBasic() } @@ -102,17 +93,18 @@ func (msg MsgCreateClient) UnpackInterfaces(unpacker codectypes.AnyUnpacker) err } // NewMsgUpdateClient creates a new MsgUpdateClient instance +// //nolint:interfacer -func NewMsgUpdateClient(id string, header exported.Header, signer string) (*MsgUpdateClient, error) { - anyHeader, err := PackHeader(header) +func NewMsgUpdateClient(id string, clientMsg exported.ClientMessage, signer string) (*MsgUpdateClient, error) { + anyClientMsg, err := PackClientMessage(clientMsg) if err != nil { return nil, err } return &MsgUpdateClient{ - ClientId: id, - Header: anyHeader, - Signer: signer, + ClientId: id, + ClientMessage: anyClientMsg, + Signer: signer, }, nil } @@ -120,18 +112,15 @@ func NewMsgUpdateClient(id string, header exported.Header, signer string) (*MsgU func (msg MsgUpdateClient) ValidateBasic() error { _, err := sdk.AccAddressFromBech32(msg.Signer) if err != nil { - return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) + return errorsmod.Wrapf(ibcerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) } - header, err := UnpackHeader(msg.Header) + clientMsg, err := UnpackClientMessage(msg.ClientMessage) if err != nil { return err } - if err := header.ValidateBasic(); err != nil { + if err := clientMsg.ValidateBasic(); err != nil { return err } - if msg.ClientId == exported.Localhost { - return sdkerrors.Wrap(ErrInvalidClient, "localhost client is only updated on ABCI BeginBlock") - } return host.ClientIdentifierValidator(msg.ClientId) } @@ -146,12 +135,13 @@ func (msg MsgUpdateClient) GetSigners() []sdk.AccAddress { // UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces func (msg MsgUpdateClient) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { - var header exported.Header - return unpacker.UnpackAny(msg.Header, &header) + var clientMsg exported.ClientMessage + return unpacker.UnpackAny(msg.ClientMessage, &clientMsg) } // NewMsgUpgradeClient creates a new MsgUpgradeClient instance -// nolint: interfacer +// +//nolint:interfacer func NewMsgUpgradeClient(clientID string, clientState exported.ClientState, consState exported.ConsensusState, proofUpgradeClient, proofUpgradeConsState []byte, signer string, ) (*MsgUpgradeClient, error) { @@ -190,18 +180,18 @@ func (msg MsgUpgradeClient) ValidateBasic() error { } if clientState.ClientType() != consensusState.ClientType() { - return sdkerrors.Wrapf(ErrInvalidUpgradeClient, "consensus state's client-type does not match client. expected: %s, got: %s", + return errorsmod.Wrapf(ErrInvalidUpgradeClient, "consensus state's client-type does not match client. expected: %s, got: %s", clientState.ClientType(), consensusState.ClientType()) } if len(msg.ProofUpgradeClient) == 0 { - return sdkerrors.Wrap(ErrInvalidUpgradeClient, "proof of upgrade client cannot be empty") + return errorsmod.Wrap(ErrInvalidUpgradeClient, "proof of upgrade client cannot be empty") } if len(msg.ProofUpgradeConsensusState) == 0 { - return sdkerrors.Wrap(ErrInvalidUpgradeClient, "proof of upgrade consensus state cannot be empty") + return errorsmod.Wrap(ErrInvalidUpgradeClient, "proof of upgrade consensus state cannot be empty") } _, err = sdk.AccAddressFromBech32(msg.Signer) if err != nil { - return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) + return errorsmod.Wrapf(ibcerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) } return host.ClientIdentifierValidator(msg.ClientId) } @@ -228,9 +218,10 @@ func (msg MsgUpgradeClient) UnpackInterfaces(unpacker codectypes.AnyUnpacker) er } // NewMsgSubmitMisbehaviour creates a new MsgSubmitMisbehaviour instance. +// //nolint:interfacer -func NewMsgSubmitMisbehaviour(clientID string, misbehaviour exported.Misbehaviour, signer string) (*MsgSubmitMisbehaviour, error) { - anyMisbehaviour, err := PackMisbehaviour(misbehaviour) +func NewMsgSubmitMisbehaviour(clientID string, misbehaviour exported.ClientMessage, signer string) (*MsgSubmitMisbehaviour, error) { + anyMisbehaviour, err := PackClientMessage(misbehaviour) if err != nil { return nil, err } @@ -246,22 +237,15 @@ func NewMsgSubmitMisbehaviour(clientID string, misbehaviour exported.Misbehaviou func (msg MsgSubmitMisbehaviour) ValidateBasic() error { _, err := sdk.AccAddressFromBech32(msg.Signer) if err != nil { - return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) + return errorsmod.Wrapf(ibcerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) } - misbehaviour, err := UnpackMisbehaviour(msg.Misbehaviour) + misbehaviour, err := UnpackClientMessage(msg.Misbehaviour) if err != nil { return err } if err := misbehaviour.ValidateBasic(); err != nil { return err } - if misbehaviour.GetClientID() != msg.ClientId { - return sdkerrors.Wrapf( - ErrInvalidMisbehaviour, - "misbehaviour client-id doesn't match client-id from message (%s ≠ %s)", - misbehaviour.GetClientID(), msg.ClientId, - ) - } return host.ClientIdentifierValidator(msg.ClientId) } @@ -277,6 +261,6 @@ func (msg MsgSubmitMisbehaviour) GetSigners() []sdk.AccAddress { // UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces func (msg MsgSubmitMisbehaviour) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { - var misbehaviour exported.Misbehaviour + var misbehaviour exported.ClientMessage return unpacker.UnpackAny(msg.Misbehaviour, &misbehaviour) } diff --git a/modules/core/02-client/types/msgs_test.go b/modules/core/02-client/types/msgs_test.go index cf625f93bcc..9eeac2e3908 100644 --- a/modules/core/02-client/types/msgs_test.go +++ b/modules/core/02-client/types/msgs_test.go @@ -4,15 +4,14 @@ import ( "testing" "time" - "github.com/golang/protobuf/proto" + "github.com/golang/protobuf/proto" //nolint:staticcheck "github.com/stretchr/testify/suite" - "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - commitmenttypes "github.com/cosmos/ibc-go/v4/modules/core/23-commitment/types" - "github.com/cosmos/ibc-go/v4/modules/core/exported" - solomachinetypes "github.com/cosmos/ibc-go/v4/modules/light-clients/06-solomachine/types" - ibctmtypes "github.com/cosmos/ibc-go/v4/modules/light-clients/07-tendermint/types" - ibctesting "github.com/cosmos/ibc-go/v4/testing" + "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + commitmenttypes "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/types" + solomachine "github.com/cosmos/ibc-go/v7/modules/light-clients/06-solomachine" + ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" + ibctesting "github.com/cosmos/ibc-go/v7/testing" ) type TypesTestSuite struct { @@ -20,14 +19,16 @@ type TypesTestSuite struct { coordinator *ibctesting.Coordinator - chainA *ibctesting.TestChain - chainB *ibctesting.TestChain + chainA *ibctesting.TestChain + chainB *ibctesting.TestChain + solomachine *ibctesting.Solomachine } 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)) + suite.solomachine = ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachinesingle", "testing", 1) } func TestTypesTestSuite(t *testing.T) { @@ -55,7 +56,7 @@ func (suite *TypesTestSuite) TestMarshalMsgCreateClient() { }, { "tendermint client", func() { - tendermintClient := ibctmtypes.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) + tendermintClient := ibctm.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath) msg, err = types.NewMsgCreateClient(tendermintClient, suite.chainA.CurrentTMClientHeader().ConsensusState(), suite.chainA.SenderAccount.GetAddress().String()) suite.Require().NoError(err) }, @@ -100,7 +101,7 @@ func (suite *TypesTestSuite) TestMsgCreateClient_ValidateBasic() { { "valid - tendermint client", func() { - tendermintClient := ibctmtypes.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) + tendermintClient := ibctm.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath) msg, err = types.NewMsgCreateClient(tendermintClient, suite.chainA.CurrentTMClientHeader().ConsensusState(), suite.chainA.SenderAccount.GetAddress().String()) suite.Require().NoError(err) }, @@ -109,7 +110,7 @@ func (suite *TypesTestSuite) TestMsgCreateClient_ValidateBasic() { { "invalid tendermint client", func() { - msg, err = types.NewMsgCreateClient(&ibctmtypes.ClientState{}, suite.chainA.CurrentTMClientHeader().ConsensusState(), suite.chainA.SenderAccount.GetAddress().String()) + msg, err = types.NewMsgCreateClient(&ibctm.ClientState{}, suite.chainA.CurrentTMClientHeader().ConsensusState(), suite.chainA.SenderAccount.GetAddress().String()) suite.Require().NoError(err) }, false, @@ -124,7 +125,7 @@ func (suite *TypesTestSuite) TestMsgCreateClient_ValidateBasic() { { "failed to unpack consensus state", func() { - tendermintClient := ibctmtypes.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) + tendermintClient := ibctm.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath) msg, err = types.NewMsgCreateClient(tendermintClient, suite.chainA.CurrentTMClientHeader().ConsensusState(), suite.chainA.SenderAccount.GetAddress().String()) suite.Require().NoError(err) msg.ConsensusState = nil @@ -151,7 +152,7 @@ func (suite *TypesTestSuite) TestMsgCreateClient_ValidateBasic() { "invalid solomachine client", func() { soloMachine := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachine", "", 2) - msg, err = types.NewMsgCreateClient(&solomachinetypes.ClientState{}, soloMachine.ConsensusState(), suite.chainA.SenderAccount.GetAddress().String()) + msg, err = types.NewMsgCreateClient(&solomachine.ClientState{}, soloMachine.ConsensusState(), suite.chainA.SenderAccount.GetAddress().String()) suite.Require().NoError(err) }, false, @@ -160,7 +161,7 @@ func (suite *TypesTestSuite) TestMsgCreateClient_ValidateBasic() { "invalid solomachine consensus state", func() { soloMachine := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachine", "", 2) - msg, err = types.NewMsgCreateClient(soloMachine.ClientState(), &solomachinetypes.ConsensusState{}, suite.chainA.SenderAccount.GetAddress().String()) + msg, err = types.NewMsgCreateClient(soloMachine.ClientState(), &solomachine.ConsensusState{}, suite.chainA.SenderAccount.GetAddress().String()) suite.Require().NoError(err) }, false, @@ -168,7 +169,7 @@ func (suite *TypesTestSuite) TestMsgCreateClient_ValidateBasic() { { "invalid - client state and consensus state client types do not match", func() { - tendermintClient := ibctmtypes.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) + tendermintClient := ibctm.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath) soloMachine := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachine", "", 2) msg, err = types.NewMsgCreateClient(tendermintClient, soloMachine.ConsensusState(), suite.chainA.SenderAccount.GetAddress().String()) suite.Require().NoError(err) @@ -203,7 +204,7 @@ func (suite *TypesTestSuite) TestMarshalMsgUpdateClient() { { "solo machine client", func() { soloMachine := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachine", "", 2) - msg, err = types.NewMsgUpdateClient(soloMachine.ClientID, soloMachine.CreateHeader(), suite.chainA.SenderAccount.GetAddress().String()) + msg, err = types.NewMsgUpdateClient(soloMachine.ClientID, soloMachine.CreateHeader(soloMachine.Diversifier), suite.chainA.SenderAccount.GetAddress().String()) suite.Require().NoError(err) }, }, @@ -268,7 +269,7 @@ func (suite *TypesTestSuite) TestMsgUpdateClient_ValidateBasic() { { "invalid tendermint header", func() { - msg, err = types.NewMsgUpdateClient("tendermint", &ibctmtypes.Header{}, suite.chainA.SenderAccount.GetAddress().String()) + msg, err = types.NewMsgUpdateClient("tendermint", &ibctm.Header{}, suite.chainA.SenderAccount.GetAddress().String()) suite.Require().NoError(err) }, false, @@ -276,7 +277,7 @@ func (suite *TypesTestSuite) TestMsgUpdateClient_ValidateBasic() { { "failed to unpack header", func() { - msg.Header = nil + msg.ClientMessage = nil }, false, }, @@ -291,7 +292,8 @@ func (suite *TypesTestSuite) TestMsgUpdateClient_ValidateBasic() { "valid - solomachine header", func() { soloMachine := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachine", "", 2) - msg, err = types.NewMsgUpdateClient(soloMachine.ClientID, soloMachine.CreateHeader(), suite.chainA.SenderAccount.GetAddress().String()) + msg, err = types.NewMsgUpdateClient(soloMachine.ClientID, soloMachine.CreateHeader(soloMachine.Diversifier), suite.chainA.SenderAccount.GetAddress().String()) + suite.Require().NoError(err) }, true, @@ -299,15 +301,7 @@ func (suite *TypesTestSuite) TestMsgUpdateClient_ValidateBasic() { { "invalid solomachine header", func() { - msg, err = types.NewMsgUpdateClient("solomachine", &solomachinetypes.Header{}, suite.chainA.SenderAccount.GetAddress().String()) - suite.Require().NoError(err) - }, - false, - }, - { - "unsupported - localhost", - func() { - msg, err = types.NewMsgUpdateClient(exported.Localhost, suite.chainA.CurrentTMClientHeader(), suite.chainA.SenderAccount.GetAddress().String()) + msg, err = types.NewMsgUpdateClient("solomachine", &solomachine.Header{}, suite.chainA.SenderAccount.GetAddress().String()) suite.Require().NoError(err) }, false, @@ -338,8 +332,8 @@ func (suite *TypesTestSuite) TestMarshalMsgUpgradeClient() { { "client upgrades to new tendermint client", func() { - tendermintClient := ibctmtypes.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) - tendermintConsState := &ibctmtypes.ConsensusState{NextValidatorsHash: []byte("nextValsHash")} + tendermintClient := ibctm.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath) + tendermintConsState := &ibctm.ConsensusState{NextValidatorsHash: []byte("nextValsHash")} msg, err = types.NewMsgUpgradeClient("clientid", tendermintClient, tendermintConsState, []byte("proofUpgradeClient"), []byte("proofUpgradeConsState"), suite.chainA.SenderAccount.GetAddress().String()) suite.Require().NoError(err) }, @@ -451,8 +445,8 @@ func (suite *TypesTestSuite) TestMsgUpgradeClient_ValidateBasic() { for _, tc := range cases { tc := tc - clientState := ibctmtypes.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) - consState := &ibctmtypes.ConsensusState{NextValidatorsHash: []byte("nextValsHash")} + clientState := ibctm.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath) + consState := &ibctm.ConsensusState{NextValidatorsHash: []byte("nextValsHash")} msg, err := types.NewMsgUpgradeClient("testclientid", clientState, consState, []byte("proofUpgradeClient"), []byte("proofUpgradeConsState"), suite.chainA.SenderAccount.GetAddress().String()) suite.Require().NoError(err) @@ -492,7 +486,7 @@ func (suite *TypesTestSuite) TestMarshalMsgSubmitMisbehaviour() { header1 := suite.chainA.CreateTMClientHeader(suite.chainA.ChainID, int64(height.RevisionHeight), heightMinus1, suite.chainA.CurrentHeader.Time, suite.chainA.Vals, suite.chainA.Vals, suite.chainA.Vals, suite.chainA.Signers) header2 := suite.chainA.CreateTMClientHeader(suite.chainA.ChainID, int64(height.RevisionHeight), heightMinus1, suite.chainA.CurrentHeader.Time.Add(time.Minute), suite.chainA.Vals, suite.chainA.Vals, suite.chainA.Vals, suite.chainA.Signers) - misbehaviour := ibctmtypes.NewMisbehaviour("tendermint", header1, header2) + misbehaviour := ibctm.NewMisbehaviour("tendermint", header1, header2) msg, err = types.NewMsgSubmitMisbehaviour("tendermint", misbehaviour, suite.chainA.SenderAccount.GetAddress().String()) suite.Require().NoError(err) }, @@ -549,7 +543,7 @@ func (suite *TypesTestSuite) TestMsgSubmitMisbehaviour_ValidateBasic() { header1 := suite.chainA.CreateTMClientHeader(suite.chainA.ChainID, int64(height.RevisionHeight), heightMinus1, suite.chainA.CurrentHeader.Time, suite.chainA.Vals, suite.chainA.Vals, suite.chainA.Vals, suite.chainA.Signers) header2 := suite.chainA.CreateTMClientHeader(suite.chainA.ChainID, int64(height.RevisionHeight), heightMinus1, suite.chainA.CurrentHeader.Time.Add(time.Minute), suite.chainA.Vals, suite.chainA.Vals, suite.chainA.Vals, suite.chainA.Signers) - misbehaviour := ibctmtypes.NewMisbehaviour("tendermint", header1, header2) + misbehaviour := ibctm.NewMisbehaviour("tendermint", header1, header2) msg, err = types.NewMsgSubmitMisbehaviour("tendermint", misbehaviour, suite.chainA.SenderAccount.GetAddress().String()) suite.Require().NoError(err) }, @@ -558,7 +552,7 @@ func (suite *TypesTestSuite) TestMsgSubmitMisbehaviour_ValidateBasic() { { "invalid tendermint misbehaviour", func() { - msg, err = types.NewMsgSubmitMisbehaviour("tendermint", &ibctmtypes.Misbehaviour{}, suite.chainA.SenderAccount.GetAddress().String()) + msg, err = types.NewMsgSubmitMisbehaviour("tendermint", &ibctm.Misbehaviour{}, suite.chainA.SenderAccount.GetAddress().String()) suite.Require().NoError(err) }, false, @@ -589,7 +583,7 @@ func (suite *TypesTestSuite) TestMsgSubmitMisbehaviour_ValidateBasic() { { "invalid solomachine misbehaviour", func() { - msg, err = types.NewMsgSubmitMisbehaviour("solomachine", &solomachinetypes.Misbehaviour{}, suite.chainA.SenderAccount.GetAddress().String()) + msg, err = types.NewMsgSubmitMisbehaviour("solomachine", &solomachine.Misbehaviour{}, suite.chainA.SenderAccount.GetAddress().String()) suite.Require().NoError(err) }, false, diff --git a/modules/core/02-client/types/params.go b/modules/core/02-client/types/params.go index ce4af7d0369..21ad31007df 100644 --- a/modules/core/02-client/types/params.go +++ b/modules/core/02-client/types/params.go @@ -6,7 +6,7 @@ import ( paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" - "github.com/cosmos/ibc-go/v4/modules/core/exported" + "github.com/cosmos/ibc-go/v7/modules/core/exported" ) var ( diff --git a/modules/core/02-client/types/params_test.go b/modules/core/02-client/types/params_test.go index bcff83f1f46..447b0ffa5c7 100644 --- a/modules/core/02-client/types/params_test.go +++ b/modules/core/02-client/types/params_test.go @@ -5,7 +5,7 @@ import ( "github.com/stretchr/testify/require" - "github.com/cosmos/ibc-go/v4/modules/core/exported" + "github.com/cosmos/ibc-go/v7/modules/core/exported" ) func TestValidateParams(t *testing.T) { diff --git a/modules/core/02-client/types/proposal.go b/modules/core/02-client/types/proposal.go index 3040122a35f..2e6655d0ce9 100644 --- a/modules/core/02-client/types/proposal.go +++ b/modules/core/02-client/types/proposal.go @@ -2,13 +2,14 @@ package types import ( "fmt" + "reflect" + errorsmod "cosmossdk.io/errors" codectypes "github.com/cosmos/cosmos-sdk/codec/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" - "github.com/cosmos/ibc-go/v4/modules/core/exported" + "github.com/cosmos/ibc-go/v7/modules/core/exported" ) const ( @@ -58,7 +59,7 @@ func (cup *ClientUpdateProposal) ValidateBasic() error { } if cup.SubjectClientId == cup.SubstituteClientId { - return sdkerrors.Wrap(ErrInvalidSubstitute, "subject and substitute client identifiers are equal") + return errorsmod.Wrap(ErrInvalidSubstitute, "subject and substitute client identifiers are equal") } if _, _, err := ParseClientIdentifier(cup.SubjectClientId); err != nil { return err @@ -72,7 +73,7 @@ func (cup *ClientUpdateProposal) ValidateBasic() error { // NewUpgradeProposal creates a new IBC breaking upgrade proposal. func NewUpgradeProposal(title, description string, plan upgradetypes.Plan, upgradedClientState exported.ClientState) (govtypes.Content, error) { - any, err := PackClientState(upgradedClientState) + protoAny, err := PackClientState(upgradedClientState) if err != nil { return nil, err } @@ -81,7 +82,7 @@ func NewUpgradeProposal(title, description string, plan upgradetypes.Plan, upgra Title: title, Description: description, Plan: plan, - UpgradedClientState: any, + UpgradedClientState: protoAny, }, nil } @@ -108,12 +109,16 @@ func (up *UpgradeProposal) ValidateBasic() error { } if up.UpgradedClientState == nil { - return sdkerrors.Wrap(ErrInvalidUpgradeProposal, "upgraded client state cannot be nil") + return errorsmod.Wrap(ErrInvalidUpgradeProposal, "upgraded client state cannot be nil") } - _, err := UnpackClientState(up.UpgradedClientState) + clientState, err := UnpackClientState(up.UpgradedClientState) if err != nil { - return sdkerrors.Wrap(err, "failed to unpack upgraded client state") + return errorsmod.Wrap(err, "failed to unpack upgraded client state") + } + + if !reflect.DeepEqual(clientState, clientState.ZeroCustomFields()) { + return errorsmod.Wrap(ErrInvalidUpgradeProposal, "upgraded client state is not zeroed out") } return nil diff --git a/modules/core/02-client/types/proposal_test.go b/modules/core/02-client/types/proposal_test.go index c722cc3c886..8009e4ebd8b 100644 --- a/modules/core/02-client/types/proposal_test.go +++ b/modules/core/02-client/types/proposal_test.go @@ -5,12 +5,12 @@ import ( "github.com/cosmos/cosmos-sdk/codec" codectypes "github.com/cosmos/cosmos-sdk/codec/types" - govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" - "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - ibctmtypes "github.com/cosmos/ibc-go/v4/modules/light-clients/07-tendermint/types" - ibctesting "github.com/cosmos/ibc-go/v4/testing" + "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" + ibctesting "github.com/cosmos/ibc-go/v7/testing" ) func (suite *TypesTestSuite) TestValidateBasic() { @@ -109,20 +109,30 @@ func (suite *TypesTestSuite) TestUpgradeProposalValidateBasic() { }{ { "success", func() { - proposal, err = types.NewUpgradeProposal(ibctesting.Title, ibctesting.Description, plan, cs) + proposal, err = types.NewUpgradeProposal(ibctesting.Title, ibctesting.Description, plan, cs.ZeroCustomFields()) suite.Require().NoError(err) }, true, }, { "fails validate abstract - empty title", func() { - proposal, err = types.NewUpgradeProposal("", ibctesting.Description, plan, cs) + proposal, err = types.NewUpgradeProposal("", ibctesting.Description, plan, cs.ZeroCustomFields()) + suite.Require().NoError(err) + }, false, + }, + { + "non zeroed fields", func() { + proposal, err = types.NewUpgradeProposal(ibctesting.Title, ibctesting.Description, plan, &ibctm.ClientState{ + FrozenHeight: types.Height{ + RevisionHeight: 10, + }, + }) suite.Require().NoError(err) }, false, }, { "plan height is zero", func() { invalidPlan := upgradetypes.Plan{Name: "ibc upgrade", Height: 0} - proposal, err = types.NewUpgradeProposal(ibctesting.Title, ibctesting.Description, invalidPlan, cs) + proposal, err = types.NewUpgradeProposal(ibctesting.Title, ibctesting.Description, invalidPlan, cs.ZeroCustomFields()) suite.Require().NoError(err) }, false, }, @@ -138,14 +148,14 @@ func (suite *TypesTestSuite) TestUpgradeProposalValidateBasic() { }, { "failed to unpack client state", func() { - any, err := types.PackConsensusState(&ibctmtypes.ConsensusState{}) + protoAny, err := types.PackConsensusState(&ibctm.ConsensusState{}) suite.Require().NoError(err) proposal = &types.UpgradeProposal{ Title: ibctesting.Title, Description: ibctesting.Description, Plan: plan, - UpgradedClientState: any, + UpgradedClientState: protoAny, } }, false, }, @@ -173,7 +183,7 @@ func (suite *TypesTestSuite) TestMarshalUpgradeProposal() { Name: "upgrade ibc", Height: 1000, } - content, err := types.NewUpgradeProposal("title", "description", plan, &ibctmtypes.ClientState{}) + content, err := types.NewUpgradeProposal("title", "description", plan, &ibctm.ClientState{}) suite.Require().NoError(err) up, ok := content.(*types.UpgradeProposal) @@ -183,7 +193,7 @@ func (suite *TypesTestSuite) TestMarshalUpgradeProposal() { ir := codectypes.NewInterfaceRegistry() types.RegisterInterfaces(ir) govtypes.RegisterInterfaces(ir) - ibctmtypes.RegisterInterfaces(ir) + ibctm.RegisterInterfaces(ir) cdc := codec.NewProtoCodec(ir) // marshal message @@ -207,10 +217,10 @@ func (suite *TypesTestSuite) TestUpgradeString() { Height: 1000, } - proposal, err := types.NewUpgradeProposal(ibctesting.Title, ibctesting.Description, plan, &ibctmtypes.ClientState{}) + proposal, err := types.NewUpgradeProposal(ibctesting.Title, ibctesting.Description, plan, &ibctm.ClientState{}) suite.Require().NoError(err) - expect := fmt.Sprintf("IBC Upgrade Proposal\n Title: title\n Description: description\n Upgrade Plan\n Name: ibc upgrade\n height: 1000\n Info: https://foo.bar/baz.\n Upgraded IBC Client: %s", &ibctmtypes.ClientState{}) + expect := fmt.Sprintf("IBC Upgrade Proposal\n Title: title\n Description: description\n Upgrade Plan\n Name: ibc upgrade\n height: 1000\n Info: https://foo.bar/baz.\n Upgraded IBC Client: %s", &ibctm.ClientState{}) suite.Require().Equal(expect, proposal.String()) } diff --git a/modules/core/02-client/types/query.go b/modules/core/02-client/types/query.go index 81e80dc8793..6bf1fb5c7ba 100644 --- a/modules/core/02-client/types/query.go +++ b/modules/core/02-client/types/query.go @@ -3,7 +3,7 @@ package types import ( codectypes "github.com/cosmos/cosmos-sdk/codec/types" - "github.com/cosmos/ibc-go/v4/modules/core/exported" + "github.com/cosmos/ibc-go/v7/modules/core/exported" ) var ( diff --git a/modules/core/02-client/types/query.pb.go b/modules/core/02-client/types/query.pb.go index 9b733d70b51..727629ff673 100644 --- a/modules/core/02-client/types/query.pb.go +++ b/modules/core/02-client/types/query.pb.go @@ -8,9 +8,9 @@ import ( fmt "fmt" types "github.com/cosmos/cosmos-sdk/codec/types" query "github.com/cosmos/cosmos-sdk/types/query" - _ "github.com/gogo/protobuf/gogoproto" - grpc1 "github.com/gogo/protobuf/grpc" - proto "github.com/gogo/protobuf/proto" + _ "github.com/cosmos/gogoproto/gogoproto" + grpc1 "github.com/cosmos/gogoproto/grpc" + proto "github.com/cosmos/gogoproto/proto" _ "google.golang.org/genproto/googleapis/api/annotations" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" @@ -984,73 +984,73 @@ func init() { func init() { proto.RegisterFile("ibc/core/client/v1/query.proto", fileDescriptor_dc42cdfd1d52d76e) } var fileDescriptor_dc42cdfd1d52d76e = []byte{ - // 1056 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x57, 0xcd, 0x6f, 0x1b, 0x45, - 0x14, 0xcf, 0xa4, 0x69, 0xd4, 0x3e, 0xbb, 0x09, 0x9a, 0xe6, 0xc3, 0xdd, 0x16, 0xc7, 0xd9, 0x20, - 0x9a, 0x96, 0x64, 0x27, 0x71, 0xda, 0x24, 0x42, 0x42, 0x82, 0x54, 0x2a, 0xed, 0xa5, 0x94, 0x45, - 0x08, 0x84, 0x84, 0xa2, 0xdd, 0xf5, 0x64, 0xb3, 0x92, 0xbd, 0xe3, 0x7a, 0x76, 0x2d, 0x45, 0x55, - 0x2e, 0x3d, 0x21, 0x4e, 0x48, 0x48, 0x5c, 0x91, 0x38, 0x72, 0xa8, 0x38, 0x20, 0x71, 0xe5, 0x04, - 0x39, 0x70, 0xa8, 0x04, 0x07, 0x4e, 0x14, 0x25, 0xfc, 0x21, 0xc8, 0x33, 0xb3, 0xf6, 0xae, 0x3d, - 0xae, 0xd7, 0xa8, 0xf4, 0xb6, 0xfb, 0x3e, 0x7f, 0xef, 0xf7, 0x9e, 0xdf, 0x5b, 0x43, 0x39, 0x70, - 0x3d, 0xe2, 0xb1, 0x16, 0x25, 0x5e, 0x3d, 0xa0, 0x61, 0x44, 0xda, 0x9b, 0xe4, 0x51, 0x4c, 0x5b, - 0x47, 0x56, 0xb3, 0xc5, 0x22, 0x86, 0x71, 0xe0, 0x7a, 0x56, 0x47, 0x6f, 0x49, 0xbd, 0xd5, 0xde, - 0x34, 0x6e, 0x7a, 0x8c, 0x37, 0x18, 0x27, 0xae, 0xc3, 0xa9, 0x34, 0x26, 0xed, 0x4d, 0x97, 0x46, - 0xce, 0x26, 0x69, 0x3a, 0x7e, 0x10, 0x3a, 0x51, 0xc0, 0x42, 0xe9, 0x6f, 0x2c, 0x69, 0xe2, 0xab, - 0x48, 0xd2, 0xe0, 0x8a, 0xcf, 0x98, 0x5f, 0xa7, 0x44, 0xbc, 0xb9, 0xf1, 0x01, 0x71, 0x42, 0x95, - 0xdb, 0xb8, 0xa6, 0x54, 0x4e, 0x33, 0x20, 0x4e, 0x18, 0xb2, 0x48, 0x04, 0xe6, 0x4a, 0x3b, 0xe7, - 0x33, 0x9f, 0x89, 0x47, 0xd2, 0x79, 0x92, 0x52, 0x73, 0x1b, 0x16, 0x3f, 0xec, 0x20, 0xba, 0x23, - 0x72, 0x7c, 0x14, 0x39, 0x11, 0xb5, 0xe9, 0xa3, 0x98, 0xf2, 0x08, 0x5f, 0x85, 0x8b, 0x32, 0xf3, - 0x7e, 0x50, 0x2b, 0xa1, 0x0a, 0x5a, 0xbd, 0x68, 0x5f, 0x90, 0x82, 0xfb, 0x35, 0xf3, 0x29, 0x82, - 0xd2, 0xa0, 0x23, 0x6f, 0xb2, 0x90, 0x53, 0xbc, 0x03, 0x45, 0xe5, 0xc9, 0x3b, 0x72, 0xe1, 0x5c, - 0xa8, 0xce, 0x59, 0x12, 0x9f, 0x95, 0x40, 0xb7, 0xde, 0x0b, 0x8f, 0xec, 0x82, 0xd7, 0x0b, 0x80, - 0xe7, 0xe0, 0x7c, 0xb3, 0xc5, 0xd8, 0x41, 0x69, 0xb2, 0x82, 0x56, 0x8b, 0xb6, 0x7c, 0xc1, 0x77, - 0xa0, 0x28, 0x1e, 0xf6, 0x0f, 0x69, 0xe0, 0x1f, 0x46, 0xa5, 0x73, 0x22, 0x9c, 0x61, 0x0d, 0x52, - 0x6d, 0xdd, 0x13, 0x16, 0x7b, 0x53, 0x27, 0x7f, 0x2d, 0x4d, 0xd8, 0x05, 0xe1, 0x25, 0x45, 0xa6, - 0x3b, 0x88, 0x97, 0x27, 0x95, 0xde, 0x05, 0xe8, 0x35, 0x42, 0xa1, 0x7d, 0xd3, 0x92, 0x5d, 0xb3, - 0x3a, 0x5d, 0xb3, 0x64, 0x8b, 0x55, 0xd7, 0xac, 0x87, 0x8e, 0x9f, 0xb0, 0x64, 0xa7, 0x3c, 0xcd, - 0x3f, 0x10, 0x5c, 0xd1, 0x24, 0x51, 0xac, 0x84, 0x70, 0x29, 0xcd, 0x0a, 0x2f, 0xa1, 0xca, 0xb9, - 0xd5, 0x42, 0xf5, 0x86, 0xae, 0x8e, 0xfb, 0x35, 0x1a, 0x46, 0xc1, 0x41, 0x40, 0x6b, 0xa9, 0x50, - 0x7b, 0xe5, 0x4e, 0x59, 0xdf, 0x3f, 0x5f, 0x5a, 0xd0, 0xaa, 0xb9, 0x5d, 0x4c, 0x71, 0xc9, 0xf1, - 0xfb, 0x99, 0xaa, 0x26, 0x45, 0x55, 0xd7, 0x47, 0x56, 0x25, 0xc1, 0x66, 0xca, 0xfa, 0x01, 0x81, - 0x21, 0xcb, 0xea, 0xa8, 0x42, 0x1e, 0xf3, 0xdc, 0x73, 0x82, 0xaf, 0xc3, 0x6c, 0x8b, 0xb6, 0x03, - 0x1e, 0xb0, 0x70, 0x3f, 0x8c, 0x1b, 0x2e, 0x6d, 0x09, 0x24, 0x53, 0xf6, 0x4c, 0x22, 0x7e, 0x20, - 0xa4, 0x19, 0xc3, 0x54, 0x9f, 0x53, 0x86, 0xb2, 0x91, 0x78, 0x05, 0x2e, 0xd5, 0x3b, 0xf5, 0x45, - 0x89, 0xd9, 0x54, 0x05, 0xad, 0x5e, 0xb0, 0x8b, 0x52, 0xa8, 0xba, 0xfd, 0x13, 0x82, 0xab, 0x5a, - 0xc8, 0xaa, 0x17, 0xef, 0xc0, 0xac, 0x97, 0x68, 0x72, 0x0c, 0xe9, 0x8c, 0x97, 0x09, 0xf3, 0x7f, - 0xce, 0xe9, 0x13, 0x3d, 0x72, 0x9e, 0x8b, 0xed, 0xbb, 0x9a, 0x96, 0xff, 0x97, 0x41, 0xfe, 0x05, - 0xc1, 0x35, 0x3d, 0x08, 0xc5, 0xdf, 0xe7, 0xf0, 0x5a, 0x1f, 0x7f, 0xc9, 0x38, 0xaf, 0xe9, 0xca, - 0xcd, 0x86, 0xf9, 0x24, 0x88, 0x0e, 0x33, 0x04, 0xcc, 0x66, 0xe9, 0x7d, 0x89, 0xa3, 0xfb, 0x05, - 0x82, 0x65, 0x4d, 0x21, 0x32, 0xfb, 0xab, 0xe5, 0xf4, 0x57, 0x04, 0xe6, 0x8b, 0xa0, 0x28, 0x66, - 0x3f, 0x85, 0xc5, 0x3e, 0x66, 0xd5, 0x38, 0x25, 0x04, 0x8f, 0x9e, 0xa7, 0x79, 0x4f, 0x97, 0xe1, - 0xe5, 0x91, 0xba, 0x33, 0xb0, 0x4a, 0xe3, 0x5c, 0x54, 0x9a, 0x5b, 0x03, 0xeb, 0x31, 0xee, 0x15, - 0xbe, 0x00, 0xd3, 0x5c, 0x48, 0x94, 0x9b, 0x7a, 0x33, 0x8d, 0x4c, 0xb6, 0x87, 0x4e, 0xcb, 0x69, - 0x24, 0xd9, 0xcc, 0x0f, 0x32, 0x01, 0x13, 0x9d, 0x0a, 0x58, 0x85, 0xe9, 0xa6, 0x90, 0xa8, 0x9f, - 0xb6, 0x96, 0x38, 0xe5, 0xa3, 0x2c, 0xcd, 0x65, 0x58, 0x12, 0x01, 0x3f, 0x6e, 0xfa, 0x2d, 0xa7, - 0x96, 0x59, 0xaf, 0x49, 0xce, 0x3a, 0x54, 0x86, 0x9b, 0xa8, 0xd4, 0xf7, 0x60, 0x3e, 0x56, 0xea, - 0xfd, 0xdc, 0x97, 0xf0, 0x72, 0x3c, 0x18, 0xd1, 0x7c, 0x43, 0x0d, 0x4d, 0x37, 0x9b, 0x6e, 0x05, - 0x9b, 0x31, 0xac, 0xbc, 0xd0, 0x4a, 0xc1, 0x7a, 0x00, 0xa5, 0x1e, 0xac, 0x31, 0xd6, 0xdf, 0x42, - 0xac, 0x8d, 0x5b, 0xfd, 0xad, 0x08, 0xe7, 0x45, 0x5e, 0xfc, 0x2d, 0x82, 0x42, 0x0a, 0x36, 0x7e, - 0x4b, 0xc7, 0xf5, 0x90, 0x0f, 0x0d, 0x63, 0x2d, 0x9f, 0xb1, 0x2c, 0xc2, 0xbc, 0xfd, 0xe4, 0xf7, - 0x7f, 0xbe, 0x9e, 0x24, 0x78, 0x9d, 0x0c, 0xfd, 0x54, 0x52, 0x1b, 0x89, 0x3c, 0xee, 0x8e, 0xe2, - 0x31, 0xfe, 0x06, 0x41, 0x31, 0x7d, 0x2c, 0x71, 0xae, 0xac, 0xc9, 0xa4, 0x19, 0xeb, 0x39, 0xad, - 0x15, 0xc8, 0x1b, 0x02, 0xe4, 0x0a, 0x5e, 0x1e, 0x09, 0x12, 0x3f, 0x47, 0x30, 0x93, 0xe5, 0x15, - 0x5b, 0xc3, 0x93, 0xe9, 0xda, 0x6f, 0x90, 0xdc, 0xf6, 0x0a, 0x5e, 0x5d, 0xc0, 0x3b, 0xc0, 0x35, - 0x2d, 0xbc, 0xbe, 0xc5, 0x9e, 0xa6, 0x91, 0x24, 0xc7, 0x98, 0x3c, 0xee, 0x3b, 0xeb, 0xc7, 0x44, - 0xae, 0xa9, 0x94, 0x42, 0x0a, 0x8e, 0xf1, 0x53, 0x04, 0xb3, 0x7d, 0x87, 0x04, 0xe7, 0x85, 0xdc, - 0x6d, 0xc0, 0x46, 0x7e, 0x07, 0x55, 0xe4, 0xae, 0x28, 0xb2, 0x8a, 0x37, 0xc6, 0x2d, 0x12, 0x9f, - 0x20, 0x98, 0xd7, 0x6e, 0x69, 0x7c, 0x3b, 0x27, 0x8a, 0xec, 0x81, 0x31, 0xb6, 0xc7, 0x75, 0x53, - 0x25, 0xbc, 0x2b, 0x4a, 0x78, 0x1b, 0xef, 0x8e, 0xdd, 0x27, 0x75, 0x33, 0xf0, 0x77, 0x99, 0xb1, - 0x8f, 0xf3, 0x8d, 0x7d, 0x3c, 0xd6, 0xd8, 0xf7, 0x76, 0x78, 0xee, 0xdf, 0x66, 0x9c, 0xe5, 0xfb, - 0xcb, 0x2e, 0x48, 0xb9, 0x8e, 0x47, 0x82, 0xcc, 0x5c, 0x81, 0x91, 0x20, 0xb3, 0x77, 0xc1, 0x7c, - 0x5d, 0x80, 0x5c, 0xc4, 0xf3, 0x12, 0x64, 0x17, 0x9f, 0x3c, 0x01, 0xf8, 0x47, 0x04, 0x97, 0x35, - 0xbb, 0x1d, 0x6f, 0x0d, 0xcd, 0x32, 0xfc, 0x58, 0x18, 0xb7, 0xc6, 0x73, 0x52, 0x08, 0xab, 0x02, - 0xe1, 0x1a, 0xbe, 0xa9, 0xa3, 0x51, 0x7b, 0x58, 0x38, 0xfe, 0x19, 0xc1, 0x82, 0x7e, 0xfd, 0xe3, - 0xed, 0xd1, 0x20, 0xb4, 0x6b, 0x65, 0x67, 0x6c, 0xbf, 0x3c, 0x63, 0x30, 0xec, 0x02, 0xf1, 0x3d, - 0xfb, 0xe4, 0xb4, 0x8c, 0x9e, 0x9d, 0x96, 0xd1, 0xdf, 0xa7, 0x65, 0xf4, 0xd5, 0x59, 0x79, 0xe2, - 0xd9, 0x59, 0x79, 0xe2, 0xcf, 0xb3, 0xf2, 0xc4, 0x67, 0xbb, 0x7e, 0x10, 0x1d, 0xc6, 0xae, 0xe5, - 0xb1, 0x06, 0x51, 0x7f, 0xa6, 0x03, 0xd7, 0x5b, 0xf7, 0x19, 0x69, 0xdf, 0x22, 0x0d, 0x56, 0x8b, - 0xeb, 0x94, 0xcb, 0x3c, 0x1b, 0xd5, 0x75, 0x95, 0x2a, 0x3a, 0x6a, 0x52, 0xee, 0x4e, 0x8b, 0x43, - 0xb6, 0xf5, 0x6f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x6e, 0xea, 0xa4, 0x42, 0xb8, 0x0f, 0x00, 0x00, + // 1051 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x57, 0xcf, 0x6f, 0x1b, 0x45, + 0x14, 0xce, 0xa4, 0x69, 0xd4, 0x3e, 0xbb, 0x09, 0x9a, 0x26, 0xa9, 0xbb, 0x8d, 0x1c, 0x67, 0x83, + 0x68, 0x5a, 0x92, 0x9d, 0xc4, 0xa1, 0x49, 0x84, 0x84, 0x04, 0xa9, 0x54, 0xda, 0x4b, 0x29, 0x8b, + 0x10, 0x08, 0x09, 0x45, 0xbb, 0xeb, 0xc9, 0x66, 0x25, 0x7b, 0xc7, 0xf5, 0xec, 0x5a, 0x8a, 0xaa, + 0x5c, 0x7a, 0xe2, 0x06, 0x12, 0x12, 0x57, 0x24, 0x8e, 0x1c, 0x2a, 0x0e, 0x48, 0x5c, 0x39, 0x41, + 0x8e, 0x45, 0x70, 0xe0, 0x44, 0x51, 0xc2, 0x1f, 0x82, 0x3c, 0x33, 0x6b, 0xef, 0xda, 0xe3, 0x7a, + 0x8d, 0x42, 0x6f, 0xbb, 0xef, 0xe7, 0xf7, 0xbe, 0xf7, 0xfc, 0xde, 0x1a, 0xca, 0x81, 0xeb, 0x11, + 0x8f, 0xb5, 0x28, 0xf1, 0xea, 0x01, 0x0d, 0x23, 0xd2, 0xde, 0x24, 0x8f, 0x63, 0xda, 0x3a, 0xb2, + 0x9a, 0x2d, 0x16, 0x31, 0x8c, 0x03, 0xd7, 0xb3, 0x3a, 0x7a, 0x4b, 0xea, 0xad, 0xf6, 0xa6, 0x71, + 0xdb, 0x63, 0xbc, 0xc1, 0x38, 0x71, 0x1d, 0x4e, 0xa5, 0x31, 0x69, 0x6f, 0xba, 0x34, 0x72, 0x36, + 0x49, 0xd3, 0xf1, 0x83, 0xd0, 0x89, 0x02, 0x16, 0x4a, 0x7f, 0x63, 0x49, 0x13, 0x5f, 0x45, 0x92, + 0x06, 0xd7, 0x7d, 0xc6, 0xfc, 0x3a, 0x25, 0xe2, 0xcd, 0x8d, 0x0f, 0x88, 0x13, 0xaa, 0xdc, 0xc6, + 0xa2, 0x52, 0x39, 0xcd, 0x80, 0x38, 0x61, 0xc8, 0x22, 0x11, 0x98, 0x2b, 0xed, 0x9c, 0xcf, 0x7c, + 0x26, 0x1e, 0x49, 0xe7, 0x49, 0x4a, 0xcd, 0x6d, 0xb8, 0xf6, 0x61, 0x07, 0xd1, 0x5d, 0x91, 0xe3, + 0xa3, 0xc8, 0x89, 0xa8, 0x4d, 0x1f, 0xc7, 0x94, 0x47, 0xf8, 0x06, 0x5c, 0x96, 0x99, 0xf7, 0x83, + 0x5a, 0x09, 0x55, 0xd0, 0xea, 0x65, 0xfb, 0x92, 0x14, 0x3c, 0xa8, 0x99, 0xcf, 0x10, 0x94, 0x06, + 0x1d, 0x79, 0x93, 0x85, 0x9c, 0xe2, 0x1d, 0x28, 0x2a, 0x4f, 0xde, 0x91, 0x0b, 0xe7, 0x42, 0x75, + 0xce, 0x92, 0xf8, 0xac, 0x04, 0xba, 0xf5, 0x5e, 0x78, 0x64, 0x17, 0xbc, 0x5e, 0x00, 0x3c, 0x07, + 0x17, 0x9b, 0x2d, 0xc6, 0x0e, 0x4a, 0x93, 0x15, 0xb4, 0x5a, 0xb4, 0xe5, 0x0b, 0xbe, 0x0b, 0x45, + 0xf1, 0xb0, 0x7f, 0x48, 0x03, 0xff, 0x30, 0x2a, 0x5d, 0x10, 0xe1, 0x0c, 0x6b, 0x90, 0x6a, 0xeb, + 0xbe, 0xb0, 0xd8, 0x9b, 0x3a, 0xf9, 0x6b, 0x69, 0xc2, 0x2e, 0x08, 0x2f, 0x29, 0x32, 0xdd, 0x41, + 0xbc, 0x3c, 0xa9, 0xf4, 0x1e, 0x40, 0xaf, 0x11, 0x0a, 0xed, 0x1b, 0x96, 0xec, 0x9a, 0xd5, 0xe9, + 0x9a, 0x25, 0x5b, 0xac, 0xba, 0x66, 0x3d, 0x72, 0xfc, 0x84, 0x25, 0x3b, 0xe5, 0x69, 0xfe, 0x81, + 0xe0, 0xba, 0x26, 0x89, 0x62, 0x25, 0x84, 0x2b, 0x69, 0x56, 0x78, 0x09, 0x55, 0x2e, 0xac, 0x16, + 0xaa, 0xb7, 0x74, 0x75, 0x3c, 0xa8, 0xd1, 0x30, 0x0a, 0x0e, 0x02, 0x5a, 0x4b, 0x85, 0xda, 0x2b, + 0x77, 0xca, 0xfa, 0xfe, 0xc5, 0xd2, 0x82, 0x56, 0xcd, 0xed, 0x62, 0x8a, 0x4b, 0x8e, 0xdf, 0xcf, + 0x54, 0x35, 0x29, 0xaa, 0xba, 0x39, 0xb2, 0x2a, 0x09, 0x36, 0x53, 0xd6, 0x0f, 0x08, 0x0c, 0x59, + 0x56, 0x47, 0x15, 0xf2, 0x98, 0xe7, 0x9e, 0x13, 0x7c, 0x13, 0x66, 0x5b, 0xb4, 0x1d, 0xf0, 0x80, + 0x85, 0xfb, 0x61, 0xdc, 0x70, 0x69, 0x4b, 0x20, 0x99, 0xb2, 0x67, 0x12, 0xf1, 0x43, 0x21, 0xcd, + 0x18, 0xa6, 0xfa, 0x9c, 0x32, 0x94, 0x8d, 0xc4, 0x2b, 0x70, 0xa5, 0xde, 0xa9, 0x2f, 0x4a, 0xcc, + 0xa6, 0x2a, 0x68, 0xf5, 0x92, 0x5d, 0x94, 0x42, 0xd5, 0xed, 0x9f, 0x10, 0xdc, 0xd0, 0x42, 0x56, + 0xbd, 0x78, 0x07, 0x66, 0xbd, 0x44, 0x93, 0x63, 0x48, 0x67, 0xbc, 0x4c, 0x98, 0xff, 0x73, 0x4e, + 0x9f, 0xea, 0x91, 0xf3, 0x5c, 0x6c, 0xdf, 0xd3, 0xb4, 0xfc, 0xbf, 0x0c, 0xf2, 0x2f, 0x08, 0x16, + 0xf5, 0x20, 0x14, 0x7f, 0x9f, 0xc3, 0x6b, 0x7d, 0xfc, 0x25, 0xe3, 0xbc, 0xa6, 0x2b, 0x37, 0x1b, + 0xe6, 0x93, 0x20, 0x3a, 0xcc, 0x10, 0x30, 0x9b, 0xa5, 0xf7, 0x1c, 0x47, 0xf7, 0x0b, 0x04, 0xcb, + 0x9a, 0x42, 0x64, 0xf6, 0x57, 0xcb, 0xe9, 0xaf, 0x08, 0xcc, 0x97, 0x41, 0x51, 0xcc, 0x7e, 0x0a, + 0xd7, 0xfa, 0x98, 0x55, 0xe3, 0x94, 0x10, 0x3c, 0x7a, 0x9e, 0xe6, 0x3d, 0x5d, 0x86, 0xf3, 0x23, + 0x75, 0x67, 0x60, 0x95, 0xc6, 0xb9, 0xa8, 0x34, 0xb7, 0x06, 0xd6, 0x63, 0xdc, 0x2b, 0x7c, 0x01, + 0xa6, 0xb9, 0x90, 0x28, 0x37, 0xf5, 0x66, 0x1a, 0x99, 0x6c, 0x8f, 0x9c, 0x96, 0xd3, 0x48, 0xb2, + 0x99, 0x1f, 0x64, 0x02, 0x26, 0x3a, 0x15, 0xb0, 0x0a, 0xd3, 0x4d, 0x21, 0x51, 0x3f, 0x6d, 0x2d, + 0x71, 0xca, 0x47, 0x59, 0x9a, 0xcb, 0xb0, 0x24, 0x02, 0x7e, 0xdc, 0xf4, 0x5b, 0x4e, 0x2d, 0xb3, + 0x5e, 0x93, 0x9c, 0x75, 0xa8, 0x0c, 0x37, 0x51, 0xa9, 0xef, 0xc3, 0x7c, 0xac, 0xd4, 0xfb, 0xb9, + 0x2f, 0xe1, 0xd5, 0x78, 0x30, 0xa2, 0xf9, 0xba, 0x1a, 0x9a, 0x6e, 0x36, 0xdd, 0x0a, 0x36, 0x63, + 0x58, 0x79, 0xa9, 0x95, 0x82, 0xf5, 0x10, 0x4a, 0x3d, 0x58, 0x63, 0xac, 0xbf, 0x85, 0x58, 0x1b, + 0xb7, 0xfa, 0x5b, 0x11, 0x2e, 0x8a, 0xbc, 0xf8, 0x5b, 0x04, 0x85, 0x14, 0x6c, 0xfc, 0xa6, 0x8e, + 0xeb, 0x21, 0x1f, 0x1a, 0xc6, 0x5a, 0x3e, 0x63, 0x59, 0x84, 0x79, 0xe7, 0xe9, 0xef, 0xff, 0x7c, + 0x3d, 0x49, 0xf0, 0x3a, 0x19, 0xfa, 0xa9, 0xa4, 0x36, 0x12, 0x79, 0xd2, 0x1d, 0xc5, 0x63, 0xfc, + 0x0d, 0x82, 0x62, 0xfa, 0x58, 0xe2, 0x5c, 0x59, 0x93, 0x49, 0x33, 0xd6, 0x73, 0x5a, 0x2b, 0x90, + 0xb7, 0x04, 0xc8, 0x15, 0xbc, 0x3c, 0x12, 0x24, 0x7e, 0x81, 0x60, 0x26, 0xcb, 0x2b, 0xb6, 0x86, + 0x27, 0xd3, 0xb5, 0xdf, 0x20, 0xb9, 0xed, 0x15, 0xbc, 0xba, 0x80, 0x77, 0x80, 0x6b, 0x5a, 0x78, + 0x7d, 0x8b, 0x3d, 0x4d, 0x23, 0x49, 0x8e, 0x31, 0x79, 0xd2, 0x77, 0xd6, 0x8f, 0x89, 0x5c, 0x53, + 0x29, 0x85, 0x14, 0x1c, 0xe3, 0x67, 0x08, 0x66, 0xfb, 0x0e, 0x09, 0xce, 0x0b, 0xb9, 0xdb, 0x80, + 0x8d, 0xfc, 0x0e, 0xaa, 0xc8, 0x5d, 0x51, 0x64, 0x15, 0x6f, 0x8c, 0x5b, 0x24, 0x3e, 0x41, 0x30, + 0xaf, 0xdd, 0xd2, 0xf8, 0x4e, 0x4e, 0x14, 0xd9, 0x03, 0x63, 0x6c, 0x8f, 0xeb, 0xa6, 0x4a, 0x78, + 0x57, 0x94, 0xf0, 0x36, 0xde, 0x1d, 0xbb, 0x4f, 0xea, 0x66, 0xe0, 0xef, 0x32, 0x63, 0x1f, 0xe7, + 0x1b, 0xfb, 0x78, 0xac, 0xb1, 0xef, 0xed, 0xf0, 0xdc, 0xbf, 0xcd, 0x38, 0xcb, 0xf7, 0x97, 0x5d, + 0x90, 0x72, 0x1d, 0x8f, 0x04, 0x99, 0xb9, 0x02, 0x23, 0x41, 0x66, 0xef, 0x82, 0x69, 0x0a, 0x90, + 0x8b, 0xd8, 0xd0, 0x81, 0x94, 0x77, 0x00, 0xff, 0x88, 0xe0, 0xaa, 0x66, 0xc1, 0xe3, 0xad, 0xa1, + 0xa9, 0x86, 0x5f, 0x0c, 0xe3, 0xad, 0xf1, 0x9c, 0x14, 0xcc, 0xaa, 0x80, 0xb9, 0x86, 0x6f, 0xeb, + 0x60, 0x6a, 0xaf, 0x0b, 0xc7, 0x3f, 0x23, 0x58, 0xd0, 0xdf, 0x00, 0xbc, 0x3d, 0x1a, 0x84, 0x76, + 0xb7, 0xec, 0x8c, 0xed, 0x97, 0x67, 0x16, 0x86, 0x9d, 0x21, 0xbe, 0x67, 0x9f, 0x9c, 0x96, 0xd1, + 0xf3, 0xd3, 0x32, 0xfa, 0xfb, 0xb4, 0x8c, 0xbe, 0x3a, 0x2b, 0x4f, 0x3c, 0x3f, 0x2b, 0x4f, 0xfc, + 0x79, 0x56, 0x9e, 0xf8, 0x6c, 0xd7, 0x0f, 0xa2, 0xc3, 0xd8, 0xb5, 0x3c, 0xd6, 0x20, 0xea, 0x1f, + 0x75, 0xe0, 0x7a, 0xeb, 0x3e, 0x23, 0xed, 0x1d, 0xd2, 0x60, 0xb5, 0xb8, 0x4e, 0xb9, 0xcc, 0xb3, + 0x51, 0x5d, 0x57, 0xa9, 0xa2, 0xa3, 0x26, 0xe5, 0xee, 0xb4, 0xb8, 0x66, 0x5b, 0xff, 0x06, 0x00, + 0x00, 0xff, 0xff, 0xd5, 0xf9, 0x22, 0x36, 0xbd, 0x0f, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -1079,7 +1079,7 @@ type QueryClient interface { ConsensusStateHeights(ctx context.Context, in *QueryConsensusStateHeightsRequest, opts ...grpc.CallOption) (*QueryConsensusStateHeightsResponse, error) // Status queries the status of an IBC client. ClientStatus(ctx context.Context, in *QueryClientStatusRequest, opts ...grpc.CallOption) (*QueryClientStatusResponse, error) - // ClientParams queries all parameters of the ibc client. + // ClientParams queries all parameters of the ibc client submodule. ClientParams(ctx context.Context, in *QueryClientParamsRequest, opts ...grpc.CallOption) (*QueryClientParamsResponse, error) // UpgradedClientState queries an Upgraded IBC light client. UpgradedClientState(ctx context.Context, in *QueryUpgradedClientStateRequest, opts ...grpc.CallOption) (*QueryUpgradedClientStateResponse, error) @@ -1192,7 +1192,7 @@ type QueryServer interface { ConsensusStateHeights(context.Context, *QueryConsensusStateHeightsRequest) (*QueryConsensusStateHeightsResponse, error) // Status queries the status of an IBC client. ClientStatus(context.Context, *QueryClientStatusRequest) (*QueryClientStatusResponse, error) - // ClientParams queries all parameters of the ibc client. + // ClientParams queries all parameters of the ibc client submodule. ClientParams(context.Context, *QueryClientParamsRequest) (*QueryClientParamsResponse, error) // UpgradedClientState queries an Upgraded IBC light client. UpgradedClientState(context.Context, *QueryUpgradedClientStateRequest) (*QueryUpgradedClientStateResponse, error) diff --git a/modules/core/02-client/types/query.pb.gw.go b/modules/core/02-client/types/query.pb.gw.go index 4286cc772ae..1e0e41f2579 100644 --- a/modules/core/02-client/types/query.pb.gw.go +++ b/modules/core/02-client/types/query.pb.gw.go @@ -20,6 +20,7 @@ import ( "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/grpclog" + "google.golang.org/grpc/metadata" "google.golang.org/grpc/status" ) @@ -30,6 +31,7 @@ var _ status.Status var _ = runtime.String var _ = utilities.NewDoubleArray var _ = descriptor.ForMessage +var _ = metadata.Join func request_Query_ClientState_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq QueryClientStateRequest @@ -492,12 +494,14 @@ func local_request_Query_UpgradedConsensusState_0(ctx context.Context, marshaler // RegisterQueryHandlerServer registers the http handlers for service Query to "mux". // UnaryRPC :call QueryServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. -// Note that using this registration option will cause many gRPC library features (such as grpc.SendHeader, etc) to stop working. Consider using RegisterQueryHandlerFromEndpoint instead. +// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterQueryHandlerFromEndpoint instead. func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, server QueryServer) error { mux.Handle("GET", pattern_Query_ClientState_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { @@ -505,6 +509,7 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv return } resp, md, err := local_request_Query_ClientState_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) @@ -518,6 +523,8 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv mux.Handle("GET", pattern_Query_ClientStates_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { @@ -525,6 +532,7 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv return } resp, md, err := local_request_Query_ClientStates_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) @@ -538,6 +546,8 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv mux.Handle("GET", pattern_Query_ConsensusState_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { @@ -545,6 +555,7 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv return } resp, md, err := local_request_Query_ConsensusState_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) @@ -558,6 +569,8 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv mux.Handle("GET", pattern_Query_ConsensusStates_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { @@ -565,6 +578,7 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv return } resp, md, err := local_request_Query_ConsensusStates_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) @@ -578,6 +592,8 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv mux.Handle("GET", pattern_Query_ConsensusStateHeights_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { @@ -585,6 +601,7 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv return } resp, md, err := local_request_Query_ConsensusStateHeights_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) @@ -598,6 +615,8 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv mux.Handle("GET", pattern_Query_ClientStatus_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { @@ -605,6 +624,7 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv return } resp, md, err := local_request_Query_ClientStatus_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) @@ -618,6 +638,8 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv mux.Handle("GET", pattern_Query_ClientParams_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { @@ -625,6 +647,7 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv return } resp, md, err := local_request_Query_ClientParams_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) @@ -638,6 +661,8 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv mux.Handle("GET", pattern_Query_UpgradedClientState_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { @@ -645,6 +670,7 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv return } resp, md, err := local_request_Query_UpgradedClientState_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) @@ -658,6 +684,8 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv mux.Handle("GET", pattern_Query_UpgradedConsensusState_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { @@ -665,6 +693,7 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv return } resp, md, err := local_request_Query_UpgradedConsensusState_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) @@ -900,23 +929,23 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie } var ( - pattern_Query_ClientState_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5}, []string{"ibc", "core", "client", "v1", "client_states", "client_id"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_ClientState_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5}, []string{"ibc", "core", "client", "v1", "client_states", "client_id"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_ClientStates_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"ibc", "core", "client", "v1", "client_states"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_ClientStates_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"ibc", "core", "client", "v1", "client_states"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_ConsensusState_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 2, 8, 1, 0, 4, 1, 5, 9}, []string{"ibc", "core", "client", "v1", "consensus_states", "client_id", "revision", "revision_number", "height", "revision_height"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_ConsensusState_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 2, 8, 1, 0, 4, 1, 5, 9}, []string{"ibc", "core", "client", "v1", "consensus_states", "client_id", "revision", "revision_number", "height", "revision_height"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_ConsensusStates_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5}, []string{"ibc", "core", "client", "v1", "consensus_states", "client_id"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_ConsensusStates_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5}, []string{"ibc", "core", "client", "v1", "consensus_states", "client_id"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_ConsensusStateHeights_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6}, []string{"ibc", "core", "client", "v1", "consensus_states", "client_id", "heights"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_ConsensusStateHeights_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6}, []string{"ibc", "core", "client", "v1", "consensus_states", "client_id", "heights"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_ClientStatus_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5}, []string{"ibc", "core", "client", "v1", "client_status", "client_id"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_ClientStatus_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5}, []string{"ibc", "core", "client", "v1", "client_status", "client_id"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_ClientParams_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"ibc", "client", "v1", "params"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_ClientParams_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"ibc", "core", "client", "v1", "params"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_UpgradedClientState_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"ibc", "core", "client", "v1", "upgraded_client_states"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_UpgradedClientState_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"ibc", "core", "client", "v1", "upgraded_client_states"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_UpgradedConsensusState_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"ibc", "core", "client", "v1", "upgraded_consensus_states"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_UpgradedConsensusState_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"ibc", "core", "client", "v1", "upgraded_consensus_states"}, "", runtime.AssumeColonVerbOpt(false))) ) var ( diff --git a/modules/core/02-client/types/tx.pb.go b/modules/core/02-client/types/tx.pb.go index 1ed94c0c49b..ce17ca96ad7 100644 --- a/modules/core/02-client/types/tx.pb.go +++ b/modules/core/02-client/types/tx.pb.go @@ -7,9 +7,9 @@ import ( context "context" fmt "fmt" types "github.com/cosmos/cosmos-sdk/codec/types" - _ "github.com/gogo/protobuf/gogoproto" - grpc1 "github.com/gogo/protobuf/grpc" - proto "github.com/gogo/protobuf/proto" + _ "github.com/cosmos/gogoproto/gogoproto" + grpc1 "github.com/cosmos/gogoproto/grpc" + proto "github.com/cosmos/gogoproto/proto" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" @@ -111,12 +111,12 @@ func (m *MsgCreateClientResponse) XXX_DiscardUnknown() { var xxx_messageInfo_MsgCreateClientResponse proto.InternalMessageInfo // MsgUpdateClient defines an sdk.Msg to update a IBC client state using -// the given header. +// the given client message. type MsgUpdateClient struct { // client unique identifier ClientId string `protobuf:"bytes,1,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty" yaml:"client_id"` - // header to update the light client - Header *types.Any `protobuf:"bytes,2,opt,name=header,proto3" json:"header,omitempty"` + // client message to update the light client + ClientMessage *types.Any `protobuf:"bytes,2,opt,name=client_message,json=clientMessage,proto3" json:"client_message,omitempty"` // signer address Signer string `protobuf:"bytes,3,opt,name=signer,proto3" json:"signer,omitempty"` } @@ -281,13 +281,14 @@ var xxx_messageInfo_MsgUpgradeClientResponse proto.InternalMessageInfo // MsgSubmitMisbehaviour defines an sdk.Msg type that submits Evidence for // light client misbehaviour. +// Warning: DEPRECATED type MsgSubmitMisbehaviour struct { // client unique identifier - ClientId string `protobuf:"bytes,1,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty" yaml:"client_id"` + ClientId string `protobuf:"bytes,1,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty" yaml:"client_id"` // Deprecated: Do not use. // misbehaviour used for freezing the light client - Misbehaviour *types.Any `protobuf:"bytes,2,opt,name=misbehaviour,proto3" json:"misbehaviour,omitempty"` + Misbehaviour *types.Any `protobuf:"bytes,2,opt,name=misbehaviour,proto3" json:"misbehaviour,omitempty"` // Deprecated: Do not use. // signer address - Signer string `protobuf:"bytes,3,opt,name=signer,proto3" json:"signer,omitempty"` + Signer string `protobuf:"bytes,3,opt,name=signer,proto3" json:"signer,omitempty"` // Deprecated: Do not use. } func (m *MsgSubmitMisbehaviour) Reset() { *m = MsgSubmitMisbehaviour{} } @@ -375,45 +376,46 @@ func init() { func init() { proto.RegisterFile("ibc/core/client/v1/tx.proto", fileDescriptor_cb5dc4651eb49a04) } var fileDescriptor_cb5dc4651eb49a04 = []byte{ - // 602 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x55, 0x3f, 0x6f, 0xd3, 0x4e, - 0x18, 0x8e, 0x9b, 0xdf, 0x2f, 0x6a, 0xae, 0x81, 0x56, 0x26, 0xb4, 0xa9, 0xab, 0xda, 0x91, 0xe9, - 0x10, 0x44, 0x7b, 0x47, 0x02, 0x43, 0xd5, 0x8d, 0x74, 0x62, 0x88, 0x04, 0xae, 0x18, 0x60, 0x09, - 0xfe, 0x73, 0xbd, 0x9c, 0x88, 0x7d, 0x91, 0xcf, 0x8e, 0xc8, 0x37, 0x60, 0x64, 0xe0, 0x03, 0x54, - 0x0c, 0x7c, 0x16, 0xc6, 0x0e, 0x0c, 0x4c, 0x51, 0x95, 0x2c, 0xcc, 0xf9, 0x04, 0x28, 0x3e, 0x27, - 0xc4, 0x6e, 0x1c, 0x45, 0xfc, 0xd9, 0x7c, 0x7e, 0x9f, 0x7b, 0x9e, 0xf7, 0xf1, 0xf3, 0x9e, 0x0f, - 0x1c, 0x50, 0xcb, 0x46, 0x36, 0xf3, 0x31, 0xb2, 0xbb, 0x14, 0x7b, 0x01, 0xea, 0xd7, 0x51, 0xf0, - 0x1e, 0xf6, 0x7c, 0x16, 0x30, 0x59, 0xa6, 0x96, 0x0d, 0xa7, 0x45, 0x28, 0x8a, 0xb0, 0x5f, 0x57, - 0xca, 0x84, 0x11, 0x16, 0x95, 0xd1, 0xf4, 0x49, 0x20, 0x95, 0x7d, 0xc2, 0x18, 0xe9, 0x62, 0x14, - 0xad, 0xac, 0xf0, 0x12, 0x99, 0xde, 0x40, 0x94, 0xf4, 0x1b, 0x09, 0x6c, 0xb7, 0x38, 0x39, 0xf7, - 0xb1, 0x19, 0xe0, 0xf3, 0x88, 0x47, 0x7e, 0x01, 0x4a, 0x82, 0xb1, 0xcd, 0x03, 0x33, 0xc0, 0x15, - 0xa9, 0x2a, 0xd5, 0xb6, 0x1a, 0x65, 0x28, 0x58, 0xe0, 0x8c, 0x05, 0x3e, 0xf3, 0x06, 0xcd, 0xbd, - 0xc9, 0x50, 0xbb, 0x37, 0x30, 0xdd, 0xee, 0x99, 0xbe, 0xb8, 0x47, 0x37, 0xb6, 0xc4, 0xf2, 0x62, - 0xba, 0x92, 0x5f, 0x83, 0x6d, 0x9b, 0x79, 0x1c, 0x7b, 0x3c, 0xe4, 0x31, 0xe9, 0xc6, 0x0a, 0x52, - 0x65, 0x32, 0xd4, 0x76, 0x63, 0xd2, 0xe4, 0x36, 0xdd, 0xb8, 0x3b, 0x7f, 0x23, 0xa8, 0x77, 0x41, - 0x81, 0x53, 0xe2, 0x61, 0xbf, 0x92, 0xaf, 0x4a, 0xb5, 0xa2, 0x11, 0xaf, 0xce, 0x36, 0x3f, 0x5c, - 0x69, 0xb9, 0x1f, 0x57, 0x5a, 0x4e, 0xdf, 0x07, 0x7b, 0x29, 0x87, 0x06, 0xe6, 0xbd, 0x29, 0x8b, - 0xfe, 0x49, 0xb8, 0x7f, 0xd5, 0x73, 0x7e, 0xb9, 0xaf, 0x83, 0x62, 0xec, 0x84, 0x3a, 0x91, 0xf5, - 0x62, 0xb3, 0x3c, 0x19, 0x6a, 0x3b, 0x09, 0x93, 0xd4, 0xd1, 0x8d, 0x4d, 0xf1, 0xfc, 0xdc, 0x91, - 0x8f, 0x41, 0xa1, 0x83, 0x4d, 0x07, 0xfb, 0xab, 0x5c, 0x19, 0x31, 0x66, 0xed, 0x8e, 0x17, 0xbb, - 0x9a, 0x77, 0xfc, 0x2d, 0x0f, 0x76, 0xa2, 0x1a, 0xf1, 0x4d, 0xe7, 0x0f, 0x5a, 0x4e, 0x67, 0xbc, - 0xf1, 0x2f, 0x32, 0xce, 0xff, 0xa5, 0x8c, 0x5f, 0x82, 0x72, 0xcf, 0x67, 0xec, 0xb2, 0x1d, 0x0a, - 0xdb, 0x6d, 0xa1, 0x5b, 0xf9, 0xaf, 0x2a, 0xd5, 0x4a, 0x4d, 0x6d, 0x32, 0xd4, 0x0e, 0x04, 0xd3, - 0x32, 0x94, 0x6e, 0xc8, 0xd1, 0xeb, 0xe4, 0x27, 0x7b, 0x07, 0x0e, 0x53, 0xe0, 0x54, 0xef, 0xff, - 0x47, 0xdc, 0xb5, 0xc9, 0x50, 0x3b, 0x5a, 0xca, 0x9d, 0xee, 0x59, 0x49, 0x88, 0x64, 0xcd, 0x68, - 0x21, 0x23, 0x71, 0x05, 0x54, 0xd2, 0xa9, 0xce, 0x23, 0xff, 0x22, 0x81, 0xfb, 0x2d, 0x4e, 0x2e, - 0x42, 0xcb, 0xa5, 0x41, 0x8b, 0x72, 0x0b, 0x77, 0xcc, 0x3e, 0x65, 0xa1, 0xff, 0x3b, 0xb9, 0x9f, - 0x82, 0x92, 0xbb, 0x40, 0xb1, 0x72, 0x60, 0x13, 0xc8, 0x35, 0xc6, 0x56, 0x03, 0x87, 0x4b, 0xfb, - 0x9c, 0x39, 0x69, 0x7c, 0xce, 0x83, 0x7c, 0x8b, 0x13, 0xf9, 0x2d, 0x28, 0x25, 0x7e, 0x38, 0x0f, - 0xe0, 0xed, 0x5f, 0x19, 0x4c, 0x9d, 0x59, 0xe5, 0xd1, 0x1a, 0xa0, 0x99, 0xd2, 0x54, 0x21, 0x71, - 0xa8, 0xb3, 0x14, 0x16, 0x41, 0x99, 0x0a, 0xcb, 0x0e, 0xa2, 0x6c, 0x83, 0x3b, 0xc9, 0x89, 0x3a, - 0xca, 0xdc, 0xbd, 0x80, 0x52, 0x8e, 0xd7, 0x41, 0xcd, 0x45, 0x7c, 0x20, 0x2f, 0x89, 0xfd, 0x61, - 0x06, 0xc7, 0x6d, 0xa8, 0x52, 0x5f, 0x1b, 0x3a, 0xd3, 0x6c, 0x1a, 0x5f, 0x47, 0xaa, 0x74, 0x3d, - 0x52, 0xa5, 0x9b, 0x91, 0x2a, 0x7d, 0x1c, 0xab, 0xb9, 0xeb, 0xb1, 0x9a, 0xfb, 0x3e, 0x56, 0x73, - 0x6f, 0x4e, 0x09, 0x0d, 0x3a, 0xa1, 0x05, 0x6d, 0xe6, 0x22, 0x9b, 0x71, 0x97, 0x71, 0x44, 0x2d, - 0xfb, 0x84, 0x30, 0xd4, 0x7f, 0x8a, 0x5c, 0xe6, 0x84, 0x5d, 0xcc, 0xc5, 0x6d, 0xf5, 0xb8, 0x71, - 0x12, 0x5f, 0x58, 0xc1, 0xa0, 0x87, 0xb9, 0x55, 0x88, 0xe6, 0xea, 0xc9, 0xcf, 0x00, 0x00, 0x00, - 0xff, 0xff, 0x48, 0x1a, 0x96, 0xf0, 0xd0, 0x06, 0x00, 0x00, + // 623 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x55, 0xcd, 0x6e, 0xd3, 0x4c, + 0x14, 0x8d, 0x9b, 0xef, 0x8b, 0x9a, 0x69, 0xfa, 0x23, 0x13, 0x52, 0xd7, 0x55, 0xed, 0xc8, 0x74, + 0x11, 0x04, 0xb5, 0x49, 0xba, 0x00, 0x15, 0x36, 0xa4, 0x2b, 0x16, 0x91, 0xc0, 0x15, 0x0b, 0xd8, + 0x04, 0xff, 0x4c, 0xa7, 0x23, 0x62, 0x4f, 0xe4, 0xb1, 0x23, 0xf2, 0x06, 0x2c, 0x79, 0x84, 0x0a, + 0x5e, 0x80, 0xc7, 0x60, 0xd9, 0x05, 0x0b, 0x56, 0x51, 0x95, 0x6c, 0x58, 0xe7, 0x09, 0x50, 0x3c, + 0x4e, 0xb0, 0x1d, 0x3b, 0x8a, 0x04, 0xec, 0x3c, 0x73, 0xcf, 0x9c, 0x73, 0x8f, 0xcf, 0x1d, 0x1b, + 0x1c, 0x62, 0xd3, 0xd2, 0x2c, 0xe2, 0x41, 0xcd, 0xea, 0x61, 0xe8, 0xfa, 0xda, 0xa0, 0xa9, 0xf9, + 0x1f, 0xd4, 0xbe, 0x47, 0x7c, 0xc2, 0xf3, 0xd8, 0xb4, 0xd4, 0x59, 0x51, 0x65, 0x45, 0x75, 0xd0, + 0x14, 0xab, 0x88, 0x20, 0x12, 0x96, 0xb5, 0xd9, 0x13, 0x43, 0x8a, 0x07, 0x88, 0x10, 0xd4, 0x83, + 0x5a, 0xb8, 0x32, 0x83, 0x4b, 0xcd, 0x70, 0x87, 0xac, 0xa4, 0xdc, 0x72, 0x60, 0xb7, 0x43, 0xd1, + 0xb9, 0x07, 0x0d, 0x1f, 0x9e, 0x87, 0x3c, 0xfc, 0x4b, 0x50, 0x61, 0x8c, 0x5d, 0xea, 0x1b, 0x3e, + 0x14, 0xb8, 0x3a, 0xd7, 0xd8, 0x6a, 0x55, 0x55, 0xc6, 0xa2, 0xce, 0x59, 0xd4, 0xe7, 0xee, 0xb0, + 0xbd, 0x3f, 0x1d, 0xc9, 0x77, 0x86, 0x86, 0xd3, 0x3b, 0x53, 0xe2, 0x67, 0x14, 0x7d, 0x8b, 0x2d, + 0x2f, 0x66, 0x2b, 0xfe, 0x0d, 0xd8, 0xb5, 0x88, 0x4b, 0xa1, 0x4b, 0x03, 0x1a, 0x91, 0x6e, 0xac, + 0x20, 0x15, 0xa7, 0x23, 0xb9, 0x16, 0x91, 0x26, 0x8f, 0x29, 0xfa, 0xce, 0x62, 0x87, 0x51, 0xd7, + 0x40, 0x89, 0x62, 0xe4, 0x42, 0x4f, 0x28, 0xd6, 0xb9, 0x46, 0x59, 0x8f, 0x56, 0x67, 0x9b, 0x1f, + 0xaf, 0xe5, 0xc2, 0xcf, 0x6b, 0xb9, 0xa0, 0x1c, 0x80, 0xfd, 0x94, 0x43, 0x1d, 0xd2, 0xfe, 0x8c, + 0x45, 0xf9, 0xc2, 0xdc, 0xbf, 0xee, 0xdb, 0xbf, 0xdd, 0x37, 0x41, 0x39, 0x72, 0x82, 0xed, 0xd0, + 0x7a, 0xb9, 0x5d, 0x9d, 0x8e, 0xe4, 0xbd, 0x84, 0x49, 0x6c, 0x2b, 0xfa, 0x26, 0x7b, 0x7e, 0x61, + 0xf3, 0x4f, 0xc1, 0x4e, 0xb4, 0xef, 0x40, 0x4a, 0x0d, 0xb4, 0xd2, 0x9d, 0xbe, 0xcd, 0xb0, 0x1d, + 0x06, 0x5d, 0xdb, 0x40, 0xbc, 0xc9, 0x85, 0x81, 0xef, 0x45, 0xb0, 0x17, 0xd6, 0x90, 0x67, 0xd8, + 0x7f, 0xe0, 0x20, 0x1d, 0xf9, 0xc6, 0xbf, 0x88, 0xbc, 0xf8, 0x97, 0x22, 0x7f, 0x05, 0xaa, 0x7d, + 0x8f, 0x90, 0xcb, 0x6e, 0xc0, 0x6c, 0x77, 0x99, 0xae, 0xf0, 0x5f, 0x9d, 0x6b, 0x54, 0xda, 0xf2, + 0x74, 0x24, 0x1f, 0x32, 0xa6, 0x2c, 0x94, 0xa2, 0xf3, 0xe1, 0x76, 0xf2, 0x95, 0xbd, 0x07, 0x47, + 0x29, 0x70, 0xaa, 0xf7, 0xff, 0x43, 0xee, 0xc6, 0x74, 0x24, 0x1f, 0x67, 0x72, 0xa7, 0x7b, 0x16, + 0x13, 0x22, 0x79, 0x23, 0x5b, 0xca, 0x49, 0x5c, 0x04, 0x42, 0x3a, 0xd5, 0x45, 0xe4, 0x5f, 0x39, + 0x70, 0xb7, 0x43, 0xd1, 0x45, 0x60, 0x3a, 0xd8, 0xef, 0x60, 0x6a, 0xc2, 0x2b, 0x63, 0x80, 0x49, + 0xe0, 0xf1, 0xa7, 0xcb, 0xb9, 0xd7, 0xb2, 0x72, 0x17, 0xb8, 0x58, 0xf2, 0xcf, 0x40, 0xc5, 0x89, + 0x91, 0xac, 0x4c, 0x7e, 0x43, 0xe0, 0xf4, 0x04, 0x9a, 0x17, 0x93, 0xc3, 0x1b, 0x22, 0x96, 0xed, + 0xc8, 0xe0, 0x28, 0xb3, 0xe3, 0xb9, 0xa7, 0xd6, 0xe7, 0x22, 0x28, 0x76, 0x28, 0xe2, 0xdf, 0x81, + 0x4a, 0xe2, 0x4b, 0x74, 0x4f, 0x5d, 0xfe, 0xc6, 0xa9, 0xa9, 0xcb, 0x2c, 0x3e, 0x58, 0x03, 0x34, + 0x57, 0x9a, 0x29, 0x24, 0x6e, 0x7b, 0x9e, 0x42, 0x1c, 0x94, 0xab, 0x90, 0x75, 0x25, 0x79, 0x0b, + 0x6c, 0x27, 0x67, 0xeb, 0x38, 0xf7, 0x74, 0x0c, 0x25, 0x3e, 0x5c, 0x07, 0xb5, 0x10, 0xf1, 0x00, + 0x9f, 0x31, 0x00, 0xf7, 0x73, 0x38, 0x96, 0xa1, 0x62, 0x73, 0x6d, 0xe8, 0x5c, 0xb3, 0xad, 0x7f, + 0x1b, 0x4b, 0xdc, 0xcd, 0x58, 0xe2, 0x6e, 0xc7, 0x12, 0xf7, 0x69, 0x22, 0x15, 0x6e, 0x26, 0x52, + 0xe1, 0xc7, 0x44, 0x2a, 0xbc, 0x7d, 0x82, 0xb0, 0x7f, 0x15, 0x98, 0xaa, 0x45, 0x1c, 0xcd, 0x22, + 0xd4, 0x21, 0x54, 0xc3, 0xa6, 0x75, 0x82, 0x88, 0x36, 0x78, 0xac, 0x39, 0xc4, 0x0e, 0x7a, 0x90, + 0xb2, 0xdf, 0xd8, 0xa3, 0xd6, 0x49, 0xf4, 0x27, 0xf3, 0x87, 0x7d, 0x48, 0xcd, 0x52, 0x38, 0x5f, + 0xa7, 0xbf, 0x02, 0x00, 0x00, 0xff, 0xff, 0x0d, 0x8f, 0x23, 0x99, 0xe9, 0x06, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -716,9 +718,9 @@ func (m *MsgUpdateClient) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x1a } - if m.Header != nil { + if m.ClientMessage != nil { { - size, err := m.Header.MarshalToSizedBuffer(dAtA[:i]) + size, err := m.ClientMessage.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } @@ -982,8 +984,8 @@ func (m *MsgUpdateClient) Size() (n int) { if l > 0 { n += 1 + l + sovTx(uint64(l)) } - if m.Header != nil { - l = m.Header.Size() + if m.ClientMessage != nil { + l = m.ClientMessage.Size() n += 1 + l + sovTx(uint64(l)) } l = len(m.Signer) @@ -1347,7 +1349,7 @@ func (m *MsgUpdateClient) Unmarshal(dAtA []byte) error { iNdEx = postIndex case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field ClientMessage", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -1374,10 +1376,10 @@ func (m *MsgUpdateClient) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if m.Header == nil { - m.Header = &types.Any{} + if m.ClientMessage == nil { + m.ClientMessage = &types.Any{} } - if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.ClientMessage.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex diff --git a/modules/core/03-connection/client/cli/cli.go b/modules/core/03-connection/client/cli/cli.go index d4972654569..aba7df4d967 100644 --- a/modules/core/03-connection/client/cli/cli.go +++ b/modules/core/03-connection/client/cli/cli.go @@ -3,7 +3,7 @@ package cli import ( "github.com/spf13/cobra" - "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types" + "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" ) // GetQueryCmd returns the query commands for IBC connections @@ -19,6 +19,7 @@ func GetQueryCmd() *cobra.Command { GetCmdQueryConnections(), GetCmdQueryConnection(), GetCmdQueryClientConnections(), + GetCmdConnectionParams(), ) return queryCmd diff --git a/modules/core/03-connection/client/cli/query.go b/modules/core/03-connection/client/cli/query.go index 310e21bef82..e0d6d917d5b 100644 --- a/modules/core/03-connection/client/cli/query.go +++ b/modules/core/03-connection/client/cli/query.go @@ -8,9 +8,9 @@ import ( "github.com/cosmos/cosmos-sdk/version" "github.com/spf13/cobra" - "github.com/cosmos/ibc-go/v4/modules/core/03-connection/client/utils" - "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" + "github.com/cosmos/ibc-go/v7/modules/core/03-connection/client/utils" + "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" + ibcexported "github.com/cosmos/ibc-go/v7/modules/core/exported" ) // GetCmdQueryConnections defines the command to query all the connection ends @@ -20,7 +20,7 @@ func GetCmdQueryConnections() *cobra.Command { Use: "connections", Short: "Query all connections", Long: "Query all connections ends from a chain", - Example: fmt.Sprintf("%s query %s %s connections", version.AppName, host.ModuleName, types.SubModuleName), + Example: fmt.Sprintf("%s query %s %s connections", version.AppName, ibcexported.ModuleName, types.SubModuleName), Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, _ []string) error { clientCtx, err := client.GetClientQueryContext(cmd) @@ -59,7 +59,7 @@ func GetCmdQueryConnection() *cobra.Command { Use: "end [connection-id]", Short: "Query stored connection end", Long: "Query stored connection end", - Example: fmt.Sprintf("%s query %s %s end [connection-id]", version.AppName, host.ModuleName, types.SubModuleName), + Example: fmt.Sprintf("%s query %s %s end [connection-id]", version.AppName, ibcexported.ModuleName, types.SubModuleName), Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { clientCtx, err := client.GetClientQueryContext(cmd) @@ -91,7 +91,7 @@ func GetCmdQueryClientConnections() *cobra.Command { Use: "path [client-id]", Short: "Query stored client connection paths", Long: "Query stored client connection paths", - Example: fmt.Sprintf("%s query %s %s path [client-id]", version.AppName, host.ModuleName, types.SubModuleName), + Example: fmt.Sprintf("%s query %s %s path [client-id]", version.AppName, ibcexported.ModuleName, types.SubModuleName), Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { clientCtx, err := client.GetClientQueryContext(cmd) @@ -116,3 +116,28 @@ func GetCmdQueryClientConnections() *cobra.Command { return cmd } + +// GetCmdConnectionParams returns the command handler for ibc connection parameter querying. +func GetCmdConnectionParams() *cobra.Command { + cmd := &cobra.Command{ + Use: "params", + Short: "Query the current ibc connection parameters", + Long: "Query the current ibc connection parameters", + Args: cobra.NoArgs, + Example: fmt.Sprintf("%s query %s %s params", version.AppName, ibcexported.ModuleName, types.SubModuleName), + RunE: func(cmd *cobra.Command, _ []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + queryClient := types.NewQueryClient(clientCtx) + + res, _ := queryClient.ConnectionParams(cmd.Context(), &types.QueryConnectionParamsRequest{}) + return clientCtx.PrintProto(res.Params) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} diff --git a/modules/core/03-connection/client/utils/utils.go b/modules/core/03-connection/client/utils/utils.go index bb1b8f7f1c5..a7cab07d6bf 100644 --- a/modules/core/03-connection/client/utils/utils.go +++ b/modules/core/03-connection/client/utils/utils.go @@ -4,19 +4,19 @@ import ( "context" "errors" "fmt" - "io/ioutil" + "os" + errorsmod "cosmossdk.io/errors" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/codec" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - - clientutils "github.com/cosmos/ibc-go/v4/modules/core/02-client/client/utils" - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types" - commitmenttypes "github.com/cosmos/ibc-go/v4/modules/core/23-commitment/types" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" - ibcclient "github.com/cosmos/ibc-go/v4/modules/core/client" - "github.com/cosmos/ibc-go/v4/modules/core/exported" + + clientutils "github.com/cosmos/ibc-go/v7/modules/core/02-client/client/utils" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" + commitmenttypes "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + ibcclient "github.com/cosmos/ibc-go/v7/modules/core/client" + "github.com/cosmos/ibc-go/v7/modules/core/exported" ) // QueryConnection returns a connection end. @@ -47,7 +47,7 @@ func queryConnectionABCI(clientCtx client.Context, connectionID string) (*types. // check if connection exists if len(value) == 0 { - return nil, sdkerrors.Wrap(types.ErrConnectionNotFound, connectionID) + return nil, errorsmod.Wrap(types.ErrConnectionNotFound, connectionID) } cdc := codec.NewProtoCodec(clientCtx.InterfaceRegistry) @@ -88,7 +88,7 @@ func queryClientConnectionsABCI(clientCtx client.Context, clientID string) (*typ // check if connection paths exist if len(value) == 0 { - return nil, sdkerrors.Wrap(types.ErrClientConnectionPathsNotFound, clientID) + return nil, errorsmod.Wrap(types.ErrClientConnectionPathsNotFound, clientID) } var paths []string @@ -169,7 +169,7 @@ func ParseClientState(cdc *codec.LegacyAmino, arg string) (exported.ClientState, var clientState exported.ClientState if err := cdc.UnmarshalJSON([]byte(arg), &clientState); err != nil { // check for file path if JSON input is not provided - contents, err := ioutil.ReadFile(arg) + contents, err := os.ReadFile(arg) if err != nil { return nil, errors.New("either JSON input nor path to .json file were provided") } @@ -186,7 +186,7 @@ func ParsePrefix(cdc *codec.LegacyAmino, arg string) (commitmenttypes.MerklePref var prefix commitmenttypes.MerklePrefix if err := cdc.UnmarshalJSON([]byte(arg), &prefix); err != nil { // check for file path if JSON input is not provided - contents, err := ioutil.ReadFile(arg) + contents, err := os.ReadFile(arg) if err != nil { return commitmenttypes.MerklePrefix{}, errors.New("neither JSON input nor path to .json file were provided") } @@ -204,7 +204,7 @@ func ParseProof(cdc *codec.LegacyAmino, arg string) ([]byte, error) { var merkleProof commitmenttypes.MerkleProof if err := cdc.UnmarshalJSON([]byte(arg), &merkleProof); err != nil { // check for file path if JSON input is not provided - contents, err := ioutil.ReadFile(arg) + contents, err := os.ReadFile(arg) if err != nil { return nil, errors.New("neither JSON input nor path to .json file were provided") } diff --git a/modules/core/03-connection/doc.go b/modules/core/03-connection/doc.go new file mode 100644 index 00000000000..9f89e3bbf67 --- /dev/null +++ b/modules/core/03-connection/doc.go @@ -0,0 +1,12 @@ +/* +Package connection implements the ICS 03 - Connection Semantics specification +(https://github.com/cosmos/ibc/tree/main/spec/core/ics-003-connection-semantics). This +concrete implementation defines types and methods for safely creating two +stateful objects (connection ends) on two separate chains, each associated with a +light client of the other chain, which together facilitate cross-chain +sub-state verification and packet association (through channels). + +The main type is ConnectionEnd, which defines a stateful object on a +chain connected to another. +*/ +package connection diff --git a/modules/core/03-connection/genesis.go b/modules/core/03-connection/genesis.go index b0070d79197..44a8585bcdb 100644 --- a/modules/core/03-connection/genesis.go +++ b/modules/core/03-connection/genesis.go @@ -3,8 +3,8 @@ package connection import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/ibc-go/v4/modules/core/03-connection/keeper" - "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types" + "github.com/cosmos/ibc-go/v7/modules/core/03-connection/keeper" + "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" ) // InitGenesis initializes the ibc connection submodule's state from a provided genesis diff --git a/modules/core/03-connection/keeper/events.go b/modules/core/03-connection/keeper/events.go index ac6cae539e3..e9e5a575990 100644 --- a/modules/core/03-connection/keeper/events.go +++ b/modules/core/03-connection/keeper/events.go @@ -3,11 +3,11 @@ package keeper import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types" + "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" ) -// EmitConnectionOpenInitEvent emits a connection open init event -func EmitConnectionOpenInitEvent(ctx sdk.Context, connectionID string, clientID string, counterparty types.Counterparty) { +// emitConnectionOpenInitEvent emits a connection open init event +func emitConnectionOpenInitEvent(ctx sdk.Context, connectionID string, clientID string, counterparty types.Counterparty) { ctx.EventManager().EmitEvents(sdk.Events{ sdk.NewEvent( types.EventTypeConnectionOpenInit, @@ -23,8 +23,8 @@ func EmitConnectionOpenInitEvent(ctx sdk.Context, connectionID string, clientID }) } -// EmitConnectionOpenTryEvent emits a connection open try event -func EmitConnectionOpenTryEvent(ctx sdk.Context, connectionID string, clientID string, counterparty types.Counterparty) { +// emitConnectionOpenTryEvent emits a connection open try event +func emitConnectionOpenTryEvent(ctx sdk.Context, connectionID string, clientID string, counterparty types.Counterparty) { ctx.EventManager().EmitEvents(sdk.Events{ sdk.NewEvent( types.EventTypeConnectionOpenTry, @@ -40,8 +40,8 @@ func EmitConnectionOpenTryEvent(ctx sdk.Context, connectionID string, clientID s }) } -// EmitConnectionOpenAckEvent emits a connection open acknowledge event -func EmitConnectionOpenAckEvent(ctx sdk.Context, connectionID string, connectionEnd types.ConnectionEnd) { +// emitConnectionOpenAckEvent emits a connection open acknowledge event +func emitConnectionOpenAckEvent(ctx sdk.Context, connectionID string, connectionEnd types.ConnectionEnd) { ctx.EventManager().EmitEvents(sdk.Events{ sdk.NewEvent( types.EventTypeConnectionOpenAck, @@ -57,8 +57,8 @@ func EmitConnectionOpenAckEvent(ctx sdk.Context, connectionID string, connection }) } -// EmitConnectionOpenConfirmEvent emits a connection open confirm event -func EmitConnectionOpenConfirmEvent(ctx sdk.Context, connectionID string, connectionEnd types.ConnectionEnd) { +// emitConnectionOpenConfirmEvent emits a connection open confirm event +func emitConnectionOpenConfirmEvent(ctx sdk.Context, connectionID string, connectionEnd types.ConnectionEnd) { ctx.EventManager().EmitEvents(sdk.Events{ sdk.NewEvent( types.EventTypeConnectionOpenConfirm, diff --git a/modules/core/03-connection/keeper/grpc_query.go b/modules/core/03-connection/keeper/grpc_query.go index d07616163f6..c2a0afb2bf5 100644 --- a/modules/core/03-connection/keeper/grpc_query.go +++ b/modules/core/03-connection/keeper/grpc_query.go @@ -3,16 +3,16 @@ package keeper import ( "context" + errorsmod "cosmossdk.io/errors" "github.com/cosmos/cosmos-sdk/store/prefix" sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/types/query" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" ) var _ types.QueryServer = Keeper{} @@ -32,7 +32,7 @@ func (q Keeper) Connection(c context.Context, req *types.QueryConnectionRequest) if !found { return nil, status.Error( codes.NotFound, - sdkerrors.Wrap(types.ErrConnectionNotFound, req.ConnectionId).Error(), + errorsmod.Wrap(types.ErrConnectionNotFound, req.ConnectionId).Error(), ) } @@ -94,7 +94,7 @@ func (q Keeper) ClientConnections(c context.Context, req *types.QueryClientConne if !found { return nil, status.Error( codes.NotFound, - sdkerrors.Wrap(types.ErrClientConnectionPathsNotFound, req.ClientId).Error(), + errorsmod.Wrap(types.ErrClientConnectionPathsNotFound, req.ClientId).Error(), ) } @@ -120,7 +120,7 @@ func (q Keeper) ConnectionClientState(c context.Context, req *types.QueryConnect if !found { return nil, status.Error( codes.NotFound, - sdkerrors.Wrapf(types.ErrConnectionNotFound, "connection-id: %s", req.ConnectionId).Error(), + errorsmod.Wrapf(types.ErrConnectionNotFound, "connection-id: %s", req.ConnectionId).Error(), ) } @@ -128,7 +128,7 @@ func (q Keeper) ConnectionClientState(c context.Context, req *types.QueryConnect if !found { return nil, status.Error( codes.NotFound, - sdkerrors.Wrapf(clienttypes.ErrClientNotFound, "client-id: %s", connection.ClientId).Error(), + errorsmod.Wrapf(clienttypes.ErrClientNotFound, "client-id: %s", connection.ClientId).Error(), ) } @@ -154,7 +154,7 @@ func (q Keeper) ConnectionConsensusState(c context.Context, req *types.QueryConn if !found { return nil, status.Error( codes.NotFound, - sdkerrors.Wrapf(types.ErrConnectionNotFound, "connection-id: %s", req.ConnectionId).Error(), + errorsmod.Wrapf(types.ErrConnectionNotFound, "connection-id: %s", req.ConnectionId).Error(), ) } @@ -163,7 +163,7 @@ func (q Keeper) ConnectionConsensusState(c context.Context, req *types.QueryConn if !found { return nil, status.Error( codes.NotFound, - sdkerrors.Wrapf(clienttypes.ErrConsensusStateNotFound, "client-id: %s", connection.ClientId).Error(), + errorsmod.Wrapf(clienttypes.ErrConsensusStateNotFound, "client-id: %s", connection.ClientId).Error(), ) } @@ -175,3 +175,13 @@ func (q Keeper) ConnectionConsensusState(c context.Context, req *types.QueryConn proofHeight := clienttypes.GetSelfHeight(ctx) return types.NewQueryConnectionConsensusStateResponse(connection.ClientId, anyConsensusState, height, nil, proofHeight), nil } + +// ConnectionParams implements the Query/ConnectionParams gRPC method. +func (q Keeper) ConnectionParams(c context.Context, req *types.QueryConnectionParamsRequest) (*types.QueryConnectionParamsResponse, error) { + ctx := sdk.UnwrapSDKContext(c) + params := q.GetParams(ctx) + + return &types.QueryConnectionParamsResponse{ + Params: ¶ms, + }, nil +} diff --git a/modules/core/03-connection/keeper/grpc_query_test.go b/modules/core/03-connection/keeper/grpc_query_test.go index b1ffb50b910..dd9531fb43e 100644 --- a/modules/core/03-connection/keeper/grpc_query_test.go +++ b/modules/core/03-connection/keeper/grpc_query_test.go @@ -6,10 +6,10 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/query" - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types" - "github.com/cosmos/ibc-go/v4/modules/core/exported" - ibctesting "github.com/cosmos/ibc-go/v4/testing" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" + "github.com/cosmos/ibc-go/v7/modules/core/exported" + ibctesting "github.com/cosmos/ibc-go/v7/testing" ) func (suite *KeeperTestSuite) TestQueryConnection() { @@ -440,3 +440,10 @@ func (suite *KeeperTestSuite) TestQueryConnectionConsensusState() { }) } } + +func (suite *KeeperTestSuite) TestQueryConnectionParams() { + ctx := sdk.WrapSDKContext(suite.chainA.GetContext()) + expParams := types.DefaultParams() + res, _ := suite.chainA.QueryServer.ConnectionParams(ctx, &types.QueryConnectionParamsRequest{}) + suite.Require().Equal(&expParams, res.Params) +} diff --git a/modules/core/03-connection/keeper/handshake.go b/modules/core/03-connection/keeper/handshake.go index faca5c0f74c..602547c7781 100644 --- a/modules/core/03-connection/keeper/handshake.go +++ b/modules/core/03-connection/keeper/handshake.go @@ -1,14 +1,15 @@ package keeper import ( + errorsmod "cosmossdk.io/errors" "github.com/cosmos/cosmos-sdk/telemetry" sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types" - commitmenttypes "github.com/cosmos/ibc-go/v4/modules/core/23-commitment/types" - "github.com/cosmos/ibc-go/v4/modules/core/exported" + ibcerrors "github.com/cosmos/ibc-go/v7/internal/errors" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" + commitmenttypes "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/types" + "github.com/cosmos/ibc-go/v7/modules/core/exported" ) // ConnOpenInit initialises a connection attempt on chain A. The generated connection identifier @@ -26,28 +27,28 @@ func (k Keeper) ConnOpenInit( versions := types.GetCompatibleVersions() if version != nil { if !types.IsSupportedVersion(types.GetCompatibleVersions(), version) { - return "", sdkerrors.Wrap(types.ErrInvalidVersion, "version is not supported") + return "", errorsmod.Wrap(types.ErrInvalidVersion, "version is not supported") } versions = []exported.Version{version} } - // connection defines chain A's ConnectionEnd connectionID := k.GenerateConnectionIdentifier(ctx) - connection := types.NewConnectionEnd(types.INIT, clientID, counterparty, types.ExportedVersionsToProto(versions), delayPeriod) - k.SetConnection(ctx, connectionID, connection) - if err := k.addConnectionToClient(ctx, clientID, connectionID); err != nil { return "", err } + // connection defines chain A's ConnectionEnd + connection := types.NewConnectionEnd(types.INIT, clientID, counterparty, types.ExportedVersionsToProto(versions), delayPeriod) + k.SetConnection(ctx, connectionID, connection) + k.Logger(ctx).Info("connection state updated", "connection-id", connectionID, "previous-state", "NONE", "new-state", "INIT") defer func() { telemetry.IncrCounter(1, "ibc", "connection", "open-init") }() - EmitConnectionOpenInitEvent(ctx, connectionID, clientID, counterparty) + emitConnectionOpenInitEvent(ctx, connectionID, clientID, counterparty) return connectionID, nil } @@ -56,8 +57,8 @@ func (k Keeper) ConnOpenInit( // code is executed on chain B). // // NOTE: -// - Here chain A acts as the counterparty -// - Identifiers are checked on msg validation +// - Here chain A acts as the counterparty +// - Identifiers are checked on msg validation func (k Keeper) ConnOpenTry( ctx sdk.Context, counterparty types.Counterparty, // counterpartyConnectionIdentifier, counterpartyPrefix and counterpartyClientIdentifier @@ -71,14 +72,13 @@ 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) { - // generate a new connection connectionID := k.GenerateConnectionIdentifier(ctx) selfHeight := clienttypes.GetSelfHeight(ctx) if consensusHeight.GTE(selfHeight) { - return "", sdkerrors.Wrapf( - sdkerrors.ErrInvalidHeight, + return "", errorsmod.Wrapf( + ibcerrors.ErrInvalidHeight, "consensus height is greater than or equal to the current block height (%s >= %s)", consensusHeight, selfHeight, ) } @@ -90,7 +90,7 @@ func (k Keeper) ConnOpenTry( expectedConsensusState, err := k.clientKeeper.GetSelfConsensusState(ctx, consensusHeight) if err != nil { - return "", sdkerrors.Wrapf(err, "self consensus state not found for height %s", consensusHeight.String()) + return "", errorsmod.Wrapf(err, "self consensus state not found for height %s", consensusHeight.String()) } // expectedConnection defines Chain A's ConnectionEnd @@ -133,7 +133,7 @@ func (k Keeper) ConnOpenTry( // store connection in chainB state if err := k.addConnectionToClient(ctx, clientID, connectionID); err != nil { - return "", sdkerrors.Wrapf(err, "failed to add connection with ID %s to client with ID %s", connectionID, clientID) + return "", errorsmod.Wrapf(err, "failed to add connection with ID %s to client with ID %s", connectionID, clientID) } k.SetConnection(ctx, connectionID, connection) @@ -143,7 +143,7 @@ func (k Keeper) ConnOpenTry( telemetry.IncrCounter(1, "ibc", "connection", "open-try") }() - EmitConnectionOpenTryEvent(ctx, connectionID, clientID, counterparty) + emitConnectionOpenTryEvent(ctx, connectionID, clientID, counterparty) return connectionID, nil } @@ -167,8 +167,8 @@ func (k Keeper) ConnOpenAck( // Check that chainB client hasn't stored invalid height selfHeight := clienttypes.GetSelfHeight(ctx) if consensusHeight.GTE(selfHeight) { - return sdkerrors.Wrapf( - sdkerrors.ErrInvalidHeight, + return errorsmod.Wrapf( + ibcerrors.ErrInvalidHeight, "consensus height is greater than or equal to the current block height (%s >= %s)", consensusHeight, selfHeight, ) } @@ -176,12 +176,12 @@ func (k Keeper) ConnOpenAck( // Retrieve connection connection, found := k.GetConnection(ctx, connectionID) if !found { - return sdkerrors.Wrap(types.ErrConnectionNotFound, connectionID) + return errorsmod.Wrap(types.ErrConnectionNotFound, connectionID) } // verify the previously set connection state if connection.State != types.INIT { - return sdkerrors.Wrapf( + return errorsmod.Wrapf( types.ErrInvalidConnectionState, "connection state is not INIT (got %s)", connection.State.String(), ) @@ -189,7 +189,7 @@ func (k Keeper) ConnOpenAck( // ensure selected version is supported if !types.IsSupportedVersion(types.ProtoVersionsToExported(connection.Versions), version) { - return sdkerrors.Wrapf( + return errorsmod.Wrapf( types.ErrInvalidConnectionState, "the counterparty selected version %s is not supported by versions selected on INIT", version, ) @@ -203,7 +203,7 @@ func (k Keeper) ConnOpenAck( // Retrieve chainA's consensus state at consensusheight expectedConsensusState, err := k.clientKeeper.GetSelfConsensusState(ctx, consensusHeight) if err != nil { - return sdkerrors.Wrapf(err, "self consensus state not found for height %s", consensusHeight.String()) + return errorsmod.Wrapf(err, "self consensus state not found for height %s", consensusHeight.String()) } prefix := k.GetCommitmentPrefix() @@ -242,7 +242,7 @@ func (k Keeper) ConnOpenAck( connection.Counterparty.ConnectionId = counterpartyConnectionID k.SetConnection(ctx, connectionID, connection) - EmitConnectionOpenAckEvent(ctx, connectionID, connection) + emitConnectionOpenAckEvent(ctx, connectionID, connection) return nil } @@ -260,12 +260,12 @@ func (k Keeper) ConnOpenConfirm( // Retrieve connection connection, found := k.GetConnection(ctx, connectionID) if !found { - return sdkerrors.Wrap(types.ErrConnectionNotFound, connectionID) + return errorsmod.Wrap(types.ErrConnectionNotFound, connectionID) } // Check that connection state on ChainB is on state: TRYOPEN if connection.State != types.TRYOPEN { - return sdkerrors.Wrapf( + return errorsmod.Wrapf( types.ErrInvalidConnectionState, "connection state is not TRYOPEN (got %s)", connection.State.String(), ) @@ -292,7 +292,7 @@ func (k Keeper) ConnOpenConfirm( telemetry.IncrCounter(1, "ibc", "connection", "open-confirm") }() - EmitConnectionOpenConfirmEvent(ctx, connectionID, connection) + emitConnectionOpenConfirmEvent(ctx, connectionID, connection) return nil } diff --git a/modules/core/03-connection/keeper/handshake_test.go b/modules/core/03-connection/keeper/handshake_test.go index aeabf5718c2..c14ec879204 100644 --- a/modules/core/03-connection/keeper/handshake_test.go +++ b/modules/core/03-connection/keeper/handshake_test.go @@ -3,12 +3,12 @@ package keeper_test import ( "time" - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" - "github.com/cosmos/ibc-go/v4/modules/core/exported" - ibctmtypes "github.com/cosmos/ibc-go/v4/modules/light-clients/07-tendermint/types" - ibctesting "github.com/cosmos/ibc-go/v4/testing" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + "github.com/cosmos/ibc-go/v7/modules/core/exported" + ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" + ibctesting "github.com/cosmos/ibc-go/v7/testing" ) // TestConnOpenInit - chainA initializes (INIT state) a connection with @@ -112,7 +112,8 @@ func (suite *KeeperTestSuite) TestConnOpenTry() { // commit in order for proof to return correct value suite.coordinator.CommitBlock(suite.chainA) - path.EndpointB.UpdateClient() + err = path.EndpointB.UpdateClient() + suite.Require().NoError(err) // retrieve client state of chainA to pass as counterpartyClient counterpartyClient = suite.chainA.GetClientState(path.EndpointA.ClientID) @@ -125,7 +126,7 @@ func (suite *KeeperTestSuite) TestConnOpenTry() { counterpartyClient = suite.chainA.GetClientState(path.EndpointA.ClientID) // Set an invalid client of chainA on chainB - tmClient, ok := counterpartyClient.(*ibctmtypes.ClientState) + tmClient, ok := counterpartyClient.(*ibctm.ClientState) suite.Require().True(ok) tmClient.ChainId = "wrongchainid" @@ -182,7 +183,7 @@ func (suite *KeeperTestSuite) TestConnOpenTry() { counterpartyClient = suite.chainA.GetClientState(path.EndpointA.ClientID) // modify counterparty client without setting in store so it still passes validate but fails proof verification - tmClient, ok := counterpartyClient.(*ibctmtypes.ClientState) + tmClient, ok := counterpartyClient.(*ibctm.ClientState) suite.Require().True(ok) tmClient.LatestHeight = tmClient.LatestHeight.Increment().(clienttypes.Height) }, false}, @@ -194,7 +195,7 @@ func (suite *KeeperTestSuite) TestConnOpenTry() { consState, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.GetLatestClientConsensusState(suite.chainA.GetContext(), path.EndpointA.ClientID) suite.Require().True(found) - tmConsState, ok := consState.(*ibctmtypes.ConsensusState) + tmConsState, ok := consState.(*ibctm.ConsensusState) suite.Require().True(ok) tmConsState.Timestamp = time.Now() @@ -291,7 +292,7 @@ func (suite *KeeperTestSuite) TestConnOpenAck() { counterpartyClient = suite.chainB.GetClientState(path.EndpointB.ClientID) // Set an invalid client of chainA on chainB - tmClient, ok := counterpartyClient.(*ibctmtypes.ClientState) + tmClient, ok := counterpartyClient.(*ibctm.ClientState) suite.Require().True(ok) tmClient.ChainId = "wrongchainid" @@ -431,7 +432,7 @@ func (suite *KeeperTestSuite) TestConnOpenAck() { counterpartyClient = suite.chainB.GetClientState(path.EndpointB.ClientID) // modify counterparty client without setting in store so it still passes validate but fails proof verification - tmClient, ok := counterpartyClient.(*ibctmtypes.ClientState) + tmClient, ok := counterpartyClient.(*ibctm.ClientState) suite.Require().True(ok) tmClient.LatestHeight = tmClient.LatestHeight.Increment().(clienttypes.Height) @@ -449,7 +450,7 @@ func (suite *KeeperTestSuite) TestConnOpenAck() { consState, found := suite.chainB.App.GetIBCKeeper().ClientKeeper.GetLatestClientConsensusState(suite.chainB.GetContext(), path.EndpointB.ClientID) suite.Require().True(found) - tmConsState, ok := consState.(*ibctmtypes.ConsensusState) + tmConsState, ok := consState.(*ibctm.ConsensusState) suite.Require().True(ok) tmConsState.Timestamp = tmConsState.Timestamp.Add(time.Second) diff --git a/modules/core/03-connection/keeper/keeper.go b/modules/core/03-connection/keeper/keeper.go index e6370da8d26..c38fa2e07df 100644 --- a/modules/core/03-connection/keeper/keeper.go +++ b/modules/core/03-connection/keeper/keeper.go @@ -1,18 +1,18 @@ package keeper import ( - "github.com/tendermint/tendermint/libs/log" - + errorsmod "cosmossdk.io/errors" "github.com/cosmos/cosmos-sdk/codec" + storetypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" + "github.com/tendermint/tendermint/libs/log" - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types" - commitmenttypes "github.com/cosmos/ibc-go/v4/modules/core/23-commitment/types" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" - "github.com/cosmos/ibc-go/v4/modules/core/exported" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" + commitmenttypes "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + "github.com/cosmos/ibc-go/v7/modules/core/exported" ) // Keeper defines the IBC connection keeper @@ -20,14 +20,14 @@ type Keeper struct { // implements gRPC QueryServer interface types.QueryServer - storeKey sdk.StoreKey + storeKey storetypes.StoreKey paramSpace paramtypes.Subspace cdc codec.BinaryCodec clientKeeper types.ClientKeeper } // NewKeeper creates a new IBC connection Keeper instance -func NewKeeper(cdc codec.BinaryCodec, key sdk.StoreKey, paramSpace paramtypes.Subspace, ck types.ClientKeeper) Keeper { +func NewKeeper(cdc codec.BinaryCodec, key storetypes.StoreKey, paramSpace paramtypes.Subspace, ck types.ClientKeeper) Keeper { // set KeyTable if it has not already been set if !paramSpace.HasKeyTable() { paramSpace = paramSpace.WithKeyTable(types.ParamKeyTable()) @@ -43,7 +43,7 @@ func NewKeeper(cdc codec.BinaryCodec, key sdk.StoreKey, paramSpace paramtypes.Su // Logger returns a module-specific logger. func (k Keeper) Logger(ctx sdk.Context) log.Logger { - return ctx.Logger().With("module", "x/"+host.ModuleName+"/"+types.SubModuleName) + return ctx.Logger().With("module", "x/"+exported.ModuleName+"/"+types.SubModuleName) } // GetCommitmentPrefix returns the IBC connection store prefix as a commitment @@ -66,7 +66,7 @@ func (k Keeper) GenerateConnectionIdentifier(ctx sdk.Context) string { func (k Keeper) GetConnection(ctx sdk.Context, connectionID string) (types.ConnectionEnd, bool) { store := ctx.KVStore(k.storeKey) bz := store.Get(host.ConnectionKey(connectionID)) - if bz == nil { + if len(bz) == 0 { return types.ConnectionEnd{}, false } @@ -76,6 +76,13 @@ func (k Keeper) GetConnection(ctx sdk.Context, connectionID string) (types.Conne return connection, true } +// HasConnection returns a true if the connection with the given identifier +// exists in the store. +func (k Keeper) HasConnection(ctx sdk.Context, connectionID string) bool { + store := ctx.KVStore(k.storeKey) + return store.Has(host.ConnectionKey(connectionID)) +} + // SetConnection sets a connection to the store func (k Keeper) SetConnection(ctx sdk.Context, connectionID string, connection types.ConnectionEnd) { store := ctx.KVStore(k.storeKey) @@ -86,18 +93,19 @@ func (k Keeper) SetConnection(ctx sdk.Context, connectionID string, connection t // GetTimestampAtHeight returns the timestamp in nanoseconds of the consensus state at the // given height. func (k Keeper) GetTimestampAtHeight(ctx sdk.Context, connection types.ConnectionEnd, height exported.Height) (uint64, error) { - consensusState, found := k.clientKeeper.GetClientConsensusState( - ctx, connection.GetClientID(), height, - ) - + clientState, found := k.clientKeeper.GetClientState(ctx, connection.GetClientID()) if !found { - return 0, sdkerrors.Wrapf( - clienttypes.ErrConsensusStateNotFound, - "clientID (%s), height (%s)", connection.GetClientID(), height, + return 0, errorsmod.Wrapf( + clienttypes.ErrClientNotFound, "clientID (%s)", connection.GetClientID(), ) } - return consensusState.GetTimestamp(), nil + timestamp, err := clientState.GetTimestampAtHeight(ctx, k.clientKeeper.ClientStore(ctx, connection.GetClientID()), k.cdc, height) + if err != nil { + return 0, err + } + + return timestamp, nil } // GetClientConnectionPaths returns all the connection paths stored under a @@ -105,7 +113,7 @@ func (k Keeper) GetTimestampAtHeight(ctx sdk.Context, connection types.Connectio func (k Keeper) GetClientConnectionPaths(ctx sdk.Context, clientID string) ([]string, bool) { store := ctx.KVStore(k.storeKey) bz := store.Get(host.ClientConnectionsKey(clientID)) - if bz == nil { + if len(bz) == 0 { return nil, false } @@ -126,7 +134,7 @@ func (k Keeper) SetClientConnectionPaths(ctx sdk.Context, clientID string, paths func (k Keeper) GetNextConnectionSequence(ctx sdk.Context) uint64 { store := ctx.KVStore(k.storeKey) bz := store.Get([]byte(types.KeyNextConnectionSequence)) - if bz == nil { + if len(bz) == 0 { panic("next connection sequence is nil") } @@ -145,7 +153,7 @@ func (k Keeper) SetNextConnectionSequence(ctx sdk.Context, sequence uint64) { // no paths are stored. func (k Keeper) GetAllClientConnectionPaths(ctx sdk.Context) []types.ConnectionPaths { var allConnectionPaths []types.ConnectionPaths - k.clientKeeper.IterateClients(ctx, func(clientID string, cs exported.ClientState) bool { + k.clientKeeper.IterateClientStates(ctx, nil, func(clientID string, cs exported.ClientState) bool { paths, found := k.GetClientConnectionPaths(ctx, clientID) if !found { // continue when connection handshake is not initialized @@ -166,7 +174,7 @@ func (k Keeper) IterateConnections(ctx sdk.Context, cb func(types.IdentifiedConn store := ctx.KVStore(k.storeKey) iterator := sdk.KVStorePrefixIterator(store, []byte(host.KeyConnectionPrefix)) - defer iterator.Close() + defer sdk.LogDeferred(ctx.Logger(), func() error { return iterator.Close() }) for ; iterator.Valid(); iterator.Next() { var connection types.ConnectionEnd k.cdc.MustUnmarshal(iterator.Value(), &connection) @@ -193,7 +201,7 @@ func (k Keeper) GetAllConnections(ctx sdk.Context) (connections []types.Identifi func (k Keeper) addConnectionToClient(ctx sdk.Context, clientID, connectionID string) error { _, found := k.clientKeeper.GetClientState(ctx, clientID) if !found { - return sdkerrors.Wrap(clienttypes.ErrClientNotFound, clientID) + return errorsmod.Wrap(clienttypes.ErrClientNotFound, clientID) } conns, found := k.GetClientConnectionPaths(ctx, clientID) diff --git a/modules/core/03-connection/keeper/keeper_test.go b/modules/core/03-connection/keeper/keeper_test.go index dd72a9cc620..a82573788d3 100644 --- a/modules/core/03-connection/keeper/keeper_test.go +++ b/modules/core/03-connection/keeper/keeper_test.go @@ -6,8 +6,9 @@ import ( "github.com/stretchr/testify/suite" - "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types" - ibctesting "github.com/cosmos/ibc-go/v4/testing" + "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" + "github.com/cosmos/ibc-go/v7/modules/core/exported" + ibctesting "github.com/cosmos/ibc-go/v7/testing" ) type KeeperTestSuite struct { @@ -59,7 +60,7 @@ func (suite *KeeperTestSuite) TestSetAndGetClientConnectionPaths() { } // create 2 connections: A0 - B0, A1 - B1 -func (suite KeeperTestSuite) TestGetAllConnections() { +func (suite KeeperTestSuite) TestGetAllConnections() { //nolint:govet // this is a test, we are okay with copying locks path1 := ibctesting.NewPath(suite.chainA, suite.chainB) suite.coordinator.SetupConnections(path1) @@ -87,7 +88,7 @@ func (suite KeeperTestSuite) TestGetAllConnections() { // the test creates 2 clients path.EndpointA.ClientID0 and path.EndpointA.ClientID1. path.EndpointA.ClientID0 has a single // connection and path.EndpointA.ClientID1 has 2 connections. -func (suite KeeperTestSuite) TestGetAllClientConnectionPaths() { +func (suite KeeperTestSuite) TestGetAllClientConnectionPaths() { //nolint:govet // this is a test, we are okay with copying locks path1 := ibctesting.NewPath(suite.chainA, suite.chainB) path2 := ibctesting.NewPath(suite.chainA, suite.chainB) suite.coordinator.SetupConnections(path1) @@ -111,7 +112,10 @@ func (suite KeeperTestSuite) TestGetAllClientConnectionPaths() { // TestGetTimestampAtHeight verifies if the clients on each chain return the // correct timestamp for the other chain. func (suite *KeeperTestSuite) TestGetTimestampAtHeight() { - var connection types.ConnectionEnd + var ( + connection types.ConnectionEnd + height exported.Height + ) cases := []struct { msg string @@ -122,10 +126,14 @@ func (suite *KeeperTestSuite) TestGetTimestampAtHeight() { path := ibctesting.NewPath(suite.chainA, suite.chainB) suite.coordinator.SetupConnections(path) connection = path.EndpointA.GetConnection() + height = suite.chainB.LastHeader.GetHeight() }, true}, + {"client state not found", func() {}, false}, {"consensus state not found", func() { - // any non-nil value of connection is valid - suite.Require().NotNil(connection) + path := ibctesting.NewPath(suite.chainA, suite.chainB) + suite.coordinator.SetupConnections(path) + connection = path.EndpointA.GetConnection() + height = suite.chainB.LastHeader.GetHeight().Increment() }, false}, } @@ -136,7 +144,7 @@ func (suite *KeeperTestSuite) TestGetTimestampAtHeight() { tc.malleate() actualTimestamp, err := suite.chainA.App.GetIBCKeeper().ConnectionKeeper.GetTimestampAtHeight( - suite.chainA.GetContext(), connection, suite.chainB.LastHeader.GetHeight(), + suite.chainA.GetContext(), connection, height, ) if tc.expPass { diff --git a/modules/core/03-connection/keeper/params.go b/modules/core/03-connection/keeper/params.go index 26d2d4e843f..8723d5d7246 100644 --- a/modules/core/03-connection/keeper/params.go +++ b/modules/core/03-connection/keeper/params.go @@ -3,7 +3,7 @@ package keeper import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types" + "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" ) // GetMaxExpectedTimePerBlock retrieves the maximum expected time per block from the paramstore diff --git a/modules/core/03-connection/keeper/params_test.go b/modules/core/03-connection/keeper/params_test.go index 7e6ea66bbca..30450beb528 100644 --- a/modules/core/03-connection/keeper/params_test.go +++ b/modules/core/03-connection/keeper/params_test.go @@ -1,7 +1,7 @@ package keeper_test import ( - "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types" + "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" ) func (suite *KeeperTestSuite) TestParams() { @@ -12,6 +12,6 @@ func (suite *KeeperTestSuite) TestParams() { expParams.MaxExpectedTimePerBlock = 10 suite.chainA.App.GetIBCKeeper().ConnectionKeeper.SetParams(suite.chainA.GetContext(), expParams) - params = suite.chainA.App.GetIBCKeeper().ConnectionKeeper.GetParams(suite.chainA.GetContext()) + _ = suite.chainA.App.GetIBCKeeper().ConnectionKeeper.GetParams(suite.chainA.GetContext()) suite.Require().Equal(uint64(10), expParams.MaxExpectedTimePerBlock) } diff --git a/modules/core/03-connection/keeper/verify.go b/modules/core/03-connection/keeper/verify.go index e0cdbfcad03..4300ae3fa64 100644 --- a/modules/core/03-connection/keeper/verify.go +++ b/modules/core/03-connection/keeper/verify.go @@ -3,11 +3,16 @@ package keeper import ( "math" + errorsmod "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v4/modules/core/exported" + ibcerrors "github.com/cosmos/ibc-go/v7/internal/errors" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + connectiontypes "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + commitmenttypes "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + "github.com/cosmos/ibc-go/v7/modules/core/exported" ) // VerifyClientState verifies a proof of a client state of the running machine @@ -24,17 +29,30 @@ func (k Keeper) VerifyClientState( targetClient, found := k.clientKeeper.GetClientState(ctx, clientID) if !found { - return sdkerrors.Wrap(clienttypes.ErrClientNotFound, clientID) + return errorsmod.Wrap(clienttypes.ErrClientNotFound, clientID) } if status := targetClient.Status(ctx, clientStore, k.cdc); status != exported.Active { - return sdkerrors.Wrapf(clienttypes.ErrClientNotActive, "client (%s) status is %s", clientID, status) + return errorsmod.Wrapf(clienttypes.ErrClientNotActive, "client (%s) status is %s", clientID, status) } - if err := targetClient.VerifyClientState( - clientStore, k.cdc, height, - connection.GetCounterparty().GetPrefix(), connection.GetCounterparty().GetClientID(), proof, clientState); err != nil { - return sdkerrors.Wrapf(err, "failed client state verification for target client: %s", clientID) + merklePath := commitmenttypes.NewMerklePath(host.FullClientStatePath(connection.GetCounterparty().GetClientID())) + merklePath, err := commitmenttypes.ApplyPrefix(connection.GetCounterparty().GetPrefix(), merklePath) + if err != nil { + return err + } + + bz, err := k.cdc.MarshalInterface(clientState) + if err != nil { + return err + } + + if err := targetClient.VerifyMembership( + ctx, clientStore, k.cdc, height, + 0, 0, // skip delay period checks for non-packet processing verification + proof, merklePath, bz, + ); err != nil { + return errorsmod.Wrapf(err, "failed client state verification for target client: %s", clientID) } return nil @@ -55,18 +73,30 @@ func (k Keeper) VerifyClientConsensusState( clientState, found := k.clientKeeper.GetClientState(ctx, clientID) if !found { - return sdkerrors.Wrap(clienttypes.ErrClientNotFound, clientID) + return errorsmod.Wrap(clienttypes.ErrClientNotFound, clientID) } if status := clientState.Status(ctx, clientStore, k.cdc); status != exported.Active { - return sdkerrors.Wrapf(clienttypes.ErrClientNotActive, "client (%s) status is %s", clientID, status) + return errorsmod.Wrapf(clienttypes.ErrClientNotActive, "client (%s) status is %s", clientID, status) + } + + merklePath := commitmenttypes.NewMerklePath(host.FullConsensusStatePath(connection.GetCounterparty().GetClientID(), consensusHeight)) + merklePath, err := commitmenttypes.ApplyPrefix(connection.GetCounterparty().GetPrefix(), merklePath) + if err != nil { + return err } - if err := clientState.VerifyClientConsensusState( - clientStore, k.cdc, height, - connection.GetCounterparty().GetClientID(), consensusHeight, connection.GetCounterparty().GetPrefix(), proof, consensusState, + bz, err := k.cdc.MarshalInterface(consensusState) + if err != nil { + return err + } + + if err := clientState.VerifyMembership( + ctx, clientStore, k.cdc, height, + 0, 0, // skip delay period checks for non-packet processing verification + proof, merklePath, bz, ); err != nil { - return sdkerrors.Wrapf(err, "failed consensus state verification for client (%s)", clientID) + return errorsmod.Wrapf(err, "failed consensus state verification for client (%s)", clientID) } return nil @@ -80,25 +110,42 @@ func (k Keeper) VerifyConnectionState( height exported.Height, proof []byte, connectionID string, - connectionEnd exported.ConnectionI, // opposite connection + counterpartyConnection exported.ConnectionI, // opposite connection ) error { clientID := connection.GetClientID() clientStore := k.clientKeeper.ClientStore(ctx, clientID) clientState, found := k.clientKeeper.GetClientState(ctx, clientID) if !found { - return sdkerrors.Wrap(clienttypes.ErrClientNotFound, clientID) + return errorsmod.Wrap(clienttypes.ErrClientNotFound, clientID) } if status := clientState.Status(ctx, clientStore, k.cdc); status != exported.Active { - return sdkerrors.Wrapf(clienttypes.ErrClientNotActive, "client (%s) status is %s", clientID, status) + return errorsmod.Wrapf(clienttypes.ErrClientNotActive, "client (%s) status is %s", clientID, status) + } + + merklePath := commitmenttypes.NewMerklePath(host.ConnectionPath(connectionID)) + merklePath, err := commitmenttypes.ApplyPrefix(connection.GetCounterparty().GetPrefix(), merklePath) + if err != nil { + return err + } + + connectionEnd, ok := counterpartyConnection.(connectiontypes.ConnectionEnd) + if !ok { + return errorsmod.Wrapf(ibcerrors.ErrInvalidType, "invalid connection type %T", counterpartyConnection) + } + + bz, err := k.cdc.Marshal(&connectionEnd) + if err != nil { + return err } - if err := clientState.VerifyConnectionState( - clientStore, k.cdc, height, - connection.GetCounterparty().GetPrefix(), proof, connectionID, connectionEnd, + if err := clientState.VerifyMembership( + ctx, clientStore, k.cdc, height, + 0, 0, // skip delay period checks for non-packet processing verification + proof, merklePath, bz, ); err != nil { - return sdkerrors.Wrapf(err, "failed connection state verification for client (%s)", clientID) + return errorsmod.Wrapf(err, "failed connection state verification for client (%s)", clientID) } return nil @@ -120,19 +167,35 @@ func (k Keeper) VerifyChannelState( clientState, found := k.clientKeeper.GetClientState(ctx, clientID) if !found { - return sdkerrors.Wrap(clienttypes.ErrClientNotFound, clientID) + return errorsmod.Wrap(clienttypes.ErrClientNotFound, clientID) } if status := clientState.Status(ctx, clientStore, k.cdc); status != exported.Active { - return sdkerrors.Wrapf(clienttypes.ErrClientNotActive, "client (%s) status is %s", clientID, status) + return errorsmod.Wrapf(clienttypes.ErrClientNotActive, "client (%s) status is %s", clientID, status) + } + + merklePath := commitmenttypes.NewMerklePath(host.ChannelPath(portID, channelID)) + merklePath, err := commitmenttypes.ApplyPrefix(connection.GetCounterparty().GetPrefix(), merklePath) + if err != nil { + return err + } + + channelEnd, ok := channel.(channeltypes.Channel) + if !ok { + return errorsmod.Wrapf(ibcerrors.ErrInvalidType, "invalid channel type %T", channel) + } + + bz, err := k.cdc.Marshal(&channelEnd) + if err != nil { + return err } - if err := clientState.VerifyChannelState( - clientStore, k.cdc, height, - connection.GetCounterparty().GetPrefix(), proof, - portID, channelID, channel, + if err := clientState.VerifyMembership( + ctx, clientStore, k.cdc, height, + 0, 0, // skip delay period checks for non-packet processing verification + proof, merklePath, bz, ); err != nil { - return sdkerrors.Wrapf(err, "failed channel state verification for client (%s)", clientID) + return errorsmod.Wrapf(err, "failed channel state verification for client (%s)", clientID) } return nil @@ -155,24 +218,29 @@ func (k Keeper) VerifyPacketCommitment( clientState, found := k.clientKeeper.GetClientState(ctx, clientID) if !found { - return sdkerrors.Wrap(clienttypes.ErrClientNotFound, clientID) + return errorsmod.Wrap(clienttypes.ErrClientNotFound, clientID) } if status := clientState.Status(ctx, clientStore, k.cdc); status != exported.Active { - return sdkerrors.Wrapf(clienttypes.ErrClientNotActive, "client (%s) status is %s", clientID, status) + return errorsmod.Wrapf(clienttypes.ErrClientNotActive, "client (%s) status is %s", clientID, status) } // get time and block delays timeDelay := connection.GetDelayPeriod() blockDelay := k.getBlockDelay(ctx, connection) - if err := clientState.VerifyPacketCommitment( + merklePath := commitmenttypes.NewMerklePath(host.PacketCommitmentPath(portID, channelID, sequence)) + merklePath, err := commitmenttypes.ApplyPrefix(connection.GetCounterparty().GetPrefix(), merklePath) + if err != nil { + return err + } + + if err := clientState.VerifyMembership( ctx, clientStore, k.cdc, height, timeDelay, blockDelay, - connection.GetCounterparty().GetPrefix(), proof, portID, channelID, - sequence, commitmentBytes, + proof, merklePath, commitmentBytes, ); err != nil { - return sdkerrors.Wrapf(err, "failed packet commitment verification for client (%s)", clientID) + return errorsmod.Wrapf(err, "failed packet commitment verification for client (%s)", clientID) } return nil @@ -195,24 +263,29 @@ func (k Keeper) VerifyPacketAcknowledgement( clientState, found := k.clientKeeper.GetClientState(ctx, clientID) if !found { - return sdkerrors.Wrap(clienttypes.ErrClientNotFound, clientID) + return errorsmod.Wrap(clienttypes.ErrClientNotFound, clientID) } if status := clientState.Status(ctx, clientStore, k.cdc); status != exported.Active { - return sdkerrors.Wrapf(clienttypes.ErrClientNotActive, "client (%s) status is %s", clientID, status) + return errorsmod.Wrapf(clienttypes.ErrClientNotActive, "client (%s) status is %s", clientID, status) } // get time and block delays timeDelay := connection.GetDelayPeriod() blockDelay := k.getBlockDelay(ctx, connection) - if err := clientState.VerifyPacketAcknowledgement( + merklePath := commitmenttypes.NewMerklePath(host.PacketAcknowledgementPath(portID, channelID, sequence)) + merklePath, err := commitmenttypes.ApplyPrefix(connection.GetCounterparty().GetPrefix(), merklePath) + if err != nil { + return err + } + + if err := clientState.VerifyMembership( ctx, clientStore, k.cdc, height, timeDelay, blockDelay, - connection.GetCounterparty().GetPrefix(), proof, portID, channelID, - sequence, acknowledgement, + proof, merklePath, channeltypes.CommitAcknowledgement(acknowledgement), ); err != nil { - return sdkerrors.Wrapf(err, "failed packet acknowledgement verification for client (%s)", clientID) + return errorsmod.Wrapf(err, "failed packet acknowledgement verification for client (%s)", clientID) } return nil @@ -235,24 +308,29 @@ func (k Keeper) VerifyPacketReceiptAbsence( clientState, found := k.clientKeeper.GetClientState(ctx, clientID) if !found { - return sdkerrors.Wrap(clienttypes.ErrClientNotFound, clientID) + return errorsmod.Wrap(clienttypes.ErrClientNotFound, clientID) } if status := clientState.Status(ctx, clientStore, k.cdc); status != exported.Active { - return sdkerrors.Wrapf(clienttypes.ErrClientNotActive, "client (%s) status is %s", clientID, status) + return errorsmod.Wrapf(clienttypes.ErrClientNotActive, "client (%s) status is %s", clientID, status) } // get time and block delays timeDelay := connection.GetDelayPeriod() blockDelay := k.getBlockDelay(ctx, connection) - if err := clientState.VerifyPacketReceiptAbsence( + merklePath := commitmenttypes.NewMerklePath(host.PacketReceiptPath(portID, channelID, sequence)) + merklePath, err := commitmenttypes.ApplyPrefix(connection.GetCounterparty().GetPrefix(), merklePath) + if err != nil { + return err + } + + if err := clientState.VerifyNonMembership( ctx, clientStore, k.cdc, height, timeDelay, blockDelay, - connection.GetCounterparty().GetPrefix(), proof, portID, channelID, - sequence, + proof, merklePath, ); err != nil { - return sdkerrors.Wrapf(err, "failed packet receipt absence verification for client (%s)", clientID) + return errorsmod.Wrapf(err, "failed packet receipt absence verification for client (%s)", clientID) } return nil @@ -274,24 +352,29 @@ func (k Keeper) VerifyNextSequenceRecv( clientState, found := k.clientKeeper.GetClientState(ctx, clientID) if !found { - return sdkerrors.Wrap(clienttypes.ErrClientNotFound, clientID) + return errorsmod.Wrap(clienttypes.ErrClientNotFound, clientID) } if status := clientState.Status(ctx, clientStore, k.cdc); status != exported.Active { - return sdkerrors.Wrapf(clienttypes.ErrClientNotActive, "client (%s) status is %s", clientID, status) + return errorsmod.Wrapf(clienttypes.ErrClientNotActive, "client (%s) status is %s", clientID, status) } // get time and block delays timeDelay := connection.GetDelayPeriod() blockDelay := k.getBlockDelay(ctx, connection) - if err := clientState.VerifyNextSequenceRecv( + merklePath := commitmenttypes.NewMerklePath(host.NextSequenceRecvPath(portID, channelID)) + merklePath, err := commitmenttypes.ApplyPrefix(connection.GetCounterparty().GetPrefix(), merklePath) + if err != nil { + return err + } + + if err := clientState.VerifyMembership( ctx, clientStore, k.cdc, height, timeDelay, blockDelay, - connection.GetCounterparty().GetPrefix(), proof, portID, channelID, - nextSequenceRecv, + proof, merklePath, sdk.Uint64ToBigEndian(nextSequenceRecv), ); err != nil { - return sdkerrors.Wrapf(err, "failed next sequence receive verification for client (%s)", clientID) + return errorsmod.Wrapf(err, "failed next sequence receive verification for client (%s)", clientID) } return nil diff --git a/modules/core/03-connection/keeper/verify_test.go b/modules/core/03-connection/keeper/verify_test.go index 6641bf790bd..12600e8d9c3 100644 --- a/modules/core/03-connection/keeper/verify_test.go +++ b/modules/core/03-connection/keeper/verify_test.go @@ -4,17 +4,17 @@ import ( "fmt" "time" - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types" - channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" - "github.com/cosmos/ibc-go/v4/modules/core/exported" - ibctmtypes "github.com/cosmos/ibc-go/v4/modules/light-clients/07-tendermint/types" - ibctesting "github.com/cosmos/ibc-go/v4/testing" - ibcmock "github.com/cosmos/ibc-go/v4/testing/mock" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + "github.com/cosmos/ibc-go/v7/modules/core/exported" + ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" + ibctesting "github.com/cosmos/ibc-go/v7/testing" + ibcmock "github.com/cosmos/ibc-go/v7/testing/mock" ) -var defaultTimeoutHeight = clienttypes.NewHeight(0, 100000) +var defaultTimeoutHeight = clienttypes.NewHeight(1, 100000) // TestVerifyClientState verifies a client state of chainA // stored on path.EndpointB (which is on chainB) @@ -38,12 +38,12 @@ func (suite *KeeperTestSuite) TestVerifyClientState() { heightDiff = 5 }, false}, {"verification failed", func() { - counterpartyClient := path.EndpointB.GetClientState().(*ibctmtypes.ClientState) + counterpartyClient := path.EndpointB.GetClientState().(*ibctm.ClientState) counterpartyClient.ChainId = "wrongChainID" path.EndpointB.SetClientState(counterpartyClient) }, false}, {"client status is not active - client is expired", func() { - clientState := path.EndpointA.GetClientState().(*ibctmtypes.ClientState) + clientState := path.EndpointA.GetClientState().(*ibctm.ClientState) clientState.FrozenHeight = clienttypes.NewHeight(0, 1) path.EndpointA.SetClientState(clientState) }, false}, @@ -62,7 +62,7 @@ func (suite *KeeperTestSuite) TestVerifyClientState() { tc.malleate() counterpartyClient, clientProof := path.EndpointB.QueryClientStateProof() - proofHeight := clienttypes.NewHeight(0, uint64(suite.chainB.GetContext().BlockHeight()-1)) + proofHeight := clienttypes.NewHeight(1, uint64(suite.chainB.GetContext().BlockHeight()-1)) connection := path.EndpointA.GetConnection() @@ -109,7 +109,7 @@ func (suite *KeeperTestSuite) TestVerifyClientConsensusState() { consState, found := suite.chainB.App.GetIBCKeeper().ClientKeeper.GetLatestClientConsensusState(suite.chainB.GetContext(), path.EndpointB.ClientID) suite.Require().True(found) - tmConsState, ok := consState.(*ibctmtypes.ConsensusState) + tmConsState, ok := consState.(*ibctm.ConsensusState) suite.Require().True(ok) tmConsState.Timestamp = time.Now() @@ -118,7 +118,7 @@ func (suite *KeeperTestSuite) TestVerifyClientConsensusState() { suite.coordinator.CommitBlock(suite.chainB) }, false}, {"client status is not active - client is expired", func() { - clientState := path.EndpointA.GetClientState().(*ibctmtypes.ClientState) + clientState := path.EndpointA.GetClientState().(*ibctm.ClientState) clientState.FrozenHeight = clienttypes.NewHeight(0, 1) path.EndpointA.SetClientState(clientState) }, false}, @@ -138,7 +138,7 @@ func (suite *KeeperTestSuite) TestVerifyClientConsensusState() { connection := path.EndpointA.GetConnection() proof, consensusHeight := suite.chainB.QueryConsensusStateProof(path.EndpointB.ClientID) - proofHeight := clienttypes.NewHeight(0, uint64(suite.chainB.GetContext().BlockHeight()-1)) + proofHeight := clienttypes.NewHeight(1, uint64(suite.chainB.GetContext().BlockHeight()-1)) consensusState, err := suite.chainA.App.GetIBCKeeper().ClientKeeper.GetSelfConsensusState(suite.chainA.GetContext(), consensusHeight) suite.Require().NoError(err) @@ -183,7 +183,7 @@ func (suite *KeeperTestSuite) TestVerifyConnectionState() { path.EndpointA.SetConnection(connection) }, false}, {"client status is not active - client is expired", func() { - clientState := path.EndpointA.GetClientState().(*ibctmtypes.ClientState) + clientState := path.EndpointA.GetClientState().(*ibctm.ClientState) clientState.FrozenHeight = clienttypes.NewHeight(0, 1) path.EndpointA.SetClientState(clientState) }, false}, @@ -248,7 +248,7 @@ func (suite *KeeperTestSuite) TestVerifyChannelState() { path.EndpointA.SetChannel(channel) }, false}, {"client status is not active - client is expired", func() { - clientState := path.EndpointA.GetClientState().(*ibctmtypes.ClientState) + clientState := path.EndpointA.GetClientState().(*ibctm.ClientState) clientState.FrozenHeight = clienttypes.NewHeight(0, 1) path.EndpointA.SetClientState(clientState) }, false}, @@ -326,7 +326,7 @@ func (suite *KeeperTestSuite) TestVerifyPacketCommitment() { packet.Data = []byte(ibctesting.InvalidID) }, false}, {"client status is not active - client is expired", func() { - clientState := path.EndpointB.GetClientState().(*ibctmtypes.ClientState) + clientState := path.EndpointB.GetClientState().(*ibctm.ClientState) clientState.FrozenHeight = clienttypes.NewHeight(0, 1) path.EndpointB.SetClientState(clientState) }, false}, @@ -341,9 +341,9 @@ func (suite *KeeperTestSuite) TestVerifyPacketCommitment() { path = ibctesting.NewPath(suite.chainA, suite.chainB) suite.coordinator.Setup(path) - packet = channeltypes.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, defaultTimeoutHeight, 0) - err := path.EndpointA.SendPacket(packet) + sequence, err := path.EndpointA.SendPacket(defaultTimeoutHeight, 0, ibctesting.MockPacketData) suite.Require().NoError(err) + packet = channeltypes.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, defaultTimeoutHeight, 0) // reset variables heightDiff = 0 @@ -418,7 +418,7 @@ func (suite *KeeperTestSuite) TestVerifyPacketAcknowledgement() { ack = ibcmock.MockFailAcknowledgement }, false}, {"client status is not active - client is expired", func() { - clientState := path.EndpointA.GetClientState().(*ibctmtypes.ClientState) + clientState := path.EndpointA.GetClientState().(*ibctm.ClientState) clientState.FrozenHeight = clienttypes.NewHeight(0, 1) path.EndpointA.SetClientState(clientState) }, false}, @@ -435,14 +435,14 @@ func (suite *KeeperTestSuite) TestVerifyPacketAcknowledgement() { suite.coordinator.Setup(path) // send and receive packet - packet := channeltypes.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, defaultTimeoutHeight, 0) - err := path.EndpointA.SendPacket(packet) + sequence, err := path.EndpointA.SendPacket(defaultTimeoutHeight, 0, ibctesting.MockPacketData) suite.Require().NoError(err) // increment receiving chain's (chainB) time by 2 hour to always pass receive suite.coordinator.IncrementTimeBy(time.Hour * 2) suite.coordinator.CommitBlock(suite.chainB) + packet := channeltypes.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, defaultTimeoutHeight, 0) err = path.EndpointB.RecvPacket(packet) suite.Require().NoError(err) @@ -524,7 +524,7 @@ func (suite *KeeperTestSuite) TestVerifyPacketReceiptAbsence() { suite.Require().NoError(err) }, false}, {"client status is not active - client is expired", func() { - clientState := path.EndpointA.GetClientState().(*ibctmtypes.ClientState) + clientState := path.EndpointA.GetClientState().(*ibctm.ClientState) clientState.FrozenHeight = clienttypes.NewHeight(0, 1) path.EndpointA.SetClientState(clientState) }, false}, @@ -540,9 +540,9 @@ func (suite *KeeperTestSuite) TestVerifyPacketReceiptAbsence() { suite.coordinator.Setup(path) // send, only receive in malleate if applicable - packet = channeltypes.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, defaultTimeoutHeight, 0) - err := path.EndpointA.SendPacket(packet) + sequence, err := path.EndpointA.SendPacket(defaultTimeoutHeight, 0, ibctesting.MockPacketData) suite.Require().NoError(err) + packet = channeltypes.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, defaultTimeoutHeight, 0) // reset variables heightDiff = 0 @@ -553,11 +553,12 @@ func (suite *KeeperTestSuite) TestVerifyPacketReceiptAbsence() { connection := path.EndpointA.GetConnection() connection.DelayPeriod = delayTimePeriod - clientState := path.EndpointA.GetClientState().(*ibctmtypes.ClientState) + clientState := path.EndpointA.GetClientState().(*ibctm.ClientState) if clientState.FrozenHeight.IsZero() { // need to update height to prove absence or receipt suite.coordinator.CommitBlock(suite.chainA, suite.chainB) - path.EndpointA.UpdateClient() + err = path.EndpointA.UpdateClient() + suite.Require().NoError(err) } packetReceiptKey := host.PacketReceiptKey(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()) @@ -624,7 +625,7 @@ func (suite *KeeperTestSuite) TestVerifyNextSequenceRecv() { offsetSeq = 1 }, false}, {"client status is not active - client is expired", func() { - clientState := path.EndpointA.GetClientState().(*ibctmtypes.ClientState) + clientState := path.EndpointA.GetClientState().(*ibctm.ClientState) clientState.FrozenHeight = clienttypes.NewHeight(0, 1) path.EndpointA.SetClientState(clientState) }, false}, @@ -640,14 +641,14 @@ func (suite *KeeperTestSuite) TestVerifyNextSequenceRecv() { suite.coordinator.Setup(path) // send and receive packet - packet := channeltypes.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, defaultTimeoutHeight, 0) - err := path.EndpointA.SendPacket(packet) + sequence, err := path.EndpointA.SendPacket(defaultTimeoutHeight, 0, ibctesting.MockPacketData) suite.Require().NoError(err) // increment receiving chain's (chainB) time by 2 hour to always pass receive suite.coordinator.IncrementTimeBy(time.Hour * 2) suite.coordinator.CommitBlock(suite.chainB) + packet := channeltypes.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, defaultTimeoutHeight, 0) err = path.EndpointB.RecvPacket(packet) suite.Require().NoError(err) diff --git a/modules/core/03-connection/module.go b/modules/core/03-connection/module.go index 17132c269a9..cded57fb4af 100644 --- a/modules/core/03-connection/module.go +++ b/modules/core/03-connection/module.go @@ -1,11 +1,11 @@ package connection import ( - "github.com/gogo/protobuf/grpc" + "github.com/cosmos/gogoproto/grpc" "github.com/spf13/cobra" - "github.com/cosmos/ibc-go/v4/modules/core/03-connection/client/cli" - "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types" + "github.com/cosmos/ibc-go/v7/modules/core/03-connection/client/cli" + "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" ) // Name returns the IBC connection ICS name. diff --git a/modules/core/03-connection/simulation/decoder.go b/modules/core/03-connection/simulation/decoder.go index 803bdda728e..8fdf6c389e3 100644 --- a/modules/core/03-connection/simulation/decoder.go +++ b/modules/core/03-connection/simulation/decoder.go @@ -7,8 +7,8 @@ import ( "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/types/kv" - "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" + "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" ) // NewDecodeStore returns a decoder function closure that unmarshals the KVPair's diff --git a/modules/core/03-connection/simulation/decoder_test.go b/modules/core/03-connection/simulation/decoder_test.go index 77ac0f7e630..d93c1f10742 100644 --- a/modules/core/03-connection/simulation/decoder_test.go +++ b/modules/core/03-connection/simulation/decoder_test.go @@ -7,10 +7,10 @@ import ( "github.com/cosmos/cosmos-sdk/types/kv" "github.com/stretchr/testify/require" - "github.com/cosmos/ibc-go/v4/modules/core/03-connection/simulation" - "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" - "github.com/cosmos/ibc-go/v4/testing/simapp" + "github.com/cosmos/ibc-go/v7/modules/core/03-connection/simulation" + "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + "github.com/cosmos/ibc-go/v7/testing/simapp" ) func TestDecodeStore(t *testing.T) { diff --git a/modules/core/03-connection/simulation/genesis.go b/modules/core/03-connection/simulation/genesis.go index d60803babe0..5ffc3d4e522 100644 --- a/modules/core/03-connection/simulation/genesis.go +++ b/modules/core/03-connection/simulation/genesis.go @@ -5,7 +5,7 @@ import ( simtypes "github.com/cosmos/cosmos-sdk/types/simulation" - "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types" + "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" ) // GenConnectionGenesis returns the default connection genesis state. diff --git a/modules/core/03-connection/types/codec.go b/modules/core/03-connection/types/codec.go index f09a14f283c..8f6ad752fbf 100644 --- a/modules/core/03-connection/types/codec.go +++ b/modules/core/03-connection/types/codec.go @@ -6,7 +6,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/msgservice" - "github.com/cosmos/ibc-go/v4/modules/core/exported" + "github.com/cosmos/ibc-go/v7/modules/core/exported" ) // RegisterInterfaces register the ibc interfaces submodule implementations to protobuf diff --git a/modules/core/03-connection/types/connection.go b/modules/core/03-connection/types/connection.go index 508140a2de3..eddd471d51a 100644 --- a/modules/core/03-connection/types/connection.go +++ b/modules/core/03-connection/types/connection.go @@ -1,11 +1,12 @@ package types import ( - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + errorsmod "cosmossdk.io/errors" - commitmenttypes "github.com/cosmos/ibc-go/v4/modules/core/23-commitment/types" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" - "github.com/cosmos/ibc-go/v4/modules/core/exported" + ibcerrors "github.com/cosmos/ibc-go/v7/internal/errors" + commitmenttypes "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + "github.com/cosmos/ibc-go/v7/modules/core/exported" ) var _ exported.ConnectionI = (*ConnectionEnd)(nil) @@ -51,10 +52,10 @@ func (c ConnectionEnd) GetDelayPeriod() uint64 { // counterparty's. func (c ConnectionEnd) ValidateBasic() error { if err := host.ClientIdentifierValidator(c.ClientId); err != nil { - return sdkerrors.Wrap(err, "invalid client ID") + return errorsmod.Wrap(err, "invalid client ID") } if len(c.Versions) == 0 { - return sdkerrors.Wrap(sdkerrors.ErrInvalidVersion, "empty connection versions") + return errorsmod.Wrap(ibcerrors.ErrInvalidVersion, "empty connection versions") } for _, version := range c.Versions { if err := ValidateVersion(version); err != nil { @@ -94,14 +95,14 @@ func (c Counterparty) GetPrefix() exported.Prefix { func (c Counterparty) ValidateBasic() error { if c.ConnectionId != "" { if err := host.ConnectionIdentifierValidator(c.ConnectionId); err != nil { - return sdkerrors.Wrap(err, "invalid counterparty connection ID") + return errorsmod.Wrap(err, "invalid counterparty connection ID") } } if err := host.ClientIdentifierValidator(c.ClientId); err != nil { - return sdkerrors.Wrap(err, "invalid counterparty client ID") + return errorsmod.Wrap(err, "invalid counterparty client ID") } if c.Prefix.Empty() { - return sdkerrors.Wrap(ErrInvalidCounterparty, "counterparty prefix cannot be empty") + return errorsmod.Wrap(ErrInvalidCounterparty, "counterparty prefix cannot be empty") } return nil } @@ -121,7 +122,7 @@ func NewIdentifiedConnection(connectionID string, conn ConnectionEnd) Identified // ValidateBasic performs a basic validation of the connection identifier and connection fields. func (ic IdentifiedConnection) ValidateBasic() error { if err := host.ConnectionIdentifierValidator(ic.Id); err != nil { - return sdkerrors.Wrap(err, "invalid connection ID") + return errorsmod.Wrap(err, "invalid connection ID") } connection := NewConnectionEnd(ic.State, ic.ClientId, ic.Counterparty, ic.Versions, ic.DelayPeriod) return connection.ValidateBasic() diff --git a/modules/core/03-connection/types/connection.pb.go b/modules/core/03-connection/types/connection.pb.go index 893debbba18..86d1741edaf 100644 --- a/modules/core/03-connection/types/connection.pb.go +++ b/modules/core/03-connection/types/connection.pb.go @@ -5,9 +5,9 @@ package types import ( fmt "fmt" - types "github.com/cosmos/ibc-go/v4/modules/core/23-commitment/types" - _ "github.com/gogo/protobuf/gogoproto" - proto "github.com/gogo/protobuf/proto" + _ "github.com/cosmos/gogoproto/gogoproto" + proto "github.com/cosmos/gogoproto/proto" + types "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/types" io "io" math "math" math_bits "math/bits" @@ -421,49 +421,49 @@ var fileDescriptor_90572467c054e43a = []byte{ // 721 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x55, 0x4f, 0x6f, 0xda, 0x48, 0x14, 0xc7, 0xc6, 0x10, 0x18, 0xc2, 0x2e, 0x3b, 0x8b, 0x36, 0x16, 0xab, 0xd8, 0x96, 0x77, 0xb5, - 0x8b, 0x56, 0x0a, 0x5e, 0x92, 0xd5, 0x1e, 0xd2, 0xf6, 0x10, 0x08, 0x95, 0xac, 0xb6, 0x14, 0x39, - 0x24, 0x52, 0x73, 0x41, 0xc6, 0x9e, 0x90, 0x51, 0xb0, 0x07, 0xd9, 0x03, 0x82, 0x6f, 0x10, 0xe5, - 0xd4, 0x6b, 0x0f, 0x91, 0x2a, 0xf5, 0xbb, 0x54, 0x51, 0x4f, 0x39, 0xf6, 0x84, 0xaa, 0xe4, 0xda, - 0x13, 0x9f, 0xa0, 0xb2, 0xc7, 0x80, 0x13, 0x35, 0x95, 0x92, 0xf6, 0xf6, 0x9e, 0x7f, 0x7f, 0x78, - 0xef, 0xc7, 0xc3, 0x80, 0xbf, 0x71, 0xd7, 0xd2, 0x2c, 0xe2, 0x21, 0xcd, 0x22, 0xae, 0x8b, 0x2c, - 0x8a, 0x89, 0xab, 0x8d, 0xaa, 0xb1, 0xae, 0x32, 0xf0, 0x08, 0x25, 0xf0, 0x37, 0xdc, 0xb5, 0x2a, - 0x01, 0xb1, 0x12, 0x83, 0x46, 0xd5, 0x52, 0xb1, 0x47, 0x7a, 0x24, 0xa4, 0x68, 0x41, 0xc5, 0xd8, - 0xa5, 0xb8, 0xad, 0xe3, 0x60, 0xea, 0x20, 0x97, 0x32, 0xdb, 0x79, 0xc7, 0x88, 0xea, 0x7b, 0x1e, - 0xe4, 0xeb, 0x0b, 0xc3, 0x86, 0x6b, 0xc3, 0x2a, 0xc8, 0x5a, 0x7d, 0x8c, 0x5c, 0xda, 0xc1, 0xb6, - 0xc8, 0x29, 0x5c, 0x39, 0x5b, 0x2b, 0xce, 0xa6, 0x72, 0x61, 0x62, 0x3a, 0xfd, 0x6d, 0x75, 0x01, - 0xa9, 0x46, 0x86, 0xd5, 0xba, 0x0d, 0x1f, 0x81, 0xcc, 0x08, 0x79, 0x3e, 0x26, 0xae, 0x2f, 0xf2, - 0x4a, 0xb2, 0x9c, 0xdb, 0x94, 0x2b, 0x5f, 0x1f, 0xb7, 0x72, 0xc0, 0x78, 0xc6, 0x42, 0x00, 0xb7, - 0x40, 0xca, 0xa7, 0x26, 0x45, 0x62, 0x52, 0xe1, 0xca, 0x3f, 0x6d, 0xae, 0xdf, 0xa5, 0xdc, 0x0b, - 0x48, 0x06, 0xe3, 0xc2, 0x26, 0x58, 0xb5, 0xc8, 0xd0, 0xa5, 0xc8, 0x1b, 0x98, 0x1e, 0x9d, 0x88, - 0x82, 0xc2, 0x95, 0x73, 0x9b, 0x7f, 0xde, 0xa5, 0xad, 0xc7, 0xb8, 0x35, 0xe1, 0x62, 0x2a, 0x27, - 0x8c, 0x1b, 0x7a, 0xb8, 0x0d, 0x56, 0x6d, 0xd4, 0x37, 0x27, 0x9d, 0x01, 0xf2, 0x30, 0xb1, 0xc5, - 0x94, 0xc2, 0x95, 0x85, 0xda, 0xda, 0x6c, 0x2a, 0xff, 0xca, 0xf6, 0x8e, 0xa3, 0xaa, 0x91, 0x0b, - 0xdb, 0x56, 0xd8, 0x6d, 0x0b, 0xa7, 0x6f, 0xe5, 0x84, 0xfa, 0x99, 0x07, 0x45, 0xdd, 0x46, 0x2e, - 0xc5, 0x47, 0x18, 0xd9, 0xcb, 0x48, 0xe1, 0x3a, 0xe0, 0x17, 0x41, 0xe6, 0x67, 0x53, 0x39, 0xcb, - 0x0c, 0x83, 0x04, 0x79, 0x7c, 0x2b, 0x6e, 0xfe, 0xde, 0x71, 0x27, 0x1f, 0x1c, 0xb7, 0xf0, 0x1d, - 0x71, 0xa7, 0x7e, 0x70, 0xdc, 0xe9, 0x7b, 0xc7, 0xfd, 0x81, 0x03, 0xab, 0xf1, 0x8f, 0x79, 0xc8, - 0xd9, 0x3e, 0x01, 0xf9, 0xe5, 0xdc, 0xcb, 0xf8, 0xc5, 0xd9, 0x54, 0x2e, 0x46, 0xb2, 0x38, 0xac, - 0x06, 0x4b, 0xcc, 0x7b, 0xdd, 0x86, 0x35, 0x90, 0x1e, 0x78, 0xe8, 0x08, 0x8f, 0xc3, 0xcb, 0xbd, - 0x15, 0xc7, 0xe2, 0x67, 0x36, 0xaa, 0x56, 0x5e, 0x20, 0xef, 0xa4, 0x8f, 0x5a, 0x21, 0x37, 0x8a, - 0x23, 0x52, 0x46, 0xcb, 0xfc, 0x01, 0x72, 0xf5, 0x70, 0xa8, 0x96, 0x49, 0x8f, 0x7d, 0x58, 0x04, - 0xa9, 0x41, 0x50, 0x88, 0x9c, 0x92, 0x2c, 0x67, 0x0d, 0xd6, 0xa8, 0x87, 0xe0, 0xe7, 0xe5, 0x55, - 0x31, 0xe2, 0x03, 0x76, 0x5e, 0x78, 0xf3, 0x71, 0xef, 0x67, 0x60, 0x25, 0xba, 0x14, 0x28, 0x01, - 0x80, 0xe7, 0x67, 0xec, 0x31, 0x53, 0x23, 0xf6, 0x04, 0x96, 0x40, 0xe6, 0x08, 0x99, 0x74, 0xe8, - 0xa1, 0xb9, 0xc7, 0xa2, 0x8f, 0xb6, 0x71, 0x41, 0xba, 0x65, 0x7a, 0xa6, 0xe3, 0x43, 0x1b, 0xfc, - 0xee, 0x98, 0xe3, 0x0e, 0x1a, 0x0f, 0x90, 0x45, 0x91, 0xdd, 0xa1, 0xd8, 0x41, 0xc1, 0x97, 0xda, - 0xe9, 0xf6, 0x89, 0x75, 0x12, 0x9a, 0x0b, 0xb5, 0xbf, 0x66, 0x53, 0x59, 0x65, 0x13, 0x7f, 0x83, - 0xac, 0x1a, 0x6b, 0x8e, 0x39, 0x6e, 0x44, 0x60, 0x1b, 0x3b, 0xa8, 0x85, 0xbc, 0x5a, 0x80, 0xfc, - 0xf3, 0x86, 0x03, 0xa9, 0xf0, 0x5a, 0xe1, 0xff, 0x40, 0xde, 0x6b, 0xef, 0xb4, 0x1b, 0x9d, 0xfd, - 0xa6, 0xde, 0xd4, 0xdb, 0xfa, 0xce, 0x73, 0xfd, 0xb0, 0xb1, 0xdb, 0xd9, 0x6f, 0xee, 0xb5, 0x1a, - 0x75, 0xfd, 0xa9, 0xde, 0xd8, 0x2d, 0x24, 0x4a, 0xbf, 0x9c, 0x9d, 0x2b, 0xf9, 0x1b, 0x04, 0x28, - 0x02, 0xc0, 0x74, 0xc1, 0xc3, 0x02, 0x57, 0xca, 0x9c, 0x9d, 0x2b, 0x42, 0x50, 0x43, 0x09, 0xe4, - 0x19, 0xd2, 0x36, 0x5e, 0xbd, 0x6c, 0x35, 0x9a, 0x05, 0xbe, 0x94, 0x3b, 0x3b, 0x57, 0x56, 0xa2, - 0x76, 0xa9, 0x0c, 0xc1, 0x24, 0x53, 0x06, 0x75, 0x49, 0x38, 0x7d, 0x27, 0x25, 0x6a, 0x07, 0x17, - 0x57, 0x12, 0x77, 0x79, 0x25, 0x71, 0x9f, 0xae, 0x24, 0xee, 0xf5, 0xb5, 0x94, 0xb8, 0xbc, 0x96, - 0x12, 0x1f, 0xaf, 0xa5, 0xc4, 0xe1, 0xe3, 0x1e, 0xa6, 0xc7, 0xc3, 0x6e, 0x70, 0x2a, 0x9a, 0x45, - 0x7c, 0x87, 0xf8, 0x1a, 0xee, 0x5a, 0x1b, 0x3d, 0xa2, 0x8d, 0xfe, 0xd3, 0x1c, 0x62, 0x0f, 0xfb, - 0xc8, 0x67, 0x6f, 0xf0, 0x7f, 0xb7, 0x36, 0x62, 0xff, 0x0d, 0x74, 0x32, 0x40, 0x7e, 0x37, 0x1d, - 0xbe, 0xbd, 0xb7, 0xbe, 0x04, 0x00, 0x00, 0xff, 0xff, 0xfc, 0x2c, 0xbf, 0x64, 0x3f, 0x06, 0x00, + 0x8b, 0x56, 0x0a, 0x5e, 0x12, 0x69, 0x57, 0x4a, 0xdb, 0x43, 0x20, 0x54, 0xb2, 0xda, 0x52, 0xe4, + 0x90, 0x48, 0xcd, 0x05, 0x19, 0x7b, 0x42, 0x46, 0xc1, 0x1e, 0x64, 0x0f, 0x08, 0xbe, 0x41, 0x94, + 0x53, 0xaf, 0x3d, 0x44, 0xaa, 0xd4, 0xef, 0x52, 0x45, 0x3d, 0xe5, 0xd8, 0x13, 0xaa, 0x92, 0x6b, + 0x4f, 0x7c, 0x82, 0xca, 0x1e, 0x03, 0x4e, 0xd4, 0x54, 0x4a, 0xda, 0xdb, 0x7b, 0xfe, 0xfd, 0xe1, + 0xbd, 0x1f, 0x0f, 0x03, 0xfe, 0xc6, 0x5d, 0x4b, 0xb3, 0x88, 0x87, 0x34, 0x8b, 0xb8, 0x2e, 0xb2, + 0x28, 0x26, 0xae, 0x36, 0xaa, 0xc6, 0xba, 0xca, 0xc0, 0x23, 0x94, 0xc0, 0xdf, 0x70, 0xd7, 0xaa, + 0x04, 0xc4, 0x4a, 0x0c, 0x1a, 0x55, 0x4b, 0xc5, 0x1e, 0xe9, 0x91, 0x90, 0xa2, 0x05, 0x15, 0x63, + 0x97, 0xe2, 0xb6, 0x8e, 0x83, 0xa9, 0x83, 0x5c, 0xca, 0x6c, 0xe7, 0x1d, 0x23, 0xaa, 0xef, 0x79, + 0x90, 0xaf, 0x2f, 0x0c, 0x1b, 0xae, 0x0d, 0xab, 0x20, 0x6b, 0xf5, 0x31, 0x72, 0x69, 0x07, 0xdb, + 0x22, 0xa7, 0x70, 0xe5, 0x6c, 0xad, 0x38, 0x9b, 0xca, 0x85, 0x89, 0xe9, 0xf4, 0xb7, 0xd5, 0x05, + 0xa4, 0x1a, 0x19, 0x56, 0xeb, 0x36, 0x7c, 0x04, 0x32, 0x23, 0xe4, 0xf9, 0x98, 0xb8, 0xbe, 0xc8, + 0x2b, 0xc9, 0x72, 0x6e, 0x53, 0xae, 0x7c, 0x7d, 0xdc, 0xca, 0x01, 0xe3, 0x19, 0x0b, 0x01, 0xdc, + 0x02, 0x29, 0x9f, 0x9a, 0x14, 0x89, 0x49, 0x85, 0x2b, 0xff, 0xb4, 0xb9, 0x7e, 0x97, 0x72, 0x2f, + 0x20, 0x19, 0x8c, 0x0b, 0x9b, 0x60, 0xd5, 0x22, 0x43, 0x97, 0x22, 0x6f, 0x60, 0x7a, 0x74, 0x22, + 0x0a, 0x0a, 0x57, 0xce, 0x6d, 0xfe, 0x79, 0x97, 0xb6, 0x1e, 0xe3, 0xd6, 0x84, 0x8b, 0xa9, 0x9c, + 0x30, 0x6e, 0xe8, 0xe1, 0x36, 0x58, 0xb5, 0x51, 0xdf, 0x9c, 0x74, 0x06, 0xc8, 0xc3, 0xc4, 0x16, + 0x53, 0x0a, 0x57, 0x16, 0x6a, 0x6b, 0xb3, 0xa9, 0xfc, 0x2b, 0xdb, 0x3b, 0x8e, 0xaa, 0x46, 0x2e, + 0x6c, 0x5b, 0x61, 0xb7, 0x2d, 0x9c, 0xbe, 0x95, 0x13, 0xea, 0x67, 0x1e, 0x14, 0x75, 0x1b, 0xb9, + 0x14, 0x1f, 0x61, 0x64, 0x2f, 0x23, 0x85, 0xeb, 0x80, 0x5f, 0x04, 0x99, 0x9f, 0x4d, 0xe5, 0x2c, + 0x33, 0x0c, 0x12, 0xe4, 0xf1, 0xad, 0xb8, 0xf9, 0x7b, 0xc7, 0x9d, 0x7c, 0x70, 0xdc, 0xc2, 0x77, + 0xc4, 0x9d, 0xfa, 0xc1, 0x71, 0xa7, 0xef, 0x1d, 0xf7, 0x07, 0x0e, 0xac, 0xc6, 0x3f, 0xe6, 0x21, + 0x67, 0xfb, 0x04, 0xe4, 0x97, 0x73, 0x2f, 0xe3, 0x17, 0x67, 0x53, 0xb9, 0x18, 0xc9, 0xe2, 0xb0, + 0x1a, 0x2c, 0x31, 0xef, 0x75, 0x1b, 0xd6, 0x40, 0x7a, 0xe0, 0xa1, 0x23, 0x3c, 0x0e, 0x2f, 0xf7, + 0x56, 0x1c, 0x8b, 0x9f, 0xd9, 0xa8, 0x5a, 0x79, 0x81, 0xbc, 0x93, 0x3e, 0x6a, 0x85, 0xdc, 0x28, + 0x8e, 0x48, 0x19, 0x2d, 0xf3, 0x07, 0xc8, 0xd5, 0xc3, 0xa1, 0x5a, 0x26, 0x3d, 0xf6, 0x61, 0x11, + 0xa4, 0x06, 0x41, 0x21, 0x72, 0x4a, 0xb2, 0x9c, 0x35, 0x58, 0xa3, 0x1e, 0x82, 0x9f, 0x97, 0x57, + 0xc5, 0x88, 0x0f, 0xd8, 0x79, 0xe1, 0xcd, 0xc7, 0xbd, 0x9f, 0x81, 0x95, 0xe8, 0x52, 0xa0, 0x04, + 0x00, 0x9e, 0x9f, 0xb1, 0xc7, 0x4c, 0x8d, 0xd8, 0x13, 0x58, 0x02, 0x99, 0x23, 0x64, 0xd2, 0xa1, + 0x87, 0xe6, 0x1e, 0x8b, 0x3e, 0xda, 0xc6, 0x05, 0xe9, 0x96, 0xe9, 0x99, 0x8e, 0x0f, 0x6d, 0xf0, + 0xbb, 0x63, 0x8e, 0x3b, 0x68, 0x3c, 0x40, 0x16, 0x45, 0x76, 0x87, 0x62, 0x07, 0x05, 0x5f, 0x6a, + 0xa7, 0xdb, 0x27, 0xd6, 0x49, 0x68, 0x2e, 0xd4, 0xfe, 0x9a, 0x4d, 0x65, 0x95, 0x4d, 0xfc, 0x0d, + 0xb2, 0x6a, 0xac, 0x39, 0xe6, 0xb8, 0x11, 0x81, 0x6d, 0xec, 0xa0, 0x16, 0xf2, 0x6a, 0x01, 0xf2, + 0xcf, 0x1b, 0x0e, 0xa4, 0xc2, 0x6b, 0x85, 0xff, 0x01, 0x79, 0xaf, 0xbd, 0xd3, 0x6e, 0x74, 0xf6, + 0x9b, 0x7a, 0x53, 0x6f, 0xeb, 0x3b, 0xcf, 0xf5, 0xc3, 0xc6, 0x6e, 0x67, 0xbf, 0xb9, 0xd7, 0x6a, + 0xd4, 0xf5, 0xa7, 0x7a, 0x63, 0xb7, 0x90, 0x28, 0xfd, 0x72, 0x76, 0xae, 0xe4, 0x6f, 0x10, 0xa0, + 0x08, 0x00, 0xd3, 0x05, 0x0f, 0x0b, 0x5c, 0x29, 0x73, 0x76, 0xae, 0x08, 0x41, 0x0d, 0x25, 0x90, + 0x67, 0x48, 0xdb, 0x78, 0xf5, 0xb2, 0xd5, 0x68, 0x16, 0xf8, 0x52, 0xee, 0xec, 0x5c, 0x59, 0x89, + 0xda, 0xa5, 0x32, 0x04, 0x93, 0x4c, 0x19, 0xd4, 0x25, 0xe1, 0xf4, 0x9d, 0x94, 0xa8, 0x1d, 0x5c, + 0x5c, 0x49, 0xdc, 0xe5, 0x95, 0xc4, 0x7d, 0xba, 0x92, 0xb8, 0xd7, 0xd7, 0x52, 0xe2, 0xf2, 0x5a, + 0x4a, 0x7c, 0xbc, 0x96, 0x12, 0x87, 0x8f, 0x7b, 0x98, 0x1e, 0x0f, 0xbb, 0xc1, 0xa9, 0x68, 0x16, + 0xf1, 0x1d, 0xe2, 0x6b, 0xb8, 0x6b, 0x6d, 0xf4, 0x88, 0x36, 0xfa, 0x5f, 0x73, 0x88, 0x3d, 0xec, + 0x23, 0x9f, 0xbd, 0xc1, 0xff, 0xdd, 0xda, 0x88, 0xfd, 0x37, 0xd0, 0xc9, 0x00, 0xf9, 0xdd, 0x74, + 0xf8, 0xf6, 0xde, 0xfa, 0x12, 0x00, 0x00, 0xff, 0xff, 0x8f, 0xa7, 0x84, 0x5c, 0x3f, 0x06, 0x00, 0x00, } diff --git a/modules/core/03-connection/types/connection_test.go b/modules/core/03-connection/types/connection_test.go index f844052f5c8..56daca299b8 100644 --- a/modules/core/03-connection/types/connection_test.go +++ b/modules/core/03-connection/types/connection_test.go @@ -5,10 +5,10 @@ import ( "github.com/stretchr/testify/require" - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types" - commitmenttypes "github.com/cosmos/ibc-go/v4/modules/core/23-commitment/types" - ibctesting "github.com/cosmos/ibc-go/v4/testing" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" + commitmenttypes "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/types" + ibctesting "github.com/cosmos/ibc-go/v7/testing" ) var ( diff --git a/modules/core/03-connection/types/errors.go b/modules/core/03-connection/types/errors.go index 107a0e087c1..d131db91d93 100644 --- a/modules/core/03-connection/types/errors.go +++ b/modules/core/03-connection/types/errors.go @@ -1,19 +1,19 @@ package types import ( - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + errorsmod "cosmossdk.io/errors" ) // IBC connection sentinel errors var ( - ErrConnectionExists = sdkerrors.Register(SubModuleName, 2, "connection already exists") - ErrConnectionNotFound = sdkerrors.Register(SubModuleName, 3, "connection not found") - ErrClientConnectionPathsNotFound = sdkerrors.Register(SubModuleName, 4, "light client connection paths not found") - ErrConnectionPath = sdkerrors.Register(SubModuleName, 5, "connection path is not associated to the given light client") - ErrInvalidConnectionState = sdkerrors.Register(SubModuleName, 6, "invalid connection state") - ErrInvalidCounterparty = sdkerrors.Register(SubModuleName, 7, "invalid counterparty connection") - ErrInvalidConnection = sdkerrors.Register(SubModuleName, 8, "invalid connection") - ErrInvalidVersion = sdkerrors.Register(SubModuleName, 9, "invalid connection version") - ErrVersionNegotiationFailed = sdkerrors.Register(SubModuleName, 10, "connection version negotiation failed") - ErrInvalidConnectionIdentifier = sdkerrors.Register(SubModuleName, 11, "invalid connection identifier") + ErrConnectionExists = errorsmod.Register(SubModuleName, 2, "connection already exists") + ErrConnectionNotFound = errorsmod.Register(SubModuleName, 3, "connection not found") + ErrClientConnectionPathsNotFound = errorsmod.Register(SubModuleName, 4, "light client connection paths not found") + ErrConnectionPath = errorsmod.Register(SubModuleName, 5, "connection path is not associated to the given light client") + ErrInvalidConnectionState = errorsmod.Register(SubModuleName, 6, "invalid connection state") + ErrInvalidCounterparty = errorsmod.Register(SubModuleName, 7, "invalid counterparty connection") + ErrInvalidConnection = errorsmod.Register(SubModuleName, 8, "invalid connection") + ErrInvalidVersion = errorsmod.Register(SubModuleName, 9, "invalid connection version") + ErrVersionNegotiationFailed = errorsmod.Register(SubModuleName, 10, "connection version negotiation failed") + ErrInvalidConnectionIdentifier = errorsmod.Register(SubModuleName, 11, "invalid connection identifier") ) diff --git a/modules/core/03-connection/types/events.go b/modules/core/03-connection/types/events.go index 5afbb5fb904..64e40853d21 100644 --- a/modules/core/03-connection/types/events.go +++ b/modules/core/03-connection/types/events.go @@ -3,7 +3,7 @@ package types import ( "fmt" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" + ibcexported "github.com/cosmos/ibc-go/v7/modules/core/exported" ) // IBC connection events @@ -21,5 +21,5 @@ var ( EventTypeConnectionOpenAck = "connection_open_ack" EventTypeConnectionOpenConfirm = "connection_open_confirm" - AttributeValueCategory = fmt.Sprintf("%s_%s", host.ModuleName, SubModuleName) + AttributeValueCategory = fmt.Sprintf("%s_%s", ibcexported.ModuleName, SubModuleName) ) diff --git a/modules/core/03-connection/types/expected_keepers.go b/modules/core/03-connection/types/expected_keepers.go index fdb248423b5..5945766726d 100644 --- a/modules/core/03-connection/types/expected_keepers.go +++ b/modules/core/03-connection/types/expected_keepers.go @@ -3,7 +3,7 @@ package types import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/ibc-go/v4/modules/core/exported" + "github.com/cosmos/ibc-go/v7/modules/core/exported" ) // ClientKeeper expected account IBC client keeper @@ -12,6 +12,6 @@ type ClientKeeper interface { GetClientConsensusState(ctx sdk.Context, clientID string, height exported.Height) (exported.ConsensusState, bool) GetSelfConsensusState(ctx sdk.Context, height exported.Height) (exported.ConsensusState, error) ValidateSelfClient(ctx sdk.Context, clientState exported.ClientState) error - IterateClients(ctx sdk.Context, cb func(string, exported.ClientState) bool) + IterateClientStates(ctx sdk.Context, prefix []byte, cb func(string, exported.ClientState) bool) ClientStore(ctx sdk.Context, clientID string) sdk.KVStore } diff --git a/modules/core/03-connection/types/genesis.go b/modules/core/03-connection/types/genesis.go index 9f6bab811e3..3a4e5595f35 100644 --- a/modules/core/03-connection/types/genesis.go +++ b/modules/core/03-connection/types/genesis.go @@ -3,7 +3,7 @@ package types import ( "fmt" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" ) // NewConnectionPaths creates a ConnectionPaths instance. @@ -42,7 +42,7 @@ func DefaultGenesisState() GenesisState { func (gs GenesisState) Validate() error { // keep track of the max sequence to ensure it is less than // the next sequence used in creating connection identifers. - var maxSequence uint64 = 0 + var maxSequence uint64 for i, conn := range gs.Connections { sequence, err := ParseConnectionSequence(conn.Id) diff --git a/modules/core/03-connection/types/genesis.pb.go b/modules/core/03-connection/types/genesis.pb.go index 98fd5a14f67..126cc211457 100644 --- a/modules/core/03-connection/types/genesis.pb.go +++ b/modules/core/03-connection/types/genesis.pb.go @@ -5,8 +5,8 @@ package types import ( fmt "fmt" - _ "github.com/gogo/protobuf/gogoproto" - proto "github.com/gogo/protobuf/proto" + _ "github.com/cosmos/gogoproto/gogoproto" + proto "github.com/cosmos/gogoproto/proto" io "io" math "math" math_bits "math/bits" @@ -102,30 +102,30 @@ func init() { } var fileDescriptor_1879d34bc6ac3cd7 = []byte{ - // 358 bytes of a gzipped FileDescriptorProto + // 359 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x91, 0xc1, 0x4e, 0xea, 0x40, 0x14, 0x86, 0xdb, 0x0b, 0x61, 0x51, 0xee, 0xaa, 0xb9, 0x17, 0x1b, 0x16, 0x53, 0x52, 0x8d, 0xb0, - 0x90, 0x19, 0x01, 0x57, 0x86, 0x55, 0x5d, 0x18, 0x77, 0x04, 0x8c, 0x0b, 0x13, 0x43, 0xda, 0xe1, - 0x58, 0x26, 0xa1, 0x33, 0x95, 0x19, 0x88, 0x3c, 0x81, 0x0b, 0x37, 0x3e, 0x16, 0x4b, 0x96, 0xae, - 0x88, 0x81, 0x37, 0xe0, 0x09, 0x4c, 0x5b, 0x62, 0xd1, 0xd8, 0x5d, 0x73, 0xce, 0xf7, 0x7f, 0x7f, - 0x3a, 0xc7, 0x38, 0x61, 0x3e, 0x25, 0x54, 0x4c, 0x81, 0x50, 0xc1, 0x39, 0x50, 0xc5, 0x04, 0x27, - 0xf3, 0x16, 0x09, 0x80, 0x83, 0x64, 0x12, 0x47, 0x53, 0xa1, 0x84, 0x59, 0x61, 0x3e, 0xc5, 0x31, - 0x85, 0x33, 0x0a, 0xcf, 0x5b, 0xd5, 0x7f, 0x81, 0x08, 0x44, 0x82, 0x90, 0xf8, 0x2b, 0xa5, 0xab, - 0xf5, 0x1c, 0xe7, 0x41, 0x36, 0x01, 0x9d, 0xd7, 0x82, 0xf1, 0xf7, 0x3a, 0x2d, 0x1a, 0x28, 0x4f, - 0x81, 0x79, 0x6b, 0x94, 0x33, 0x48, 0x5a, 0x7a, 0xad, 0xd0, 0x28, 0xb7, 0xcf, 0xf0, 0xef, 0xed, - 0xf8, 0x66, 0x04, 0x5c, 0xb1, 0x47, 0x06, 0xa3, 0xab, 0xaf, 0xb9, 0x5b, 0x5c, 0xae, 0x6d, 0xad, - 0x7f, 0xa8, 0x31, 0x5f, 0x74, 0xe3, 0x88, 0x4e, 0x18, 0x70, 0x35, 0xcc, 0xc6, 0xc3, 0xc8, 0x53, - 0x63, 0x69, 0xfd, 0x49, 0x2a, 0xea, 0x79, 0x15, 0x99, 0xb8, 0x17, 0xe3, 0xee, 0x69, 0x6c, 0xdf, - 0xad, 0x6d, 0xb4, 0xf0, 0xc2, 0xc9, 0xa5, 0x93, 0x63, 0x75, 0xfa, 0xff, 0xd3, 0xcd, 0x8f, 0xb8, - 0xf9, 0x60, 0x58, 0x1c, 0x9e, 0xbf, 0x05, 0x24, 0x3c, 0xcd, 0x80, 0x53, 0xb0, 0x0a, 0x35, 0xbd, - 0x51, 0x74, 0x8f, 0x77, 0x6b, 0xdb, 0x4e, 0xe5, 0x79, 0xa4, 0xd3, 0xaf, 0xc4, 0xab, 0xcc, 0x3d, - 0xd8, 0x2f, 0xcc, 0xae, 0x51, 0x8a, 0xbc, 0xa9, 0x17, 0x4a, 0xab, 0x58, 0xd3, 0x1b, 0xe5, 0x36, - 0xca, 0xfb, 0xad, 0x5e, 0x42, 0xed, 0xdf, 0x6a, 0x9f, 0x71, 0xef, 0x96, 0x1b, 0xa4, 0xaf, 0x36, - 0x48, 0xff, 0xd8, 0x20, 0xfd, 0x6d, 0x8b, 0xb4, 0xd5, 0x16, 0x69, 0xef, 0x5b, 0xa4, 0xdd, 0x77, - 0x03, 0xa6, 0xc6, 0x33, 0x1f, 0x53, 0x11, 0x12, 0x2a, 0x64, 0x28, 0x24, 0x61, 0x3e, 0x6d, 0x06, - 0x82, 0xcc, 0x2f, 0x48, 0x28, 0x46, 0xb3, 0x09, 0xc8, 0xf4, 0xe0, 0xe7, 0x9d, 0xe6, 0xc1, 0xcd, - 0xd5, 0x22, 0x02, 0xe9, 0x97, 0x92, 0x63, 0x77, 0x3e, 0x03, 0x00, 0x00, 0xff, 0xff, 0x25, 0x96, - 0xe3, 0x66, 0x6b, 0x02, 0x00, 0x00, + 0x90, 0x19, 0x81, 0x85, 0x89, 0x61, 0x55, 0x17, 0xc6, 0x1d, 0x01, 0xe3, 0xc2, 0xc4, 0x90, 0x76, + 0x38, 0x96, 0x49, 0xe8, 0x4c, 0x65, 0x06, 0x22, 0x4f, 0xe0, 0xc2, 0x8d, 0x8f, 0xc5, 0x92, 0xa5, + 0x2b, 0x62, 0xe0, 0x0d, 0x78, 0x02, 0xd3, 0x96, 0x58, 0x34, 0x76, 0xd7, 0x9c, 0xf3, 0xfd, 0xdf, + 0x9f, 0xce, 0x31, 0x4e, 0x98, 0x4f, 0x09, 0x15, 0x53, 0x20, 0x54, 0x70, 0x0e, 0x54, 0x31, 0xc1, + 0xc9, 0xbc, 0x45, 0x02, 0xe0, 0x20, 0x99, 0xc4, 0xd1, 0x54, 0x28, 0x61, 0x56, 0x98, 0x4f, 0x71, + 0x4c, 0xe1, 0x8c, 0xc2, 0xf3, 0x56, 0xf5, 0x5f, 0x20, 0x02, 0x91, 0x20, 0x24, 0xfe, 0x4a, 0xe9, + 0x6a, 0x3d, 0xc7, 0x79, 0x90, 0x4d, 0x40, 0xe7, 0xb5, 0x60, 0xfc, 0xbd, 0x4e, 0x8b, 0x06, 0xca, + 0x53, 0x60, 0xde, 0x1a, 0xe5, 0x0c, 0x92, 0x96, 0x5e, 0x2b, 0x34, 0xca, 0xed, 0x33, 0xfc, 0x7b, + 0x3b, 0xbe, 0x19, 0x01, 0x57, 0xec, 0x91, 0xc1, 0xe8, 0xea, 0x6b, 0xee, 0x16, 0x97, 0x6b, 0x5b, + 0xeb, 0x1f, 0x6a, 0xcc, 0x17, 0xdd, 0x38, 0xa2, 0x13, 0x06, 0x5c, 0x0d, 0xb3, 0xf1, 0x30, 0xf2, + 0xd4, 0x58, 0x5a, 0x7f, 0x92, 0x8a, 0x7a, 0x5e, 0x45, 0x26, 0xee, 0xc5, 0xb8, 0x7b, 0x1a, 0xdb, + 0x77, 0x6b, 0x1b, 0x2d, 0xbc, 0x70, 0x72, 0xe9, 0xe4, 0x58, 0x9d, 0xfe, 0xff, 0x74, 0xf3, 0x23, + 0x6e, 0x3e, 0x18, 0x16, 0x87, 0xe7, 0x6f, 0x01, 0x09, 0x4f, 0x33, 0xe0, 0x14, 0xac, 0x42, 0x4d, + 0x6f, 0x14, 0xdd, 0xe3, 0xdd, 0xda, 0xb6, 0x53, 0x79, 0x1e, 0xe9, 0xf4, 0x2b, 0xf1, 0x2a, 0x73, + 0x0f, 0xf6, 0x0b, 0xb3, 0x6b, 0x94, 0x22, 0x6f, 0xea, 0x85, 0xd2, 0x2a, 0xd6, 0xf4, 0x46, 0xb9, + 0x8d, 0xf2, 0x7e, 0xab, 0x97, 0x50, 0xfb, 0xb7, 0xda, 0x67, 0xdc, 0xbb, 0xe5, 0x06, 0xe9, 0xab, + 0x0d, 0xd2, 0x3f, 0x36, 0x48, 0x7f, 0xdb, 0x22, 0x6d, 0xb5, 0x45, 0xda, 0xfb, 0x16, 0x69, 0xf7, + 0xdd, 0x80, 0xa9, 0xf1, 0xcc, 0xc7, 0x54, 0x84, 0x84, 0x0a, 0x19, 0x0a, 0x49, 0x98, 0x4f, 0x9b, + 0x81, 0x20, 0xf3, 0x0b, 0x12, 0x8a, 0xd1, 0x6c, 0x02, 0x32, 0x3d, 0xf8, 0x79, 0xa7, 0x79, 0x70, + 0x73, 0xb5, 0x88, 0x40, 0xfa, 0xa5, 0xe4, 0xd8, 0x9d, 0xcf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x56, + 0x1d, 0xd8, 0x5e, 0x6b, 0x02, 0x00, 0x00, } func (m *GenesisState) Marshal() (dAtA []byte, err error) { diff --git a/modules/core/03-connection/types/genesis_test.go b/modules/core/03-connection/types/genesis_test.go index eec0a61c9af..0b513cfdace 100644 --- a/modules/core/03-connection/types/genesis_test.go +++ b/modules/core/03-connection/types/genesis_test.go @@ -5,9 +5,9 @@ import ( "github.com/stretchr/testify/require" - "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types" - commitmenttypes "github.com/cosmos/ibc-go/v4/modules/core/23-commitment/types" - ibctesting "github.com/cosmos/ibc-go/v4/testing" + "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" + commitmenttypes "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/types" + ibctesting "github.com/cosmos/ibc-go/v7/testing" ) func TestValidateGenesis(t *testing.T) { diff --git a/modules/core/03-connection/types/keys.go b/modules/core/03-connection/types/keys.go index eaf6eb18e97..1b0b5132d90 100644 --- a/modules/core/03-connection/types/keys.go +++ b/modules/core/03-connection/types/keys.go @@ -4,9 +4,9 @@ import ( "fmt" "regexp" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + errorsmod "cosmossdk.io/errors" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" ) const ( @@ -50,12 +50,12 @@ func IsValidConnectionID(connectionID string) bool { // ParseConnectionSequence parses the connection sequence from the connection identifier. func ParseConnectionSequence(connectionID string) (uint64, error) { if !IsConnectionIDFormat(connectionID) { - return 0, sdkerrors.Wrap(host.ErrInvalidID, "connection identifier is not in the format: `connection-{N}`") + return 0, errorsmod.Wrap(host.ErrInvalidID, "connection identifier is not in the format: `connection-{N}`") } sequence, err := host.ParseIdentifier(connectionID, ConnectionPrefix) if err != nil { - return 0, sdkerrors.Wrap(err, "invalid connection identifier") + return 0, errorsmod.Wrap(err, "invalid connection identifier") } return sequence, nil diff --git a/modules/core/03-connection/types/keys_test.go b/modules/core/03-connection/types/keys_test.go index 1df435836da..2185ebc462c 100644 --- a/modules/core/03-connection/types/keys_test.go +++ b/modules/core/03-connection/types/keys_test.go @@ -6,7 +6,7 @@ import ( "github.com/stretchr/testify/require" - "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types" + "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" ) // tests ParseConnectionSequence and IsValidConnectionID diff --git a/modules/core/03-connection/types/msgs.go b/modules/core/03-connection/types/msgs.go index fb410d095b2..d05efe72758 100644 --- a/modules/core/03-connection/types/msgs.go +++ b/modules/core/03-connection/types/msgs.go @@ -1,14 +1,15 @@ package types import ( + errorsmod "cosmossdk.io/errors" codectypes "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - commitmenttypes "github.com/cosmos/ibc-go/v4/modules/core/23-commitment/types" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" - "github.com/cosmos/ibc-go/v4/modules/core/exported" + ibcerrors "github.com/cosmos/ibc-go/v7/internal/errors" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + commitmenttypes "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + "github.com/cosmos/ibc-go/v7/modules/core/exported" ) var ( @@ -23,6 +24,7 @@ var ( // NewMsgConnectionOpenInit creates a new MsgConnectionOpenInit instance. It sets the // counterparty connection identifier to be empty. +// //nolint:interfacer func NewMsgConnectionOpenInit( clientID, counterpartyClientID string, @@ -43,21 +45,21 @@ func NewMsgConnectionOpenInit( // ValidateBasic implements sdk.Msg. func (msg MsgConnectionOpenInit) ValidateBasic() error { if err := host.ClientIdentifierValidator(msg.ClientId); err != nil { - return sdkerrors.Wrap(err, "invalid client ID") + return errorsmod.Wrap(err, "invalid client ID") } if msg.Counterparty.ConnectionId != "" { - return sdkerrors.Wrap(ErrInvalidCounterparty, "counterparty connection identifier must be empty") + return errorsmod.Wrap(ErrInvalidCounterparty, "counterparty connection identifier must be empty") } // NOTE: Version can be nil on MsgConnectionOpenInit if msg.Version != nil { if err := ValidateVersion(msg.Version); err != nil { - return sdkerrors.Wrap(err, "basic validation of the provided version failed") + return errorsmod.Wrap(err, "basic validation of the provided version failed") } } _, err := sdk.AccAddressFromBech32(msg.Signer) if err != nil { - return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) + return errorsmod.Wrapf(ibcerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) } return msg.Counterparty.ValidateBasic() } @@ -72,21 +74,21 @@ func (msg MsgConnectionOpenInit) GetSigners() []sdk.AccAddress { } // NewMsgConnectionOpenTry creates a new MsgConnectionOpenTry instance +// //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, proofHeight, consensusHeight clienttypes.Height, signer string, ) *MsgConnectionOpenTry { counterparty := NewCounterparty(counterpartyClientID, counterpartyConnectionID, counterpartyPrefix) - csAny, _ := clienttypes.PackClientState(counterpartyClient) + protoAny, _ := clienttypes.PackClientState(counterpartyClient) return &MsgConnectionOpenTry{ - PreviousConnectionId: previousConnectionID, ClientId: clientID, - ClientState: csAny, + ClientState: protoAny, Counterparty: counterparty, CounterpartyVersions: counterpartyVersions, DelayPeriod: delayPeriod, @@ -101,55 +103,49 @@ 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 errorsmod.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") + return errorsmod.Wrap(err, "invalid client ID") } // counterparty validate basic allows empty counterparty connection identifiers if err := host.ConnectionIdentifierValidator(msg.Counterparty.ConnectionId); err != nil { - return sdkerrors.Wrap(err, "invalid counterparty connection ID") + return errorsmod.Wrap(err, "invalid counterparty connection ID") } if msg.ClientState == nil { - return sdkerrors.Wrap(clienttypes.ErrInvalidClient, "counterparty client is nil") + return errorsmod.Wrap(clienttypes.ErrInvalidClient, "counterparty client is nil") } clientState, err := clienttypes.UnpackClientState(msg.ClientState) if err != nil { - return sdkerrors.Wrapf(clienttypes.ErrInvalidClient, "unpack err: %v", err) + return errorsmod.Wrapf(clienttypes.ErrInvalidClient, "unpack err: %v", err) } if err := clientState.Validate(); err != nil { - return sdkerrors.Wrap(err, "counterparty client is invalid") + return errorsmod.Wrap(err, "counterparty client is invalid") } if len(msg.CounterpartyVersions) == 0 { - return sdkerrors.Wrap(sdkerrors.ErrInvalidVersion, "empty counterparty versions") + return errorsmod.Wrap(ibcerrors.ErrInvalidVersion, "empty counterparty versions") } for i, version := range msg.CounterpartyVersions { if err := ValidateVersion(version); err != nil { - return sdkerrors.Wrapf(err, "basic validation failed on version with index %d", i) + return errorsmod.Wrapf(err, "basic validation failed on version with index %d", i) } } if len(msg.ProofInit) == 0 { - return sdkerrors.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit an empty proof init") + return errorsmod.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit an empty proof init") } if len(msg.ProofClient) == 0 { - return sdkerrors.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit empty proof client") + return errorsmod.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit empty proof client") } if len(msg.ProofConsensus) == 0 { - return sdkerrors.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit an empty proof of consensus state") - } - if msg.ProofHeight.IsZero() { - return sdkerrors.Wrap(sdkerrors.ErrInvalidHeight, "proof height must be non-zero") + return errorsmod.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit an empty proof of consensus state") } if msg.ConsensusHeight.IsZero() { - return sdkerrors.Wrap(sdkerrors.ErrInvalidHeight, "consensus height must be non-zero") + return errorsmod.Wrap(ibcerrors.ErrInvalidHeight, "consensus height must be non-zero") } _, err = sdk.AccAddressFromBech32(msg.Signer) if err != nil { - return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) + return errorsmod.Wrapf(ibcerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) } return msg.Counterparty.ValidateBasic() } @@ -169,6 +165,7 @@ func (msg MsgConnectionOpenTry) GetSigners() []sdk.AccAddress { } // NewMsgConnectionOpenAck creates a new MsgConnectionOpenAck instance +// //nolint:interfacer func NewMsgConnectionOpenAck( connectionID, counterpartyConnectionID string, counterpartyClient exported.ClientState, @@ -177,11 +174,11 @@ func NewMsgConnectionOpenAck( version *Version, signer string, ) *MsgConnectionOpenAck { - csAny, _ := clienttypes.PackClientState(counterpartyClient) + protoAny, _ := clienttypes.PackClientState(counterpartyClient) return &MsgConnectionOpenAck{ ConnectionId: connectionID, CounterpartyConnectionId: counterpartyConnectionID, - ClientState: csAny, + ClientState: protoAny, ProofTry: proofTry, ProofClient: proofClient, ProofConsensus: proofConsensus, @@ -203,39 +200,36 @@ func (msg MsgConnectionOpenAck) ValidateBasic() error { return ErrInvalidConnectionIdentifier } if err := host.ConnectionIdentifierValidator(msg.CounterpartyConnectionId); err != nil { - return sdkerrors.Wrap(err, "invalid counterparty connection ID") + return errorsmod.Wrap(err, "invalid counterparty connection ID") } if err := ValidateVersion(msg.Version); err != nil { return err } if msg.ClientState == nil { - return sdkerrors.Wrap(clienttypes.ErrInvalidClient, "counterparty client is nil") + return errorsmod.Wrap(clienttypes.ErrInvalidClient, "counterparty client is nil") } clientState, err := clienttypes.UnpackClientState(msg.ClientState) if err != nil { - return sdkerrors.Wrapf(clienttypes.ErrInvalidClient, "unpack err: %v", err) + return errorsmod.Wrapf(clienttypes.ErrInvalidClient, "unpack err: %v", err) } if err := clientState.Validate(); err != nil { - return sdkerrors.Wrap(err, "counterparty client is invalid") + return errorsmod.Wrap(err, "counterparty client is invalid") } if len(msg.ProofTry) == 0 { - return sdkerrors.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit an empty proof try") + return errorsmod.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit an empty proof try") } if len(msg.ProofClient) == 0 { - return sdkerrors.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit empty proof client") + return errorsmod.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit empty proof client") } if len(msg.ProofConsensus) == 0 { - return sdkerrors.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit an empty proof of consensus state") - } - if msg.ProofHeight.IsZero() { - return sdkerrors.Wrap(sdkerrors.ErrInvalidHeight, "proof height must be non-zero") + return errorsmod.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit an empty proof of consensus state") } if msg.ConsensusHeight.IsZero() { - return sdkerrors.Wrap(sdkerrors.ErrInvalidHeight, "consensus height must be non-zero") + return errorsmod.Wrap(ibcerrors.ErrInvalidHeight, "consensus height must be non-zero") } _, err = sdk.AccAddressFromBech32(msg.Signer) if err != nil { - return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) + return errorsmod.Wrapf(ibcerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) } return nil } @@ -250,6 +244,7 @@ func (msg MsgConnectionOpenAck) GetSigners() []sdk.AccAddress { } // NewMsgConnectionOpenConfirm creates a new MsgConnectionOpenConfirm instance +// //nolint:interfacer func NewMsgConnectionOpenConfirm( connectionID string, proofAck []byte, proofHeight clienttypes.Height, @@ -269,14 +264,11 @@ func (msg MsgConnectionOpenConfirm) ValidateBasic() error { return ErrInvalidConnectionIdentifier } if len(msg.ProofAck) == 0 { - return sdkerrors.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit an empty proof ack") - } - if msg.ProofHeight.IsZero() { - return sdkerrors.Wrap(sdkerrors.ErrInvalidHeight, "proof height must be non-zero") + return errorsmod.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit an empty proof ack") } _, err := sdk.AccAddressFromBech32(msg.Signer) if err != nil { - return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) + return errorsmod.Wrapf(ibcerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) } return nil } diff --git a/modules/core/03-connection/types/msgs_test.go b/modules/core/03-connection/types/msgs_test.go index 8c4945973bf..c146f782c40 100644 --- a/modules/core/03-connection/types/msgs_test.go +++ b/modules/core/03-connection/types/msgs_test.go @@ -10,14 +10,15 @@ import ( storetypes "github.com/cosmos/cosmos-sdk/store/types" "github.com/stretchr/testify/suite" abci "github.com/tendermint/tendermint/abci/types" + log "github.com/tendermint/tendermint/libs/log" dbm "github.com/tendermint/tm-db" - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types" - commitmenttypes "github.com/cosmos/ibc-go/v4/modules/core/23-commitment/types" - ibctmtypes "github.com/cosmos/ibc-go/v4/modules/light-clients/07-tendermint/types" - ibctesting "github.com/cosmos/ibc-go/v4/testing" - "github.com/cosmos/ibc-go/v4/testing/simapp" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" + commitmenttypes "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/types" + ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" + ibctesting "github.com/cosmos/ibc-go/v7/testing" + "github.com/cosmos/ibc-go/v7/testing/simapp" ) var ( @@ -46,11 +47,13 @@ func (suite *MsgTestSuite) SetupTest() { app := simapp.Setup(false) db := dbm.NewMemDB() - store := rootmulti.NewStore(db) + dblog := log.TestingLogger() + store := rootmulti.NewStore(db, dblog) storeKey := storetypes.NewKVStoreKey("iavlStoreKey") store.MountStoreWithDB(storeKey, storetypes.StoreTypeIAVL, nil) - store.LoadVersion(0) + err := store.LoadVersion(0) + suite.Require().NoError(err) iavlStore := store.GetCommitStore(storeKey).(*iavl.Store) iavlStore.Set([]byte("KEY"), []byte("VALUE")) @@ -107,20 +110,22 @@ func (suite *MsgTestSuite) TestNewMsgConnectionOpenInit() { func (suite *MsgTestSuite) TestNewMsgConnectionOpenTry() { prefix := commitmenttypes.NewMerklePrefix([]byte("storePrefixKey")) - clientState := ibctmtypes.NewClientState( - chainID, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false, + clientState := ibctm.NewClientState( + chainID, ibctm.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, ) + protoAny, err := clienttypes.PackClientState(clientState) + suite.Require().NoError(err) // Pack consensus state into any to test unpacking error - consState := ibctmtypes.NewConsensusState( + consState := ibctm.NewConsensusState( time.Now(), commitmenttypes.NewMerkleRoot([]byte("root")), []byte("nextValsHash"), ) invalidAny := clienttypes.MustPackConsensusState(consState) counterparty := types.NewCounterparty("connectiontotest", "clienttotest", prefix) // invalidClientState fails validateBasic - invalidClient := ibctmtypes.NewClientState( - chainID, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clienttypes.ZeroHeight(), commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false, + invalidClient := ibctm.NewClientState( + chainID, ibctm.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clienttypes.ZeroHeight(), commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, ) testCases := []struct { @@ -128,24 +133,22 @@ 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", protoAny, counterparty, 500, []*types.Version{ibctesting.ConnectionVersion}, clientHeight, suite.proof, suite.proof, suite.proof, clientHeight, signer, nil}, 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, nil}, 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 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 { @@ -159,19 +162,19 @@ func (suite *MsgTestSuite) TestNewMsgConnectionOpenTry() { } func (suite *MsgTestSuite) TestNewMsgConnectionOpenAck() { - clientState := ibctmtypes.NewClientState( - chainID, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false, + clientState := ibctm.NewClientState( + chainID, ibctm.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, ) // Pack consensus state into any to test unpacking error - consState := ibctmtypes.NewConsensusState( + consState := ibctm.NewConsensusState( time.Now(), commitmenttypes.NewMerkleRoot([]byte("root")), []byte("nextValsHash"), ) invalidAny := clienttypes.MustPackConsensusState(consState) // invalidClientState fails validateBasic - invalidClient := ibctmtypes.NewClientState( - chainID, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clienttypes.ZeroHeight(), commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false, + invalidClient := ibctm.NewClientState( + chainID, ibctm.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clienttypes.ZeroHeight(), commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, ) connectionID := "connection-0" @@ -183,12 +186,11 @@ func (suite *MsgTestSuite) TestNewMsgConnectionOpenAck() { {"invalid connection ID", types.NewMsgConnectionOpenAck("test/conn1", connectionID, clientState, suite.proof, suite.proof, suite.proof, clientHeight, clientHeight, ibctesting.ConnectionVersion, signer), false}, {"invalid counterparty connection ID", types.NewMsgConnectionOpenAck(connectionID, "test/conn1", clientState, suite.proof, suite.proof, suite.proof, clientHeight, clientHeight, ibctesting.ConnectionVersion, signer), false}, {"invalid nil counterparty client", types.NewMsgConnectionOpenAck(connectionID, connectionID, nil, suite.proof, suite.proof, suite.proof, clientHeight, clientHeight, ibctesting.ConnectionVersion, signer), false}, - {"invalid unpacking counterparty client", &types.MsgConnectionOpenAck{connectionID, connectionID, ibctesting.ConnectionVersion, invalidAny, clientHeight, suite.proof, suite.proof, suite.proof, clientHeight, signer}, false}, + {"invalid unpacking counterparty client", &types.MsgConnectionOpenAck{connectionID, connectionID, ibctesting.ConnectionVersion, invalidAny, clientHeight, suite.proof, suite.proof, suite.proof, clientHeight, signer, nil}, false}, {"counterparty client failed validate", types.NewMsgConnectionOpenAck(connectionID, connectionID, invalidClient, suite.proof, suite.proof, suite.proof, clientHeight, clientHeight, ibctesting.ConnectionVersion, signer), false}, {"empty proofTry", types.NewMsgConnectionOpenAck(connectionID, connectionID, clientState, emptyProof, suite.proof, suite.proof, clientHeight, clientHeight, ibctesting.ConnectionVersion, signer), false}, {"empty proofClient", types.NewMsgConnectionOpenAck(connectionID, connectionID, clientState, suite.proof, emptyProof, suite.proof, clientHeight, clientHeight, ibctesting.ConnectionVersion, signer), false}, {"empty proofConsensus", types.NewMsgConnectionOpenAck(connectionID, connectionID, clientState, suite.proof, suite.proof, emptyProof, clientHeight, clientHeight, ibctesting.ConnectionVersion, signer), false}, - {"invalid proofHeight", types.NewMsgConnectionOpenAck(connectionID, connectionID, clientState, suite.proof, suite.proof, suite.proof, clienttypes.ZeroHeight(), clientHeight, ibctesting.ConnectionVersion, signer), false}, {"invalid consensusHeight", types.NewMsgConnectionOpenAck(connectionID, connectionID, clientState, suite.proof, suite.proof, suite.proof, clientHeight, clienttypes.ZeroHeight(), ibctesting.ConnectionVersion, signer), false}, {"invalid version", types.NewMsgConnectionOpenAck(connectionID, connectionID, clientState, suite.proof, suite.proof, suite.proof, clientHeight, clientHeight, &types.Version{}, signer), false}, {"empty signer", types.NewMsgConnectionOpenAck(connectionID, connectionID, clientState, suite.proof, suite.proof, suite.proof, clientHeight, clientHeight, ibctesting.ConnectionVersion, ""), false}, @@ -209,7 +211,6 @@ func (suite *MsgTestSuite) TestNewMsgConnectionOpenConfirm() { testMsgs := []*types.MsgConnectionOpenConfirm{ types.NewMsgConnectionOpenConfirm("test/conn1", suite.proof, clientHeight, signer), types.NewMsgConnectionOpenConfirm(connectionID, emptyProof, clientHeight, signer), - types.NewMsgConnectionOpenConfirm(connectionID, suite.proof, clienttypes.ZeroHeight(), signer), types.NewMsgConnectionOpenConfirm(connectionID, suite.proof, clientHeight, ""), types.NewMsgConnectionOpenConfirm(connectionID, suite.proof, clientHeight, signer), } @@ -221,9 +222,8 @@ func (suite *MsgTestSuite) TestNewMsgConnectionOpenConfirm() { }{ {testMsgs[0], false, "invalid connection ID"}, {testMsgs[1], false, "empty proofTry"}, - {testMsgs[2], false, "invalid proofHeight"}, - {testMsgs[3], false, "empty signer"}, - {testMsgs[4], true, "success"}, + {testMsgs[2], false, "empty signer"}, + {testMsgs[3], true, "success"}, } for i, tc := range testCases { diff --git a/modules/core/03-connection/types/params_test.go b/modules/core/03-connection/types/params_test.go index fa8e61a7556..8afc407d723 100644 --- a/modules/core/03-connection/types/params_test.go +++ b/modules/core/03-connection/types/params_test.go @@ -5,7 +5,7 @@ import ( "github.com/stretchr/testify/require" - "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types" + "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" ) func TestValidateParams(t *testing.T) { diff --git a/modules/core/03-connection/types/query.go b/modules/core/03-connection/types/query.go index 095995cec63..a5277bee696 100644 --- a/modules/core/03-connection/types/query.go +++ b/modules/core/03-connection/types/query.go @@ -3,8 +3,8 @@ package types import ( codectypes "github.com/cosmos/cosmos-sdk/codec/types" - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v4/modules/core/exported" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v7/modules/core/exported" ) var ( diff --git a/modules/core/03-connection/types/query.pb.go b/modules/core/03-connection/types/query.pb.go index b180904f9c5..2d87fa0c3ff 100644 --- a/modules/core/03-connection/types/query.pb.go +++ b/modules/core/03-connection/types/query.pb.go @@ -8,10 +8,10 @@ import ( fmt "fmt" types1 "github.com/cosmos/cosmos-sdk/codec/types" query "github.com/cosmos/cosmos-sdk/types/query" - types "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - _ "github.com/gogo/protobuf/gogoproto" - grpc1 "github.com/gogo/protobuf/grpc" - proto "github.com/gogo/protobuf/proto" + _ "github.com/cosmos/gogoproto/gogoproto" + grpc1 "github.com/cosmos/gogoproto/grpc" + proto "github.com/cosmos/gogoproto/proto" + types "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" _ "google.golang.org/genproto/googleapis/api/annotations" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" @@ -617,6 +617,89 @@ func (m *QueryConnectionConsensusStateResponse) GetProofHeight() types.Height { return types.Height{} } +// QueryConnectionParamsRequest is the request type for the Query/ConnectionParams RPC method. +type QueryConnectionParamsRequest struct { +} + +func (m *QueryConnectionParamsRequest) Reset() { *m = QueryConnectionParamsRequest{} } +func (m *QueryConnectionParamsRequest) String() string { return proto.CompactTextString(m) } +func (*QueryConnectionParamsRequest) ProtoMessage() {} +func (*QueryConnectionParamsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_cd8d529f8c7cd06b, []int{10} +} +func (m *QueryConnectionParamsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryConnectionParamsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryConnectionParamsRequest.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 *QueryConnectionParamsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryConnectionParamsRequest.Merge(m, src) +} +func (m *QueryConnectionParamsRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryConnectionParamsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryConnectionParamsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryConnectionParamsRequest proto.InternalMessageInfo + +// QueryConnectionParamsResponse is the response type for the Query/ConnectionParams RPC method. +type QueryConnectionParamsResponse struct { + // params defines the parameters of the module. + Params *Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params,omitempty"` +} + +func (m *QueryConnectionParamsResponse) Reset() { *m = QueryConnectionParamsResponse{} } +func (m *QueryConnectionParamsResponse) String() string { return proto.CompactTextString(m) } +func (*QueryConnectionParamsResponse) ProtoMessage() {} +func (*QueryConnectionParamsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_cd8d529f8c7cd06b, []int{11} +} +func (m *QueryConnectionParamsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryConnectionParamsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryConnectionParamsResponse.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 *QueryConnectionParamsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryConnectionParamsResponse.Merge(m, src) +} +func (m *QueryConnectionParamsResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryConnectionParamsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryConnectionParamsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryConnectionParamsResponse proto.InternalMessageInfo + +func (m *QueryConnectionParamsResponse) GetParams() *Params { + if m != nil { + return m.Params + } + return nil +} + func init() { proto.RegisterType((*QueryConnectionRequest)(nil), "ibc.core.connection.v1.QueryConnectionRequest") proto.RegisterType((*QueryConnectionResponse)(nil), "ibc.core.connection.v1.QueryConnectionResponse") @@ -628,6 +711,8 @@ func init() { proto.RegisterType((*QueryConnectionClientStateResponse)(nil), "ibc.core.connection.v1.QueryConnectionClientStateResponse") proto.RegisterType((*QueryConnectionConsensusStateRequest)(nil), "ibc.core.connection.v1.QueryConnectionConsensusStateRequest") proto.RegisterType((*QueryConnectionConsensusStateResponse)(nil), "ibc.core.connection.v1.QueryConnectionConsensusStateResponse") + proto.RegisterType((*QueryConnectionParamsRequest)(nil), "ibc.core.connection.v1.QueryConnectionParamsRequest") + proto.RegisterType((*QueryConnectionParamsResponse)(nil), "ibc.core.connection.v1.QueryConnectionParamsResponse") } func init() { @@ -635,63 +720,67 @@ func init() { } var fileDescriptor_cd8d529f8c7cd06b = []byte{ - // 895 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x56, 0x31, 0x6f, 0x23, 0x45, - 0x14, 0xf6, 0x38, 0xb9, 0xd3, 0x65, 0x1c, 0xee, 0x60, 0xe4, 0xbb, 0x33, 0x0b, 0x38, 0x61, 0x8f, - 0x90, 0x1c, 0x70, 0x33, 0xe7, 0x84, 0x3b, 0x1d, 0x21, 0x46, 0xe0, 0x28, 0x90, 0x34, 0x51, 0x58, - 0x24, 0x0a, 0x9a, 0x68, 0x77, 0x3d, 0x59, 0xaf, 0x64, 0xef, 0x38, 0x9e, 0xb5, 0x91, 0x15, 0x59, - 0x48, 0xfc, 0x01, 0x90, 0x68, 0x68, 0x68, 0x29, 0xf8, 0x03, 0x14, 0x74, 0x54, 0x29, 0x23, 0xd1, - 0xa4, 0x8a, 0x90, 0x43, 0x4b, 0xc3, 0x2f, 0x40, 0x3b, 0x33, 0xce, 0xce, 0xda, 0xeb, 0xc4, 0xb1, - 0x2e, 0xdd, 0xe6, 0xcd, 0x7b, 0xf3, 0xbe, 0xef, 0x7b, 0x6f, 0x3e, 0x07, 0x9a, 0xbe, 0xe3, 0x12, - 0x97, 0xb5, 0x28, 0x71, 0x59, 0x10, 0x50, 0x37, 0xf4, 0x59, 0x40, 0x3a, 0x25, 0x72, 0xd8, 0xa6, - 0xad, 0x2e, 0x6e, 0xb6, 0x58, 0xc8, 0xd0, 0x03, 0xdf, 0x71, 0x71, 0x94, 0x83, 0xe3, 0x1c, 0xdc, - 0x29, 0x19, 0x79, 0x8f, 0x79, 0x4c, 0xa4, 0x90, 0xe8, 0x4b, 0x66, 0x1b, 0xef, 0xb9, 0x8c, 0x37, - 0x18, 0x27, 0x8e, 0xcd, 0xa9, 0xbc, 0x86, 0x74, 0x4a, 0x0e, 0x0d, 0xed, 0x12, 0x69, 0xda, 0x9e, - 0x1f, 0xd8, 0xa2, 0x5c, 0xe6, 0x2e, 0xc4, 0xdd, 0xeb, 0x3e, 0x0d, 0xc2, 0xa8, 0xb3, 0xfc, 0x52, - 0x09, 0xcb, 0x63, 0xe0, 0x69, 0x40, 0x64, 0xe2, 0x9b, 0x1e, 0x63, 0x5e, 0x9d, 0x12, 0xbb, 0xe9, - 0x13, 0x3b, 0x08, 0x58, 0x28, 0xda, 0x70, 0x75, 0xfa, 0xba, 0x3a, 0x15, 0x7f, 0x39, 0xed, 0x03, - 0x62, 0x07, 0x8a, 0x9c, 0x59, 0x86, 0x0f, 0xbe, 0x8c, 0x40, 0x6e, 0x5e, 0xdc, 0x68, 0xd1, 0xc3, - 0x36, 0xe5, 0x21, 0x7a, 0x04, 0x5f, 0x89, 0xdb, 0xec, 0xfb, 0xd5, 0x02, 0x58, 0x04, 0x2b, 0x73, - 0xd6, 0x7c, 0x1c, 0xdc, 0xa9, 0x9a, 0x7f, 0x00, 0xf8, 0x70, 0xa4, 0x9e, 0x37, 0x59, 0xc0, 0x29, - 0xda, 0x82, 0x30, 0xce, 0x15, 0xd5, 0xb9, 0xd5, 0x25, 0x9c, 0x2e, 0x26, 0x8e, 0xeb, 0xb7, 0x82, - 0xaa, 0xa5, 0x15, 0xa2, 0x3c, 0xbc, 0xd5, 0x6c, 0x31, 0x76, 0x50, 0xc8, 0x2e, 0x82, 0x95, 0x79, - 0x4b, 0xfe, 0x81, 0x36, 0xe1, 0xbc, 0xf8, 0xd8, 0xaf, 0x51, 0xdf, 0xab, 0x85, 0x85, 0x19, 0x71, - 0xbd, 0xa1, 0x5d, 0x2f, 0x75, 0xec, 0x94, 0xf0, 0xb6, 0xc8, 0xa8, 0xcc, 0x1e, 0x9f, 0x2d, 0x64, - 0xac, 0x9c, 0xa8, 0x92, 0x21, 0xd3, 0x1e, 0x01, 0xcf, 0x07, 0xec, 0x3f, 0x87, 0x30, 0x1e, 0x97, - 0x02, 0xff, 0x2e, 0x96, 0xb3, 0xc5, 0xd1, 0x6c, 0xb1, 0x5c, 0x11, 0x35, 0x5b, 0xbc, 0x67, 0x7b, - 0x54, 0xd5, 0x5a, 0x5a, 0xa5, 0xf9, 0x2f, 0x80, 0x85, 0xd1, 0x1e, 0x4a, 0xa1, 0x5d, 0x98, 0x8b, - 0x89, 0xf2, 0x02, 0x58, 0x9c, 0x59, 0xc9, 0xad, 0x7e, 0x30, 0x4e, 0xa2, 0x9d, 0x2a, 0x0d, 0x42, - 0xff, 0xc0, 0xa7, 0x55, 0x4d, 0x6c, 0xfd, 0x02, 0xf4, 0x45, 0x02, 0x74, 0x56, 0x80, 0x5e, 0xbe, - 0x12, 0xb4, 0x04, 0xa3, 0xa3, 0x46, 0x2f, 0xe0, 0xed, 0x6b, 0xea, 0xaa, 0xf2, 0xcd, 0x0d, 0xf8, - 0x96, 0xa4, 0x2b, 0xd2, 0x52, 0x84, 0x7d, 0x03, 0xce, 0xc9, 0x2b, 0xe2, 0x95, 0xba, 0x23, 0x03, - 0x3b, 0x55, 0xf3, 0x57, 0x00, 0x8b, 0xe3, 0xca, 0x95, 0x66, 0x8f, 0xe1, 0xab, 0xda, 0x5a, 0x36, - 0xed, 0xb0, 0x26, 0x85, 0x9b, 0xb3, 0xee, 0xc5, 0xf1, 0xbd, 0x28, 0x7c, 0x93, 0x9b, 0xe3, 0xc0, - 0xb7, 0x87, 0xa6, 0x2a, 0x11, 0x7f, 0x15, 0xda, 0xe1, 0x60, 0x0f, 0x50, 0x39, 0xf5, 0x05, 0x55, - 0x0a, 0xff, 0x9d, 0x2d, 0xe4, 0xbb, 0x76, 0xa3, 0xbe, 0x6e, 0x26, 0x8e, 0xcd, 0xa1, 0xb7, 0xd5, - 0x07, 0xd0, 0xbc, 0xac, 0x89, 0x12, 0xc4, 0x86, 0x0f, 0xfd, 0x8b, 0xcd, 0xd8, 0x57, 0xda, 0xf2, - 0x28, 0x45, 0xad, 0xed, 0xe3, 0x34, 0x6a, 0xda, 0x32, 0x69, 0x77, 0xde, 0xf7, 0xd3, 0xc2, 0x37, - 0x29, 0xe4, 0xef, 0x00, 0xbe, 0x33, 0x4c, 0x32, 0xa2, 0x15, 0xf0, 0x36, 0x7f, 0x89, 0x62, 0xa2, - 0x65, 0x78, 0xaf, 0x45, 0x3b, 0x3e, 0x8f, 0x4e, 0x83, 0x76, 0xc3, 0xa1, 0x2d, 0x41, 0x66, 0xd6, - 0xba, 0x3b, 0x08, 0xef, 0x8a, 0x68, 0x22, 0x51, 0x23, 0xa6, 0x25, 0x2a, 0xe4, 0x67, 0x00, 0x2e, - 0x5d, 0x81, 0x5c, 0x4d, 0xa8, 0x0c, 0xa3, 0xd5, 0x94, 0x27, 0x89, 0xc9, 0xe4, 0xb1, 0x34, 0x66, - 0x3c, 0x30, 0x66, 0xfc, 0x59, 0xd0, 0xb5, 0xee, 0xba, 0x89, 0x6b, 0x92, 0x2f, 0x26, 0x9b, 0x7c, - 0x31, 0xf1, 0x68, 0x66, 0x2e, 0x1b, 0xcd, 0xec, 0x14, 0xa3, 0x59, 0xfd, 0xe1, 0x0e, 0xbc, 0x25, - 0x08, 0xa2, 0xdf, 0x00, 0x84, 0x31, 0x4b, 0x84, 0xc7, 0x39, 0x54, 0xfa, 0x2f, 0x89, 0x41, 0x26, - 0xce, 0x97, 0x82, 0x99, 0x1f, 0x7f, 0xff, 0xd7, 0x3f, 0x3f, 0x65, 0x9f, 0xa1, 0x35, 0x72, 0xe5, - 0xef, 0x1f, 0x27, 0x47, 0x89, 0xb9, 0xf7, 0xd0, 0x2f, 0x00, 0xe6, 0x34, 0xe3, 0x40, 0x93, 0x76, - 0x1f, 0x38, 0x94, 0xf1, 0x74, 0xf2, 0x02, 0x85, 0xf7, 0x7d, 0x81, 0x77, 0x09, 0x3d, 0x9a, 0x00, - 0x2f, 0xfa, 0x13, 0xc0, 0xd7, 0x46, 0xec, 0x0d, 0x3d, 0xbb, 0xbc, 0xe9, 0x18, 0x37, 0x35, 0x9e, - 0x5f, 0xb7, 0x4c, 0x21, 0xfe, 0x44, 0x20, 0x7e, 0x81, 0x9e, 0x8f, 0x45, 0x2c, 0x37, 0x2e, 0x29, - 0xf4, 0x60, 0x0b, 0x7b, 0xe8, 0x14, 0xc0, 0xfb, 0xa9, 0xb6, 0x84, 0x3e, 0x9a, 0x50, 0xbd, 0x51, - 0xbf, 0x34, 0xd6, 0xa7, 0x29, 0x55, 0x84, 0xb6, 0x05, 0xa1, 0x0a, 0xfa, 0x74, 0x8a, 0x95, 0x21, - 0xba, 0x69, 0xa2, 0x9f, 0xb3, 0xb0, 0x30, 0xee, 0x49, 0xa3, 0x8d, 0x49, 0x21, 0xa6, 0x79, 0x98, - 0x51, 0x9e, 0xb2, 0x5a, 0x71, 0xfc, 0x4e, 0x70, 0xec, 0xa2, 0x6f, 0xa7, 0xe2, 0x98, 0x74, 0x20, - 0x32, 0x70, 0x33, 0x72, 0x34, 0xe4, 0x8b, 0x3d, 0x22, 0x4d, 0x43, 0x3b, 0x90, 0x81, 0x5e, 0xe5, - 0xeb, 0xe3, 0x7e, 0x11, 0x9c, 0xf4, 0x8b, 0xe0, 0xef, 0x7e, 0x11, 0xfc, 0x78, 0x5e, 0xcc, 0x9c, - 0x9c, 0x17, 0x33, 0xa7, 0xe7, 0xc5, 0xcc, 0x37, 0x1b, 0x9e, 0x1f, 0xd6, 0xda, 0x0e, 0x76, 0x59, - 0x83, 0xa8, 0x7f, 0x80, 0x7d, 0xc7, 0x7d, 0xe2, 0x31, 0xd2, 0xf9, 0x90, 0x34, 0x58, 0xb5, 0x5d, - 0xa7, 0x5c, 0x22, 0x7e, 0xba, 0xf6, 0x44, 0x03, 0x1d, 0x76, 0x9b, 0x94, 0x3b, 0xb7, 0x85, 0xff, - 0xad, 0xfd, 0x1f, 0x00, 0x00, 0xff, 0xff, 0x76, 0x45, 0xa5, 0xf9, 0x8e, 0x0b, 0x00, 0x00, + // 958 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x57, 0x41, 0x6f, 0xdc, 0x44, + 0x14, 0xce, 0x6c, 0xd2, 0x88, 0xbc, 0x0d, 0x6d, 0x19, 0xa5, 0xed, 0x62, 0x8a, 0x13, 0x5c, 0xd2, + 0xa4, 0x40, 0x3d, 0xdd, 0x84, 0x84, 0x52, 0x12, 0x04, 0xa9, 0x0a, 0xcd, 0xa5, 0x0a, 0x46, 0x02, + 0x89, 0x4b, 0x64, 0x7b, 0x27, 0x8e, 0xa5, 0xac, 0x67, 0xbb, 0xe3, 0x5d, 0xb4, 0xaa, 0x22, 0x24, + 0x7e, 0x01, 0x12, 0x17, 0x2e, 0x5c, 0x41, 0xe2, 0x0f, 0x20, 0xc4, 0x8d, 0x53, 0x8f, 0x95, 0xb8, + 0xf4, 0x14, 0xa1, 0x0d, 0x57, 0x2e, 0xfc, 0x02, 0xe4, 0x99, 0x71, 0x3d, 0xde, 0xb5, 0x93, 0xcd, + 0x8a, 0xdc, 0x36, 0x6f, 0xde, 0x7b, 0xf3, 0x7d, 0xdf, 0x7b, 0xf3, 0x59, 0x01, 0x2b, 0xf4, 0x7c, + 0xe2, 0xb3, 0x36, 0x25, 0x3e, 0x8b, 0x22, 0xea, 0xc7, 0x21, 0x8b, 0x48, 0xb7, 0x4e, 0x1e, 0x77, + 0x68, 0xbb, 0x67, 0xb7, 0xda, 0x2c, 0x66, 0xf8, 0x6a, 0xe8, 0xf9, 0x76, 0x92, 0x63, 0x67, 0x39, + 0x76, 0xb7, 0x6e, 0xcc, 0x05, 0x2c, 0x60, 0x22, 0x85, 0x24, 0xbf, 0x64, 0xb6, 0xf1, 0x96, 0xcf, + 0x78, 0x93, 0x71, 0xe2, 0xb9, 0x9c, 0xca, 0x36, 0xa4, 0x5b, 0xf7, 0x68, 0xec, 0xd6, 0x49, 0xcb, + 0x0d, 0xc2, 0xc8, 0x15, 0xe5, 0x32, 0x77, 0x3e, 0xbb, 0xfd, 0x20, 0xa4, 0x51, 0x9c, 0xdc, 0x2c, + 0x7f, 0xa9, 0x84, 0xa5, 0x12, 0x78, 0x1a, 0x10, 0x99, 0x78, 0x3d, 0x60, 0x2c, 0x38, 0xa0, 0xc4, + 0x6d, 0x85, 0xc4, 0x8d, 0x22, 0x16, 0x8b, 0x6b, 0xb8, 0x3a, 0x7d, 0x55, 0x9d, 0x8a, 0xbf, 0xbc, + 0xce, 0x1e, 0x71, 0x23, 0x45, 0xce, 0xda, 0x84, 0xab, 0x9f, 0x25, 0x20, 0xef, 0xbf, 0xe8, 0xe8, + 0xd0, 0xc7, 0x1d, 0xca, 0x63, 0x7c, 0x03, 0x5e, 0xce, 0xae, 0xd9, 0x0d, 0x1b, 0x35, 0xb4, 0x80, + 0x96, 0x67, 0x9c, 0xd9, 0x2c, 0xb8, 0xdd, 0xb0, 0x7e, 0x47, 0x70, 0x6d, 0xa8, 0x9e, 0xb7, 0x58, + 0xc4, 0x29, 0x7e, 0x00, 0x90, 0xe5, 0x8a, 0xea, 0xea, 0xca, 0xa2, 0x5d, 0x2c, 0xa6, 0x9d, 0xd5, + 0x3f, 0x88, 0x1a, 0x8e, 0x56, 0x88, 0xe7, 0xe0, 0x42, 0xab, 0xcd, 0xd8, 0x5e, 0xad, 0xb2, 0x80, + 0x96, 0x67, 0x1d, 0xf9, 0x07, 0xbe, 0x0f, 0xb3, 0xe2, 0xc7, 0xee, 0x3e, 0x0d, 0x83, 0xfd, 0xb8, + 0x36, 0x29, 0xda, 0x1b, 0x5a, 0x7b, 0xa9, 0x63, 0xb7, 0x6e, 0x3f, 0x14, 0x19, 0x5b, 0x53, 0x4f, + 0x8f, 0xe6, 0x27, 0x9c, 0xaa, 0xa8, 0x92, 0x21, 0xcb, 0x1d, 0x02, 0xcf, 0x53, 0xf6, 0x9f, 0x00, + 0x64, 0xe3, 0x52, 0xe0, 0x6f, 0xda, 0x72, 0xb6, 0x76, 0x32, 0x5b, 0x5b, 0xae, 0x88, 0x9a, 0xad, + 0xbd, 0xe3, 0x06, 0x54, 0xd5, 0x3a, 0x5a, 0xa5, 0xf5, 0x0f, 0x82, 0xda, 0xf0, 0x1d, 0x4a, 0xa1, + 0x47, 0x50, 0xcd, 0x88, 0xf2, 0x1a, 0x5a, 0x98, 0x5c, 0xae, 0xae, 0xbc, 0x53, 0x26, 0xd1, 0x76, + 0x83, 0x46, 0x71, 0xb8, 0x17, 0xd2, 0x86, 0x26, 0xb6, 0xde, 0x00, 0x7f, 0x9a, 0x03, 0x5d, 0x11, + 0xa0, 0x97, 0x4e, 0x05, 0x2d, 0xc1, 0xe8, 0xa8, 0xf1, 0x5d, 0x98, 0x3e, 0xa3, 0xae, 0x2a, 0xdf, + 0xda, 0x80, 0xd7, 0x25, 0x5d, 0x91, 0x56, 0x20, 0xec, 0x6b, 0x30, 0x23, 0x5b, 0x64, 0x2b, 0xf5, + 0x92, 0x0c, 0x6c, 0x37, 0xac, 0x9f, 0x10, 0x98, 0x65, 0xe5, 0x4a, 0xb3, 0x5b, 0x70, 0x59, 0x5b, + 0xcb, 0x96, 0x1b, 0xef, 0x4b, 0xe1, 0x66, 0x9c, 0x4b, 0x59, 0x7c, 0x27, 0x09, 0x9f, 0xe7, 0xe6, + 0x78, 0xf0, 0xc6, 0xc0, 0x54, 0x25, 0xe2, 0xcf, 0x63, 0x37, 0x4e, 0xf7, 0x00, 0x6f, 0x16, 0xbe, + 0xa0, 0xad, 0xda, 0xbf, 0x47, 0xf3, 0x73, 0x3d, 0xb7, 0x79, 0x70, 0xcf, 0xca, 0x1d, 0x5b, 0x03, + 0x6f, 0xab, 0x8f, 0xc0, 0x3a, 0xe9, 0x12, 0x25, 0x88, 0x0b, 0xd7, 0xc2, 0x17, 0x9b, 0xb1, 0xab, + 0xb4, 0xe5, 0x49, 0x8a, 0x5a, 0xdb, 0x5b, 0x45, 0xd4, 0xb4, 0x65, 0xd2, 0x7a, 0x5e, 0x09, 0x8b, + 0xc2, 0xe7, 0x29, 0xe4, 0xaf, 0x08, 0xde, 0x1c, 0x24, 0x99, 0xd0, 0x8a, 0x78, 0x87, 0xff, 0x8f, + 0x62, 0xe2, 0x25, 0xb8, 0xd4, 0xa6, 0xdd, 0x90, 0x27, 0xa7, 0x51, 0xa7, 0xe9, 0xd1, 0xb6, 0x20, + 0x33, 0xe5, 0x5c, 0x4c, 0xc3, 0x8f, 0x44, 0x34, 0x97, 0xa8, 0x11, 0xd3, 0x12, 0x15, 0xf2, 0x23, + 0x04, 0x8b, 0xa7, 0x20, 0x57, 0x13, 0xda, 0x84, 0x64, 0x35, 0xe5, 0x49, 0x6e, 0x32, 0x73, 0xb6, + 0x34, 0x66, 0x3b, 0x35, 0x66, 0xfb, 0xe3, 0xa8, 0xe7, 0x5c, 0xf4, 0x73, 0x6d, 0xf2, 0x2f, 0xa6, + 0x92, 0x7f, 0x31, 0xd9, 0x68, 0x26, 0x4f, 0x1a, 0xcd, 0xd4, 0x38, 0xa3, 0x31, 0xe1, 0xfa, 0x00, + 0xbf, 0x1d, 0xb7, 0xed, 0x36, 0xd3, 0x97, 0x6c, 0x7d, 0x99, 0x3e, 0xf5, 0xa1, 0x73, 0xc5, 0x7b, + 0x1d, 0xa6, 0x5b, 0x22, 0xa2, 0xe8, 0x9a, 0x65, 0xce, 0xa6, 0xea, 0x54, 0xf6, 0xca, 0x6f, 0x33, + 0x70, 0x41, 0x74, 0xc6, 0xbf, 0x20, 0x80, 0xac, 0x3d, 0xb6, 0xcb, 0x1a, 0x14, 0x7f, 0xc2, 0x0c, + 0x32, 0x72, 0xbe, 0x44, 0x6c, 0x7d, 0xf0, 0xed, 0x9f, 0x7f, 0x7f, 0x5f, 0x59, 0xc3, 0xab, 0xe4, + 0xd4, 0x0f, 0x2f, 0x27, 0x4f, 0x72, 0x0b, 0x77, 0x88, 0x7f, 0x44, 0x50, 0xd5, 0x1c, 0x0b, 0x8f, + 0x7a, 0x7b, 0x2a, 0xa8, 0x71, 0x67, 0xf4, 0x02, 0x85, 0xf7, 0x6d, 0x81, 0x77, 0x11, 0xdf, 0x18, + 0x01, 0x2f, 0xfe, 0x03, 0xc1, 0x2b, 0x43, 0xbe, 0x8a, 0xd7, 0x4e, 0xbe, 0xb4, 0xc4, 0xc6, 0x8d, + 0xf5, 0xb3, 0x96, 0x29, 0xc4, 0x1f, 0x0a, 0xc4, 0x77, 0xf1, 0x7a, 0x29, 0x62, 0xb9, 0xea, 0x79, + 0xa1, 0xd3, 0xf5, 0x3f, 0xc4, 0xcf, 0x11, 0x5c, 0x29, 0xf4, 0x43, 0xfc, 0xfe, 0x88, 0xea, 0x0d, + 0x1b, 0xb5, 0x71, 0x6f, 0x9c, 0x52, 0x45, 0xe8, 0xa1, 0x20, 0xb4, 0x85, 0x3f, 0x1a, 0x63, 0x65, + 0x88, 0xee, 0xd6, 0xf8, 0x87, 0x0a, 0xd4, 0xca, 0xbc, 0x04, 0x6f, 0x8c, 0x0a, 0xb1, 0xc8, 0x3c, + 0x8d, 0xcd, 0x31, 0xab, 0x15, 0xc7, 0x6f, 0x04, 0xc7, 0x1e, 0xfe, 0x7a, 0x2c, 0x8e, 0x79, 0xeb, + 0x23, 0xa9, 0x8d, 0x92, 0x27, 0x03, 0x86, 0x7c, 0x48, 0xa4, 0x5b, 0x69, 0x07, 0x32, 0x70, 0x88, + 0x7f, 0x46, 0x70, 0x79, 0xd0, 0x66, 0xf0, 0xbb, 0x23, 0x92, 0xca, 0xb9, 0x96, 0xb1, 0x76, 0xc6, + 0x2a, 0x25, 0xc1, 0x4d, 0x21, 0xc1, 0x02, 0x36, 0xcb, 0x24, 0x90, 0xde, 0xb5, 0xf5, 0xc5, 0xd3, + 0xbe, 0x89, 0x9e, 0xf5, 0x4d, 0xf4, 0x57, 0xdf, 0x44, 0xdf, 0x1d, 0x9b, 0x13, 0xcf, 0x8e, 0xcd, + 0x89, 0xe7, 0xc7, 0xe6, 0xc4, 0x57, 0x1b, 0x41, 0x18, 0xef, 0x77, 0x3c, 0xdb, 0x67, 0x4d, 0xa2, + 0xfe, 0x47, 0x08, 0x3d, 0xff, 0x76, 0xc0, 0x48, 0xf7, 0x3d, 0xd2, 0x64, 0x8d, 0xce, 0x01, 0xe5, + 0xb2, 0xf1, 0x9d, 0xd5, 0xdb, 0x5a, 0xef, 0xb8, 0xd7, 0xa2, 0xdc, 0x9b, 0x16, 0x9f, 0x88, 0xd5, + 0xff, 0x02, 0x00, 0x00, 0xff, 0xff, 0x8e, 0x40, 0x19, 0x02, 0xb1, 0x0c, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -719,6 +808,8 @@ type QueryClient interface { // ConnectionConsensusState queries the consensus state associated with the // connection. ConnectionConsensusState(ctx context.Context, in *QueryConnectionConsensusStateRequest, opts ...grpc.CallOption) (*QueryConnectionConsensusStateResponse, error) + // ConnectionParams queries all parameters of the ibc connection submodule. + ConnectionParams(ctx context.Context, in *QueryConnectionParamsRequest, opts ...grpc.CallOption) (*QueryConnectionParamsResponse, error) } type queryClient struct { @@ -774,6 +865,15 @@ func (c *queryClient) ConnectionConsensusState(ctx context.Context, in *QueryCon return out, nil } +func (c *queryClient) ConnectionParams(ctx context.Context, in *QueryConnectionParamsRequest, opts ...grpc.CallOption) (*QueryConnectionParamsResponse, error) { + out := new(QueryConnectionParamsResponse) + err := c.cc.Invoke(ctx, "/ibc.core.connection.v1.Query/ConnectionParams", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // QueryServer is the server API for Query service. type QueryServer interface { // Connection queries an IBC connection end. @@ -789,6 +889,8 @@ type QueryServer interface { // ConnectionConsensusState queries the consensus state associated with the // connection. ConnectionConsensusState(context.Context, *QueryConnectionConsensusStateRequest) (*QueryConnectionConsensusStateResponse, error) + // ConnectionParams queries all parameters of the ibc connection submodule. + ConnectionParams(context.Context, *QueryConnectionParamsRequest) (*QueryConnectionParamsResponse, error) } // UnimplementedQueryServer can be embedded to have forward compatible implementations. @@ -810,6 +912,9 @@ func (*UnimplementedQueryServer) ConnectionClientState(ctx context.Context, req func (*UnimplementedQueryServer) ConnectionConsensusState(ctx context.Context, req *QueryConnectionConsensusStateRequest) (*QueryConnectionConsensusStateResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method ConnectionConsensusState not implemented") } +func (*UnimplementedQueryServer) ConnectionParams(ctx context.Context, req *QueryConnectionParamsRequest) (*QueryConnectionParamsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ConnectionParams not implemented") +} func RegisterQueryServer(s grpc1.Server, srv QueryServer) { s.RegisterService(&_Query_serviceDesc, srv) @@ -905,6 +1010,24 @@ func _Query_ConnectionConsensusState_Handler(srv interface{}, ctx context.Contex return interceptor(ctx, in, info, handler) } +func _Query_ConnectionParams_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryConnectionParamsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).ConnectionParams(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ibc.core.connection.v1.Query/ConnectionParams", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).ConnectionParams(ctx, req.(*QueryConnectionParamsRequest)) + } + return interceptor(ctx, in, info, handler) +} + var _Query_serviceDesc = grpc.ServiceDesc{ ServiceName: "ibc.core.connection.v1.Query", HandlerType: (*QueryServer)(nil), @@ -929,6 +1052,10 @@ var _Query_serviceDesc = grpc.ServiceDesc{ MethodName: "ConnectionConsensusState", Handler: _Query_ConnectionConsensusState_Handler, }, + { + MethodName: "ConnectionParams", + Handler: _Query_ConnectionParams_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "ibc/core/connection/v1/query.proto", @@ -1370,6 +1497,64 @@ func (m *QueryConnectionConsensusStateResponse) MarshalToSizedBuffer(dAtA []byte return len(dAtA) - i, nil } +func (m *QueryConnectionParamsRequest) 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 *QueryConnectionParamsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryConnectionParamsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *QueryConnectionParamsResponse) 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 *QueryConnectionParamsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryConnectionParamsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Params != nil { + { + size, err := m.Params.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func encodeVarintQuery(dAtA []byte, offset int, v uint64) int { offset -= sovQuery(v) base := offset @@ -1555,6 +1740,28 @@ func (m *QueryConnectionConsensusStateResponse) Size() (n int) { return n } +func (m *QueryConnectionParamsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *QueryConnectionParamsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Params != nil { + l = m.Params.Size() + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + func sovQuery(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -2806,6 +3013,142 @@ func (m *QueryConnectionConsensusStateResponse) Unmarshal(dAtA []byte) error { } return nil } +func (m *QueryConnectionParamsRequest) 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: QueryConnectionParamsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryConnectionParamsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + 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 *QueryConnectionParamsResponse) 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: QueryConnectionParamsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryConnectionParamsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Params", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Params == nil { + m.Params = &Params{} + } + if err := m.Params.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + 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 skipQuery(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/modules/core/03-connection/types/query.pb.gw.go b/modules/core/03-connection/types/query.pb.gw.go index 2de52353c78..71acc0e9997 100644 --- a/modules/core/03-connection/types/query.pb.gw.go +++ b/modules/core/03-connection/types/query.pb.gw.go @@ -20,6 +20,7 @@ import ( "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/grpclog" + "google.golang.org/grpc/metadata" "google.golang.org/grpc/status" ) @@ -30,6 +31,7 @@ var _ status.Status var _ = runtime.String var _ = utilities.NewDoubleArray var _ = descriptor.ForMessage +var _ = metadata.Join func request_Query_Connection_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq QueryConnectionRequest @@ -327,15 +329,35 @@ func local_request_Query_ConnectionConsensusState_0(ctx context.Context, marshal } +func request_Query_ConnectionParams_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryConnectionParamsRequest + var metadata runtime.ServerMetadata + + msg, err := client.ConnectionParams(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_ConnectionParams_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryConnectionParamsRequest + var metadata runtime.ServerMetadata + + msg, err := server.ConnectionParams(ctx, &protoReq) + return msg, metadata, err + +} + // RegisterQueryHandlerServer registers the http handlers for service Query to "mux". // UnaryRPC :call QueryServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. -// Note that using this registration option will cause many gRPC library features (such as grpc.SendHeader, etc) to stop working. Consider using RegisterQueryHandlerFromEndpoint instead. +// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterQueryHandlerFromEndpoint instead. func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, server QueryServer) error { mux.Handle("GET", pattern_Query_Connection_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { @@ -343,6 +365,7 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv return } resp, md, err := local_request_Query_Connection_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) @@ -356,6 +379,8 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv mux.Handle("GET", pattern_Query_Connections_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { @@ -363,6 +388,7 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv return } resp, md, err := local_request_Query_Connections_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) @@ -376,6 +402,8 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv mux.Handle("GET", pattern_Query_ClientConnections_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { @@ -383,6 +411,7 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv return } resp, md, err := local_request_Query_ClientConnections_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) @@ -396,6 +425,8 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv mux.Handle("GET", pattern_Query_ConnectionClientState_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { @@ -403,6 +434,7 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv return } resp, md, err := local_request_Query_ConnectionClientState_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) @@ -416,6 +448,8 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv mux.Handle("GET", pattern_Query_ConnectionConsensusState_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { @@ -423,6 +457,7 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv return } resp, md, err := local_request_Query_ConnectionConsensusState_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) @@ -433,6 +468,29 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv }) + mux.Handle("GET", pattern_Query_ConnectionParams_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + 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_ConnectionParams_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_ConnectionParams_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -574,19 +632,41 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie }) + mux.Handle("GET", pattern_Query_ConnectionParams_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_ConnectionParams_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_ConnectionParams_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + return nil } var ( - pattern_Query_Connection_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5}, []string{"ibc", "core", "connection", "v1", "connections", "connection_id"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_Connection_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5}, []string{"ibc", "core", "connection", "v1", "connections", "connection_id"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_Connections_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"ibc", "core", "connection", "v1", "connections"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_Connections_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"ibc", "core", "connection", "v1", "connections"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_ClientConnections_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5}, []string{"ibc", "core", "connection", "v1", "client_connections", "client_id"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_ClientConnections_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5}, []string{"ibc", "core", "connection", "v1", "client_connections", "client_id"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_ConnectionClientState_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6}, []string{"ibc", "core", "connection", "v1", "connections", "connection_id", "client_state"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_ConnectionClientState_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6}, []string{"ibc", "core", "connection", "v1", "connections", "connection_id", "client_state"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_ConnectionConsensusState_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 2, 7, 1, 0, 4, 1, 5, 8, 2, 9, 1, 0, 4, 1, 5, 10}, []string{"ibc", "core", "connection", "v1", "connections", "connection_id", "consensus_state", "revision", "revision_number", "height", "revision_height"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_ConnectionConsensusState_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 2, 7, 1, 0, 4, 1, 5, 8, 2, 9, 1, 0, 4, 1, 5, 10}, []string{"ibc", "core", "connection", "v1", "connections", "connection_id", "consensus_state", "revision", "revision_number", "height", "revision_height"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_ConnectionParams_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"ibc", "core", "connection", "v1", "params"}, "", runtime.AssumeColonVerbOpt(false))) ) var ( @@ -599,4 +679,6 @@ var ( forward_Query_ConnectionClientState_0 = runtime.ForwardResponseMessage forward_Query_ConnectionConsensusState_0 = runtime.ForwardResponseMessage + + forward_Query_ConnectionParams_0 = runtime.ForwardResponseMessage ) diff --git a/modules/core/03-connection/types/tx.pb.go b/modules/core/03-connection/types/tx.pb.go index 4862d905716..acfeff2341d 100644 --- a/modules/core/03-connection/types/tx.pb.go +++ b/modules/core/03-connection/types/tx.pb.go @@ -7,10 +7,10 @@ import ( context "context" fmt "fmt" types "github.com/cosmos/cosmos-sdk/codec/types" - types1 "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - _ "github.com/gogo/protobuf/gogoproto" - grpc1 "github.com/gogo/protobuf/grpc" - proto "github.com/gogo/protobuf/proto" + _ "github.com/cosmos/gogoproto/gogoproto" + grpc1 "github.com/cosmos/gogoproto/grpc" + proto "github.com/cosmos/gogoproto/proto" + types1 "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" @@ -131,6 +131,8 @@ type MsgConnectionOpenTry struct { ProofConsensus []byte `protobuf:"bytes,10,opt,name=proof_consensus,json=proofConsensus,proto3" json:"proof_consensus,omitempty" yaml:"proof_consensus"` ConsensusHeight types1.Height `protobuf:"bytes,11,opt,name=consensus_height,json=consensusHeight,proto3" json:"consensus_height" yaml:"consensus_height"` Signer string `protobuf:"bytes,12,opt,name=signer,proto3" json:"signer,omitempty"` + // optional proof data for host state machines that are unable to introspect their own consensus state + HostConsensusStateProof []byte `protobuf:"bytes,13,opt,name=host_consensus_state_proof,json=hostConsensusStateProof,proto3" json:"host_consensus_state_proof,omitempty"` } func (m *MsgConnectionOpenTry) Reset() { *m = MsgConnectionOpenTry{} } @@ -220,6 +222,8 @@ type MsgConnectionOpenAck struct { ProofConsensus []byte `protobuf:"bytes,8,opt,name=proof_consensus,json=proofConsensus,proto3" json:"proof_consensus,omitempty" yaml:"proof_consensus"` ConsensusHeight types1.Height `protobuf:"bytes,9,opt,name=consensus_height,json=consensusHeight,proto3" json:"consensus_height" yaml:"consensus_height"` Signer string `protobuf:"bytes,10,opt,name=signer,proto3" json:"signer,omitempty"` + // optional proof data for host state machines that are unable to introspect their own consensus state + HostConsensusStateProof []byte `protobuf:"bytes,11,opt,name=host_consensus_state_proof,json=hostConsensusStateProof,proto3" json:"host_consensus_state_proof,omitempty"` } func (m *MsgConnectionOpenAck) Reset() { *m = MsgConnectionOpenAck{} } @@ -387,66 +391,68 @@ func init() { func init() { proto.RegisterFile("ibc/core/connection/v1/tx.proto", fileDescriptor_5d00fde5fc97399e) } var fileDescriptor_5d00fde5fc97399e = []byte{ - // 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, 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, + // 965 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x57, 0x31, 0x73, 0xe3, 0x44, + 0x14, 0xb6, 0x62, 0xc7, 0xb1, 0xd7, 0x3e, 0xee, 0x6e, 0x71, 0x12, 0x21, 0xee, 0x2c, 0x23, 0x60, + 0x48, 0x41, 0xa4, 0xf3, 0xdd, 0x31, 0x40, 0x80, 0x22, 0x76, 0x43, 0x8a, 0x83, 0x8c, 0xb8, 0xb9, + 0x19, 0xae, 0xf1, 0xd8, 0xf2, 0x46, 0xd9, 0xb1, 0xad, 0xf5, 0x68, 0x65, 0x83, 0x68, 0x69, 0x18, + 0x2a, 0x7e, 0x01, 0x73, 0xff, 0x81, 0x3f, 0x71, 0x65, 0x0a, 0x0a, 0x2a, 0x0d, 0x93, 0x34, 0xd4, + 0xee, 0xe8, 0x98, 0xdd, 0x95, 0xe4, 0xb5, 0x23, 0x0f, 0x31, 0x0e, 0x9d, 0x9e, 0xde, 0xf7, 0xde, + 0xdb, 0xf7, 0xf6, 0xfb, 0xde, 0x48, 0x40, 0xc7, 0x3d, 0xc7, 0x72, 0x88, 0x8f, 0x2c, 0x87, 0x78, + 0x1e, 0x72, 0x02, 0x4c, 0x3c, 0x6b, 0xda, 0xb4, 0x82, 0xef, 0xcd, 0xb1, 0x4f, 0x02, 0x02, 0xf7, + 0x70, 0xcf, 0x31, 0x19, 0xc0, 0x9c, 0x03, 0xcc, 0x69, 0x53, 0xab, 0xb9, 0xc4, 0x25, 0x1c, 0x62, + 0xb1, 0x27, 0x81, 0xd6, 0xde, 0x72, 0x09, 0x71, 0x87, 0xc8, 0xe2, 0x56, 0x6f, 0x72, 0x66, 0x75, + 0xbd, 0x30, 0x76, 0x49, 0x95, 0x86, 0x18, 0x79, 0x01, 0xab, 0x22, 0x9e, 0x62, 0xc0, 0x07, 0x2b, + 0x8e, 0x22, 0xd5, 0xe5, 0x40, 0xe3, 0xb7, 0x2d, 0xb0, 0xfb, 0x8c, 0xba, 0xed, 0xf4, 0xfd, 0xd7, + 0x63, 0xe4, 0x9d, 0x78, 0x38, 0x80, 0x4d, 0x50, 0x16, 0x29, 0x3b, 0xb8, 0xaf, 0x2a, 0x0d, 0xe5, + 0xa0, 0xdc, 0xaa, 0xcd, 0x22, 0xfd, 0x5e, 0xd8, 0x1d, 0x0d, 0x8f, 0x8c, 0xd4, 0x65, 0xd8, 0x25, + 0xf1, 0x7c, 0xd2, 0x87, 0x5f, 0x81, 0xaa, 0x43, 0x26, 0x5e, 0x80, 0xfc, 0x71, 0xd7, 0x0f, 0x42, + 0x75, 0xab, 0xa1, 0x1c, 0x54, 0x1e, 0xbf, 0x67, 0x66, 0xb7, 0x6d, 0xb6, 0x25, 0x6c, 0xab, 0xf0, + 0x3a, 0xd2, 0x73, 0xf6, 0x42, 0x3c, 0xfc, 0x14, 0xec, 0x4c, 0x91, 0x4f, 0x31, 0xf1, 0xd4, 0x3c, + 0x4f, 0xa5, 0xaf, 0x4a, 0xf5, 0x42, 0xc0, 0xec, 0x04, 0x0f, 0x8f, 0x40, 0xb5, 0x8f, 0x86, 0xdd, + 0xb0, 0x33, 0x46, 0x3e, 0x26, 0x7d, 0xb5, 0xd0, 0x50, 0x0e, 0x0a, 0xad, 0xfd, 0x59, 0xa4, 0xbf, + 0x29, 0x1a, 0x90, 0xbd, 0x86, 0x5d, 0xe1, 0xe6, 0x29, 0xb7, 0xe0, 0x1e, 0x28, 0x52, 0xec, 0x7a, + 0xc8, 0x57, 0xb7, 0x59, 0xdb, 0x76, 0x6c, 0x1d, 0x95, 0x7e, 0x7a, 0xa5, 0xe7, 0xfe, 0x7a, 0xa5, + 0xe7, 0x0c, 0x1d, 0x3c, 0xcc, 0x1c, 0x9a, 0x8d, 0xe8, 0x98, 0x78, 0x14, 0x19, 0xbf, 0xef, 0x80, + 0xda, 0x35, 0xc4, 0x73, 0x3f, 0xfc, 0x2f, 0x53, 0xfd, 0x16, 0xec, 0x8d, 0x7d, 0x34, 0xc5, 0x64, + 0x42, 0x3b, 0xf3, 0xae, 0x59, 0xfc, 0x16, 0x8f, 0x7f, 0x77, 0x16, 0xe9, 0x0f, 0x45, 0x7c, 0x36, + 0xce, 0x50, 0x15, 0xbb, 0x96, 0xb8, 0xe6, 0x47, 0x3a, 0xe9, 0xc3, 0x53, 0x50, 0x8d, 0x4b, 0xd2, + 0xa0, 0x1b, 0xa0, 0x78, 0xca, 0x35, 0x53, 0x30, 0xcf, 0x4c, 0x98, 0x67, 0x1e, 0x7b, 0xa1, 0x3c, + 0x3b, 0x39, 0xc6, 0xb0, 0x2b, 0xc2, 0xfc, 0x86, 0x59, 0xd7, 0x28, 0x50, 0xd8, 0x90, 0x02, 0xcb, + 0xf7, 0xb8, 0xbd, 0xc6, 0x3d, 0x4e, 0xc1, 0xae, 0x9c, 0xab, 0x13, 0x73, 0x83, 0xaa, 0xc5, 0x46, + 0xfe, 0x06, 0x64, 0x6a, 0x35, 0x66, 0x91, 0xfe, 0x20, 0xee, 0x38, 0x2b, 0x8f, 0x61, 0xd7, 0xe4, + 0xf7, 0x71, 0x18, 0x85, 0x2f, 0x41, 0x75, 0xec, 0x13, 0x72, 0xd6, 0x39, 0x47, 0xd8, 0x3d, 0x0f, + 0xd4, 0x1d, 0x3e, 0x03, 0x4d, 0x2a, 0x27, 0xa4, 0x3a, 0x6d, 0x9a, 0x5f, 0x72, 0x44, 0xeb, 0x6d, + 0xd6, 0xf9, 0xbc, 0x27, 0x39, 0xda, 0xb0, 0x2b, 0xdc, 0x14, 0x48, 0xf8, 0x14, 0x00, 0xe1, 0xc5, + 0x1e, 0x0e, 0xd4, 0x52, 0x43, 0x39, 0xa8, 0xb6, 0x76, 0x67, 0x91, 0x7e, 0x5f, 0x8e, 0x64, 0x3e, + 0xc3, 0x2e, 0x73, 0x83, 0x6b, 0xf9, 0x28, 0x39, 0x91, 0xa8, 0xac, 0x96, 0x79, 0xdc, 0xfe, 0x72, + 0x45, 0xe1, 0x4d, 0x2a, 0xb6, 0xb9, 0x05, 0xdb, 0xe0, 0x6e, 0xec, 0x65, 0xcc, 0xf6, 0xe8, 0x84, + 0xaa, 0x80, 0x87, 0x6b, 0xb3, 0x48, 0xdf, 0x5b, 0x08, 0x4f, 0x00, 0x86, 0xfd, 0x86, 0xc8, 0x90, + 0xbc, 0x80, 0x67, 0xe0, 0x5e, 0xea, 0x4d, 0xc6, 0x52, 0xf9, 0xd7, 0xb1, 0xe8, 0xf1, 0x58, 0xf6, + 0x93, 0x4b, 0x58, 0xcc, 0x60, 0xd8, 0x77, 0xd3, 0x57, 0xf1, 0x78, 0xe6, 0xd2, 0xad, 0xca, 0xd2, + 0x85, 0x9f, 0x01, 0xed, 0x9c, 0xd0, 0x60, 0x7e, 0x44, 0x41, 0xde, 0x0e, 0x3f, 0xa6, 0x7a, 0x87, + 0xf5, 0x63, 0xef, 0x33, 0x44, 0x7a, 0x64, 0x4e, 0xe7, 0x53, 0xe6, 0x96, 0x74, 0x5f, 0x07, 0x0f, + 0xb2, 0x54, 0x9d, 0xca, 0xfe, 0xd7, 0x62, 0x86, 0xec, 0x8f, 0x9d, 0x01, 0xfc, 0x02, 0xdc, 0x59, + 0x94, 0xae, 0x90, 0xbe, 0x3a, 0x8b, 0xf4, 0x5a, 0xda, 0x9c, 0xa4, 0x58, 0xa6, 0x02, 0x49, 0xa7, + 0x0e, 0xd0, 0x16, 0x18, 0x98, 0xb5, 0x06, 0xde, 0x9f, 0x45, 0xfa, 0x3b, 0x19, 0x6c, 0x5d, 0x4a, + 0xac, 0xca, 0xce, 0x85, 0x65, 0xb0, 0xc1, 0xb6, 0x5d, 0xde, 0x23, 0x85, 0x8d, 0xf7, 0xc8, 0xb2, + 0x86, 0xb6, 0x6f, 0x51, 0x43, 0x4d, 0x20, 0xa4, 0xd1, 0x09, 0xfc, 0x50, 0x2d, 0x72, 0x2e, 0x4b, + 0x3b, 0x38, 0x75, 0x19, 0x76, 0x89, 0x3f, 0xb3, 0xb5, 0xbd, 0x2c, 0xa0, 0x9d, 0xcd, 0x04, 0x54, + 0xba, 0x15, 0x01, 0x95, 0xff, 0x57, 0x01, 0x81, 0x35, 0x04, 0x54, 0xd9, 0x44, 0x40, 0xc7, 0xce, + 0x20, 0x15, 0xd0, 0xcf, 0x5b, 0x40, 0xbd, 0x06, 0x68, 0x13, 0xef, 0x0c, 0xfb, 0xa3, 0x4d, 0x45, + 0x94, 0x5e, 0x7b, 0xd7, 0x19, 0x70, 0xcd, 0x64, 0x5c, 0x7b, 0xd7, 0x19, 0x24, 0xd7, 0xce, 0x64, + 0xbb, 0xcc, 0xc2, 0xfc, 0x2d, 0xb2, 0x70, 0x3e, 0xe9, 0xc2, 0x8a, 0xaf, 0x0c, 0x03, 0x34, 0x56, + 0xcd, 0x22, 0x19, 0xd8, 0xe3, 0xbf, 0xf3, 0x20, 0xff, 0x8c, 0xba, 0xf0, 0x07, 0x00, 0x33, 0xbe, + 0xe1, 0x0e, 0x57, 0x29, 0x38, 0xf3, 0xeb, 0x45, 0xfb, 0x68, 0x2d, 0x78, 0x72, 0x06, 0xf8, 0x1d, + 0xb8, 0x7f, 0xfd, 0x43, 0xe7, 0xc3, 0x1b, 0xe7, 0x7a, 0xee, 0x87, 0xda, 0xd3, 0x75, 0xd0, 0xab, + 0x0b, 0xb3, 0x3b, 0xbb, 0x79, 0xe1, 0x63, 0x67, 0xb0, 0x46, 0x61, 0x89, 0xa6, 0xf0, 0x47, 0x05, + 0xec, 0x66, 0x73, 0xf4, 0xd1, 0x8d, 0xf3, 0xc5, 0x11, 0xda, 0x27, 0xeb, 0x46, 0x24, 0xa7, 0x68, + 0xbd, 0x78, 0x7d, 0x59, 0x57, 0x2e, 0x2e, 0xeb, 0xca, 0x9f, 0x97, 0x75, 0xe5, 0x97, 0xab, 0x7a, + 0xee, 0xe2, 0xaa, 0x9e, 0xfb, 0xe3, 0xaa, 0x9e, 0x7b, 0xf9, 0xb9, 0x8b, 0x83, 0xf3, 0x49, 0xcf, + 0x74, 0xc8, 0xc8, 0x72, 0x08, 0x1d, 0x11, 0x6a, 0xe1, 0x9e, 0x73, 0xe8, 0x12, 0x6b, 0xfa, 0xb1, + 0x35, 0x22, 0xfd, 0xc9, 0x10, 0x51, 0xf1, 0x7b, 0xf0, 0xe8, 0xc9, 0xa1, 0xf4, 0x87, 0x10, 0x84, + 0x63, 0x44, 0x7b, 0x45, 0xbe, 0xaf, 0x9f, 0xfc, 0x13, 0x00, 0x00, 0xff, 0xff, 0x40, 0xa4, 0x36, + 0x0d, 0xd0, 0x0c, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -754,6 +760,13 @@ func (m *MsgConnectionOpenTry) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if len(m.HostConsensusStateProof) > 0 { + i -= len(m.HostConsensusStateProof) + copy(dAtA[i:], m.HostConsensusStateProof) + i = encodeVarintTx(dAtA, i, uint64(len(m.HostConsensusStateProof))) + i-- + dAtA[i] = 0x6a + } if len(m.Signer) > 0 { i -= len(m.Signer) copy(dAtA[i:], m.Signer) @@ -903,6 +916,13 @@ func (m *MsgConnectionOpenAck) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if len(m.HostConsensusStateProof) > 0 { + i -= len(m.HostConsensusStateProof) + copy(dAtA[i:], m.HostConsensusStateProof) + i = encodeVarintTx(dAtA, i, uint64(len(m.HostConsensusStateProof))) + i-- + dAtA[i] = 0x5a + } if len(m.Signer) > 0 { i -= len(m.Signer) copy(dAtA[i:], m.Signer) @@ -1187,6 +1207,10 @@ func (m *MsgConnectionOpenTry) Size() (n int) { if l > 0 { n += 1 + l + sovTx(uint64(l)) } + l = len(m.HostConsensusStateProof) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } return n } @@ -1241,6 +1265,10 @@ func (m *MsgConnectionOpenAck) Size() (n int) { if l > 0 { n += 1 + l + sovTx(uint64(l)) } + l = len(m.HostConsensusStateProof) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } return n } @@ -1958,6 +1986,40 @@ func (m *MsgConnectionOpenTry) Unmarshal(dAtA []byte) error { } m.Signer = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 13: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field HostConsensusStateProof", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.HostConsensusStateProof = append(m.HostConsensusStateProof[:0], dAtA[iNdEx:postIndex]...) + if m.HostConsensusStateProof == nil { + m.HostConsensusStateProof = []byte{} + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipTx(dAtA[iNdEx:]) @@ -2394,6 +2456,40 @@ func (m *MsgConnectionOpenAck) Unmarshal(dAtA []byte) error { } m.Signer = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 11: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field HostConsensusStateProof", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.HostConsensusStateProof = append(m.HostConsensusStateProof[:0], dAtA[iNdEx:postIndex]...) + if m.HostConsensusStateProof == nil { + m.HostConsensusStateProof = []byte{} + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipTx(dAtA[iNdEx:]) diff --git a/modules/core/03-connection/types/version.go b/modules/core/03-connection/types/version.go index 15ffc49d577..f986236dc53 100644 --- a/modules/core/03-connection/types/version.go +++ b/modules/core/03-connection/types/version.go @@ -3,9 +3,9 @@ package types import ( "strings" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + errorsmod "cosmossdk.io/errors" - "github.com/cosmos/ibc-go/v4/modules/core/exported" + "github.com/cosmos/ibc-go/v7/modules/core/exported" ) var ( @@ -50,14 +50,14 @@ func (version Version) GetFeatures() []string { // features. It unmarshals the version string into a Version object. func ValidateVersion(version *Version) error { if version == nil { - return sdkerrors.Wrap(ErrInvalidVersion, "version cannot be nil") + return errorsmod.Wrap(ErrInvalidVersion, "version cannot be nil") } if strings.TrimSpace(version.Identifier) == "" { - return sdkerrors.Wrap(ErrInvalidVersion, "version identifier cannot be blank") + return errorsmod.Wrap(ErrInvalidVersion, "version identifier cannot be blank") } for i, feature := range version.Features { if strings.TrimSpace(feature) == "" { - return sdkerrors.Wrapf(ErrInvalidVersion, "feature cannot be blank, index %d", i) + return errorsmod.Wrapf(ErrInvalidVersion, "feature cannot be blank, index %d", i) } } @@ -70,14 +70,14 @@ func ValidateVersion(version *Version) error { // identifier. func (version Version) VerifyProposedVersion(proposedVersion exported.Version) error { if proposedVersion.GetIdentifier() != version.GetIdentifier() { - return sdkerrors.Wrapf( + return errorsmod.Wrapf( ErrVersionNegotiationFailed, "proposed version identifier does not equal supported version identifier (%s != %s)", proposedVersion.GetIdentifier(), version.GetIdentifier(), ) } if len(proposedVersion.GetFeatures()) == 0 && !allowNilFeatureSet[proposedVersion.GetIdentifier()] { - return sdkerrors.Wrapf( + return errorsmod.Wrapf( ErrVersionNegotiationFailed, "nil feature sets are not supported for version identifier (%s)", proposedVersion.GetIdentifier(), ) @@ -85,7 +85,7 @@ func (version Version) VerifyProposedVersion(proposedVersion exported.Version) e for _, proposedFeature := range proposedVersion.GetFeatures() { if !contains(proposedFeature, version.GetFeatures()) { - return sdkerrors.Wrapf( + return errorsmod.Wrapf( ErrVersionNegotiationFailed, "proposed feature (%s) is not a supported feature set (%s)", proposedFeature, version.GetFeatures(), ) @@ -166,7 +166,7 @@ func PickVersion(supportedVersions, counterpartyVersions []exported.Version) (*V } } - return nil, sdkerrors.Wrapf( + return nil, errorsmod.Wrapf( ErrVersionNegotiationFailed, "failed to find a matching counterparty version (%v) from the supported version list (%v)", counterpartyVersions, supportedVersions, ) diff --git a/modules/core/03-connection/types/version_test.go b/modules/core/03-connection/types/version_test.go index 2958c55fa38..9aa4abf9259 100644 --- a/modules/core/03-connection/types/version_test.go +++ b/modules/core/03-connection/types/version_test.go @@ -5,9 +5,9 @@ import ( "github.com/stretchr/testify/require" - "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types" - "github.com/cosmos/ibc-go/v4/modules/core/exported" - ibctesting "github.com/cosmos/ibc-go/v4/testing" + "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" + "github.com/cosmos/ibc-go/v7/modules/core/exported" + ibctesting "github.com/cosmos/ibc-go/v7/testing" ) func TestValidateVersion(t *testing.T) { diff --git a/modules/core/04-channel/client/cli/cli.go b/modules/core/04-channel/client/cli/cli.go index 34259b09146..7d74011db41 100644 --- a/modules/core/04-channel/client/cli/cli.go +++ b/modules/core/04-channel/client/cli/cli.go @@ -4,7 +4,7 @@ import ( "github.com/cosmos/cosmos-sdk/client" "github.com/spf13/cobra" - "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" + "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" ) // GetQueryCmd returns the query commands for IBC channels diff --git a/modules/core/04-channel/client/cli/query.go b/modules/core/04-channel/client/cli/query.go index 8fe1937e055..b06b9655eb3 100644 --- a/modules/core/04-channel/client/cli/query.go +++ b/modules/core/04-channel/client/cli/query.go @@ -9,9 +9,9 @@ import ( "github.com/cosmos/cosmos-sdk/version" "github.com/spf13/cobra" - "github.com/cosmos/ibc-go/v4/modules/core/04-channel/client/utils" - "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" + "github.com/cosmos/ibc-go/v7/modules/core/04-channel/client/utils" + "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + ibcexported "github.com/cosmos/ibc-go/v7/modules/core/exported" ) const ( @@ -25,7 +25,7 @@ func GetCmdQueryChannels() *cobra.Command { Use: "channels", Short: "Query all channels", Long: "Query all channels from a chain", - Example: fmt.Sprintf("%s query %s %s channels", version.AppName, host.ModuleName, types.SubModuleName), + Example: fmt.Sprintf("%s query %s %s channels", version.AppName, ibcexported.ModuleName, types.SubModuleName), Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, _ []string) error { clientCtx, err := client.GetClientQueryContext(cmd) @@ -65,7 +65,7 @@ func GetCmdQueryChannel() *cobra.Command { Short: "Query a channel end", Long: "Query an IBC channel end from a port and channel identifiers", Example: fmt.Sprintf( - "%s query %s %s end [port-id] [channel-id]", version.AppName, host.ModuleName, types.SubModuleName, + "%s query %s %s end [port-id] [channel-id]", version.AppName, ibcexported.ModuleName, types.SubModuleName, ), Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { @@ -99,7 +99,7 @@ func GetCmdQueryConnectionChannels() *cobra.Command { Use: "connections [connection-id]", Short: "Query all channels associated with a connection", Long: "Query all channels associated with a connection", - Example: fmt.Sprintf("%s query %s %s connections [connection-id]", version.AppName, host.ModuleName, types.SubModuleName), + Example: fmt.Sprintf("%s query %s %s connections [connection-id]", version.AppName, ibcexported.ModuleName, types.SubModuleName), Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { clientCtx, err := client.GetClientQueryContext(cmd) @@ -169,7 +169,7 @@ func GetCmdQueryPacketCommitments() *cobra.Command { Use: "packet-commitments [port-id] [channel-id]", Short: "Query all packet commitments associated with a channel", Long: "Query all packet commitments associated with a channel", - Example: fmt.Sprintf("%s query %s %s packet-commitments [port-id] [channel-id]", version.AppName, host.ModuleName, types.SubModuleName), + Example: fmt.Sprintf("%s query %s %s packet-commitments [port-id] [channel-id]", version.AppName, ibcexported.ModuleName, types.SubModuleName), Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { clientCtx, err := client.GetClientQueryContext(cmd) @@ -210,7 +210,7 @@ func GetCmdQueryPacketCommitment() *cobra.Command { Short: "Query a packet commitment", Long: "Query a packet commitment", Example: fmt.Sprintf( - "%s query %s %s packet-commitment [port-id] [channel-id] [sequence]", version.AppName, host.ModuleName, types.SubModuleName, + "%s query %s %s packet-commitment [port-id] [channel-id] [sequence]", version.AppName, ibcexported.ModuleName, types.SubModuleName, ), Args: cobra.ExactArgs(3), RunE: func(cmd *cobra.Command, args []string) error { @@ -249,7 +249,7 @@ func GetCmdQueryPacketReceipt() *cobra.Command { Short: "Query a packet receipt", Long: "Query a packet receipt", Example: fmt.Sprintf( - "%s query %s %s packet-receipt [port-id] [channel-id] [sequence]", version.AppName, host.ModuleName, types.SubModuleName, + "%s query %s %s packet-receipt [port-id] [channel-id] [sequence]", version.AppName, ibcexported.ModuleName, types.SubModuleName, ), Args: cobra.ExactArgs(3), RunE: func(cmd *cobra.Command, args []string) error { @@ -288,7 +288,7 @@ func GetCmdQueryPacketAcknowledgement() *cobra.Command { Short: "Query a packet acknowledgement", Long: "Query a packet acknowledgement", Example: fmt.Sprintf( - "%s query %s %s packet-ack [port-id] [channel-id] [sequence]", version.AppName, host.ModuleName, types.SubModuleName, + "%s query %s %s packet-ack [port-id] [channel-id] [sequence]", version.AppName, ibcexported.ModuleName, types.SubModuleName, ), Args: cobra.ExactArgs(3), RunE: func(cmd *cobra.Command, args []string) error { @@ -331,7 +331,7 @@ func GetCmdQueryUnreceivedPackets() *cobra.Command { The return value represents: - Unreceived packet commitments: no acknowledgement exists on receiving chain for the given packet commitment sequence on sending chain. `, - Example: fmt.Sprintf("%s query %s %s unreceived-packets [port-id] [channel-id] --sequences=1,2,3", version.AppName, host.ModuleName, types.SubModuleName), + Example: fmt.Sprintf("%s query %s %s unreceived-packets [port-id] [channel-id] --sequences=1,2,3", version.AppName, ibcexported.ModuleName, types.SubModuleName), Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { clientCtx, err := client.GetClientQueryContext(cmd) @@ -381,7 +381,7 @@ func GetCmdQueryUnreceivedAcks() *cobra.Command { The return value represents: - Unreceived packet acknowledgement: packet commitment exists on original sending (executing) chain and ack exists on receiving chain. `, - Example: fmt.Sprintf("%s query %s %s unreceived-acks [port-id] [channel-id] --sequences=1,2,3", version.AppName, host.ModuleName, types.SubModuleName), + Example: fmt.Sprintf("%s query %s %s unreceived-acks [port-id] [channel-id] --sequences=1,2,3", version.AppName, ibcexported.ModuleName, types.SubModuleName), Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { clientCtx, err := client.GetClientQueryContext(cmd) @@ -428,7 +428,7 @@ func GetCmdQueryNextSequenceReceive() *cobra.Command { Short: "Query a next receive sequence", Long: "Query the next receive sequence for a given channel", Example: fmt.Sprintf( - "%s query %s %s next-sequence-receive [port-id] [channel-id]", version.AppName, host.ModuleName, types.SubModuleName, + "%s query %s %s next-sequence-receive [port-id] [channel-id]", version.AppName, ibcexported.ModuleName, types.SubModuleName, ), Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { diff --git a/modules/core/04-channel/client/utils/utils.go b/modules/core/04-channel/client/utils/utils.go index 577172b598c..be06badf011 100644 --- a/modules/core/04-channel/client/utils/utils.go +++ b/modules/core/04-channel/client/utils/utils.go @@ -4,16 +4,17 @@ import ( "context" "encoding/binary" + errorsmod "cosmossdk.io/errors" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/codec" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - - clientutils "github.com/cosmos/ibc-go/v4/modules/core/02-client/client/utils" - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" - ibcclient "github.com/cosmos/ibc-go/v4/modules/core/client" - "github.com/cosmos/ibc-go/v4/modules/core/exported" + + ibcerrors "github.com/cosmos/ibc-go/v7/internal/errors" + clientutils "github.com/cosmos/ibc-go/v7/modules/core/02-client/client/utils" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + ibcclient "github.com/cosmos/ibc-go/v7/modules/core/client" + "github.com/cosmos/ibc-go/v7/modules/core/exported" ) // QueryChannel returns a channel end. @@ -45,7 +46,7 @@ func queryChannelABCI(clientCtx client.Context, portID, channelID string) (*type // check if channel exists if len(value) == 0 { - return nil, sdkerrors.Wrapf(types.ErrChannelNotFound, "portID (%s), channelID (%s)", portID, channelID) + return nil, errorsmod.Wrapf(types.ErrChannelNotFound, "portID (%s), channelID (%s)", portID, channelID) } cdc := codec.NewProtoCodec(clientCtx.InterfaceRegistry) @@ -140,7 +141,7 @@ func QueryLatestConsensusState( clientHeight, ok := clientState.GetLatestHeight().(clienttypes.Height) if !ok { - return nil, clienttypes.Height{}, clienttypes.Height{}, sdkerrors.Wrapf(sdkerrors.ErrInvalidHeight, "invalid height type. expected type: %T, got: %T", + return nil, clienttypes.Height{}, clienttypes.Height{}, errorsmod.Wrapf(ibcerrors.ErrInvalidHeight, "invalid height type. expected type: %T, got: %T", clienttypes.Height{}, clientHeight) } res, err := QueryChannelConsensusState(clientCtx, portID, channelID, clientHeight, false) @@ -185,7 +186,7 @@ func queryNextSequenceRecvABCI(clientCtx client.Context, portID, channelID strin // check if next sequence receive exists if len(value) == 0 { - return nil, sdkerrors.Wrapf(types.ErrChannelNotFound, "portID (%s), channelID (%s)", portID, channelID) + return nil, errorsmod.Wrapf(types.ErrChannelNotFound, "portID (%s), channelID (%s)", portID, channelID) } sequence := binary.BigEndian.Uint64(value) @@ -226,7 +227,7 @@ func queryPacketCommitmentABCI( // check if packet commitment exists if len(value) == 0 { - return nil, sdkerrors.Wrapf(types.ErrPacketCommitmentNotFound, "portID (%s), channelID (%s), sequence (%d)", portID, channelID, sequence) + return nil, errorsmod.Wrapf(types.ErrPacketCommitmentNotFound, "portID (%s), channelID (%s), sequence (%d)", portID, channelID, sequence) } return types.NewQueryPacketCommitmentResponse(value, proofBz, proofHeight), nil @@ -293,7 +294,7 @@ func queryPacketAcknowledgementABCI(clientCtx client.Context, portID, channelID } if len(value) == 0 { - return nil, sdkerrors.Wrapf(types.ErrInvalidAcknowledgement, "portID (%s), channelID (%s), sequence (%d)", portID, channelID, sequence) + return nil, errorsmod.Wrapf(types.ErrInvalidAcknowledgement, "portID (%s), channelID (%s), sequence (%d)", portID, channelID, sequence) } return types.NewQueryPacketAcknowledgementResponse(value, proofBz, proofHeight), nil diff --git a/modules/core/04-channel/doc.go b/modules/core/04-channel/doc.go new file mode 100644 index 00000000000..f5c537df089 --- /dev/null +++ b/modules/core/04-channel/doc.go @@ -0,0 +1,17 @@ +/* +Package channel implements the ICS 04 - Channel and Packet Semantics specification +(https://github.com/cosmos/ibc/tree/main/spec/core/ics-004-channel-and-packet-semantics). This +concrete implementation defines types and methods for safely creating two +stateful objects (channel ends) on two separate chains, each associated with a +particular connection. A channel serves as a conduit for packets passing between a +module on one chain and a module on another, ensuring that packets are executed +only once, delivered in the order in which they were sent (if necessary), +and delivered only to the corresponding module owning the other end of the +channel on the destination chain. + +The main types are Channel, which defines a stateful object on a +chain that allows for exactly-once packet delivery between specific modules +on separate blockchains, and Packet, which defines the data carried +across different chains through IBC. +*/ +package channel diff --git a/modules/core/04-channel/genesis.go b/modules/core/04-channel/genesis.go index 0d402fd8f39..91bd5b31767 100644 --- a/modules/core/04-channel/genesis.go +++ b/modules/core/04-channel/genesis.go @@ -3,8 +3,8 @@ package channel import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/ibc-go/v4/modules/core/04-channel/keeper" - "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" + "github.com/cosmos/ibc-go/v7/modules/core/04-channel/keeper" + "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" ) // InitGenesis initializes the ibc channel submodule's state from a provided genesis diff --git a/modules/core/04-channel/keeper/events.go b/modules/core/04-channel/keeper/events.go index bd014a67979..81304721b74 100644 --- a/modules/core/04-channel/keeper/events.go +++ b/modules/core/04-channel/keeper/events.go @@ -6,12 +6,12 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" - "github.com/cosmos/ibc-go/v4/modules/core/exported" + "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + "github.com/cosmos/ibc-go/v7/modules/core/exported" ) -// EmitChannelOpenInitEvent emits a channel open init event -func EmitChannelOpenInitEvent(ctx sdk.Context, portID string, channelID string, channel types.Channel) { +// emitChannelOpenInitEvent emits a channel open init event +func emitChannelOpenInitEvent(ctx sdk.Context, portID string, channelID string, channel types.Channel) { ctx.EventManager().EmitEvents(sdk.Events{ sdk.NewEvent( types.EventTypeChannelOpenInit, @@ -22,9 +22,6 @@ func EmitChannelOpenInitEvent(ctx sdk.Context, portID string, channelID string, sdk.NewAttribute(types.AttributeKeyConnectionID, channel.ConnectionHops[0]), sdk.NewAttribute(types.AttributeVersion, channel.Version), ), - }) - - ctx.EventManager().EmitEvents(sdk.Events{ sdk.NewEvent( sdk.EventTypeMessage, sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), @@ -32,8 +29,8 @@ func EmitChannelOpenInitEvent(ctx sdk.Context, portID string, channelID string, }) } -// EmitChannelOpenTryEvent emits a channel open try event -func EmitChannelOpenTryEvent(ctx sdk.Context, portID string, channelID string, channel types.Channel) { +// emitChannelOpenTryEvent emits a channel open try event +func emitChannelOpenTryEvent(ctx sdk.Context, portID string, channelID string, channel types.Channel) { ctx.EventManager().EmitEvents(sdk.Events{ sdk.NewEvent( types.EventTypeChannelOpenTry, @@ -44,8 +41,6 @@ func EmitChannelOpenTryEvent(ctx sdk.Context, portID string, channelID string, c sdk.NewAttribute(types.AttributeKeyConnectionID, channel.ConnectionHops[0]), sdk.NewAttribute(types.AttributeVersion, channel.Version), ), - }) - ctx.EventManager().EmitEvents(sdk.Events{ sdk.NewEvent( sdk.EventTypeMessage, sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), @@ -53,8 +48,8 @@ func EmitChannelOpenTryEvent(ctx sdk.Context, portID string, channelID string, c }) } -// EmitChannelOpenAckEvent emits a channel open acknowledge event -func EmitChannelOpenAckEvent(ctx sdk.Context, portID string, channelID string, channel types.Channel) { +// emitChannelOpenAckEvent emits a channel open acknowledge event +func emitChannelOpenAckEvent(ctx sdk.Context, portID string, channelID string, channel types.Channel) { ctx.EventManager().EmitEvents(sdk.Events{ sdk.NewEvent( types.EventTypeChannelOpenAck, @@ -71,8 +66,8 @@ func EmitChannelOpenAckEvent(ctx sdk.Context, portID string, channelID string, c }) } -// EmitChannelOpenConfirmEvent emits a channel open confirm event -func EmitChannelOpenConfirmEvent(ctx sdk.Context, portID string, channelID string, channel types.Channel) { +// emitChannelOpenConfirmEvent emits a channel open confirm event +func emitChannelOpenConfirmEvent(ctx sdk.Context, portID string, channelID string, channel types.Channel) { ctx.EventManager().EmitEvents(sdk.Events{ sdk.NewEvent( types.EventTypeChannelOpenConfirm, @@ -89,8 +84,8 @@ func EmitChannelOpenConfirmEvent(ctx sdk.Context, portID string, channelID strin }) } -// EmitChannelCloseInitEvent emits a channel close init event -func EmitChannelCloseInitEvent(ctx sdk.Context, portID string, channelID string, channel types.Channel) { +// emitChannelCloseInitEvent emits a channel close init event +func emitChannelCloseInitEvent(ctx sdk.Context, portID string, channelID string, channel types.Channel) { ctx.EventManager().EmitEvents(sdk.Events{ sdk.NewEvent( types.EventTypeChannelCloseInit, @@ -107,8 +102,8 @@ func EmitChannelCloseInitEvent(ctx sdk.Context, portID string, channelID string, }) } -// EmitChannelCloseConfirmEvent emits a channel close confirm event -func EmitChannelCloseConfirmEvent(ctx sdk.Context, portID string, channelID string, channel types.Channel) { +// emitChannelCloseConfirmEvent emits a channel close confirm event +func emitChannelCloseConfirmEvent(ctx sdk.Context, portID string, channelID string, channel types.Channel) { ctx.EventManager().EmitEvents(sdk.Events{ sdk.NewEvent( types.EventTypeChannelCloseConfirm, @@ -125,13 +120,13 @@ func EmitChannelCloseConfirmEvent(ctx sdk.Context, portID string, channelID stri }) } -// EmitSendPacketEvent emits an event with packet data along with other packet information for relayer +// emitSendPacketEvent emits an event with packet data along with other packet information for relayer // to pick up and relay to other chain -func EmitSendPacketEvent(ctx sdk.Context, packet exported.PacketI, channel types.Channel, timeoutHeight exported.Height) { +func emitSendPacketEvent(ctx sdk.Context, packet exported.PacketI, channel types.Channel, timeoutHeight exported.Height) { ctx.EventManager().EmitEvents(sdk.Events{ sdk.NewEvent( types.EventTypeSendPacket, - sdk.NewAttribute(types.AttributeKeyData, string(packet.GetData())), // DEPRECATED + sdk.NewAttribute(types.AttributeKeyData, string(packet.GetData())), //nolint:staticcheck // DEPRECATED sdk.NewAttribute(types.AttributeKeyDataHex, hex.EncodeToString(packet.GetData())), sdk.NewAttribute(types.AttributeKeyTimeoutHeight, timeoutHeight.String()), sdk.NewAttribute(types.AttributeKeyTimeoutTimestamp, fmt.Sprintf("%d", packet.GetTimeoutTimestamp())), @@ -152,13 +147,13 @@ func EmitSendPacketEvent(ctx sdk.Context, packet exported.PacketI, channel types }) } -// EmitRecvPacketEvent emits a receive packet event. It will be emitted both the first time a packet +// emitRecvPacketEvent emits a receive packet event. It will be emitted both the first time a packet // is received for a certain sequence and for all duplicate receives. -func EmitRecvPacketEvent(ctx sdk.Context, packet exported.PacketI, channel types.Channel) { +func emitRecvPacketEvent(ctx sdk.Context, packet exported.PacketI, channel types.Channel) { ctx.EventManager().EmitEvents(sdk.Events{ sdk.NewEvent( types.EventTypeRecvPacket, - sdk.NewAttribute(types.AttributeKeyData, string(packet.GetData())), // DEPRECATED + sdk.NewAttribute(types.AttributeKeyData, string(packet.GetData())), //nolint:staticcheck // DEPRECATED sdk.NewAttribute(types.AttributeKeyDataHex, hex.EncodeToString(packet.GetData())), sdk.NewAttribute(types.AttributeKeyTimeoutHeight, packet.GetTimeoutHeight().String()), sdk.NewAttribute(types.AttributeKeyTimeoutTimestamp, fmt.Sprintf("%d", packet.GetTimeoutTimestamp())), @@ -179,12 +174,12 @@ func EmitRecvPacketEvent(ctx sdk.Context, packet exported.PacketI, channel types }) } -// EmitWriteAcknowledgementEvent emits an event that the relayer can query for -func EmitWriteAcknowledgementEvent(ctx sdk.Context, packet exported.PacketI, channel types.Channel, acknowledgement []byte) { +// emitWriteAcknowledgementEvent emits an event that the relayer can query for +func emitWriteAcknowledgementEvent(ctx sdk.Context, packet exported.PacketI, channel types.Channel, acknowledgement []byte) { ctx.EventManager().EmitEvents(sdk.Events{ sdk.NewEvent( types.EventTypeWriteAck, - sdk.NewAttribute(types.AttributeKeyData, string(packet.GetData())), // DEPRECATED + sdk.NewAttribute(types.AttributeKeyData, string(packet.GetData())), //nolint:staticcheck // DEPRECATED sdk.NewAttribute(types.AttributeKeyDataHex, hex.EncodeToString(packet.GetData())), sdk.NewAttribute(types.AttributeKeyTimeoutHeight, packet.GetTimeoutHeight().String()), sdk.NewAttribute(types.AttributeKeyTimeoutTimestamp, fmt.Sprintf("%d", packet.GetTimeoutTimestamp())), @@ -193,7 +188,7 @@ func EmitWriteAcknowledgementEvent(ctx sdk.Context, packet exported.PacketI, cha sdk.NewAttribute(types.AttributeKeySrcChannel, packet.GetSourceChannel()), sdk.NewAttribute(types.AttributeKeyDstPort, packet.GetDestPort()), sdk.NewAttribute(types.AttributeKeyDstChannel, packet.GetDestChannel()), - sdk.NewAttribute(types.AttributeKeyAck, string(acknowledgement)), + sdk.NewAttribute(types.AttributeKeyAck, string(acknowledgement)), //nolint:staticcheck // DEPRECATED sdk.NewAttribute(types.AttributeKeyAckHex, hex.EncodeToString(acknowledgement)), // we only support 1-hop packets now, and that is the most important hop for a relayer // (is it going to a chain I am connected to) @@ -206,9 +201,9 @@ func EmitWriteAcknowledgementEvent(ctx sdk.Context, packet exported.PacketI, cha }) } -// EmitAcknowledgePacketEvent emits an acknowledge packet event. It will be emitted both the first time +// emitAcknowledgePacketEvent emits an acknowledge packet event. It will be emitted both the first time // a packet is acknowledged for a certain sequence and for all duplicate acknowledgements. -func EmitAcknowledgePacketEvent(ctx sdk.Context, packet exported.PacketI, channel types.Channel) { +func emitAcknowledgePacketEvent(ctx sdk.Context, packet exported.PacketI, channel types.Channel) { ctx.EventManager().EmitEvents(sdk.Events{ sdk.NewEvent( types.EventTypeAcknowledgePacket, @@ -231,9 +226,9 @@ func EmitAcknowledgePacketEvent(ctx sdk.Context, packet exported.PacketI, channe }) } -// EmitTimeoutPacketEvent emits a timeout packet event. It will be emitted both the first time a packet +// emitTimeoutPacketEvent emits a timeout packet event. It will be emitted both the first time a packet // is timed out for a certain sequence and for all duplicate timeouts. -func EmitTimeoutPacketEvent(ctx sdk.Context, packet exported.PacketI, channel types.Channel) { +func emitTimeoutPacketEvent(ctx sdk.Context, packet exported.PacketI, channel types.Channel) { ctx.EventManager().EmitEvents(sdk.Events{ sdk.NewEvent( types.EventTypeTimeoutPacket, @@ -253,8 +248,8 @@ func EmitTimeoutPacketEvent(ctx sdk.Context, packet exported.PacketI, channel ty }) } -// EmitChannelClosedEvent emits a channel closed event. -func EmitChannelClosedEvent(ctx sdk.Context, packet exported.PacketI, channel types.Channel) { +// emitChannelClosedEvent emits a channel closed event. +func emitChannelClosedEvent(ctx sdk.Context, packet exported.PacketI, channel types.Channel) { ctx.EventManager().EmitEvents(sdk.Events{ sdk.NewEvent( types.EventTypeChannelClosed, @@ -265,5 +260,9 @@ func EmitChannelClosedEvent(ctx sdk.Context, packet exported.PacketI, channel ty sdk.NewAttribute(types.AttributeKeyConnectionID, channel.ConnectionHops[0]), sdk.NewAttribute(types.AttributeKeyChannelOrdering, channel.Ordering.String()), ), + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + ), }) } diff --git a/modules/core/04-channel/keeper/grpc_query.go b/modules/core/04-channel/keeper/grpc_query.go index 6936fec0596..ee11b40251e 100644 --- a/modules/core/04-channel/keeper/grpc_query.go +++ b/modules/core/04-channel/keeper/grpc_query.go @@ -5,17 +5,17 @@ import ( "strconv" "strings" + errorsmod "cosmossdk.io/errors" "github.com/cosmos/cosmos-sdk/store/prefix" sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/types/query" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - connectiontypes "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types" - "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + connectiontypes "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" + "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" ) var _ types.QueryServer = (*Keeper)(nil) @@ -35,7 +35,7 @@ func (q Keeper) Channel(c context.Context, req *types.QueryChannelRequest) (*typ if !found { return nil, status.Error( codes.NotFound, - sdkerrors.Wrapf(types.ErrChannelNotFound, "port-id: %s, channel-id %s", req.PortId, req.ChannelId).Error(), + errorsmod.Wrapf(types.ErrChannelNotFound, "port-id: %s, channel-id %s", req.PortId, req.ChannelId).Error(), ) } @@ -96,26 +96,27 @@ func (q Keeper) ConnectionChannels(c context.Context, req *types.QueryConnection channels := []*types.IdentifiedChannel{} store := prefix.NewStore(ctx.KVStore(q.storeKey), []byte(host.KeyChannelEndPrefix)) - pageRes, err := query.Paginate(store, req.Pagination, func(key, value []byte) error { + pageRes, err := query.FilteredPaginate(store, req.Pagination, func(key, value []byte, accumulate bool) (bool, error) { + // filter any metadata stored under channel key var result types.Channel if err := q.cdc.Unmarshal(value, &result); err != nil { - return err + return false, err } // ignore channel and continue to the next item if the connection is // different than the requested one if result.ConnectionHops[0] != req.Connection { - return nil + return false, nil } portID, channelID, err := host.ParseChannelPath(string(key)) if err != nil { - return err + return false, err } identifiedChannel := types.NewIdentifiedChannel(portID, channelID, result) channels = append(channels, &identifiedChannel) - return nil + return true, nil }) if err != nil { return nil, err @@ -168,7 +169,7 @@ func (q Keeper) ChannelConsensusState(c context.Context, req *types.QueryChannel if !found { return nil, status.Error( codes.NotFound, - sdkerrors.Wrapf(types.ErrChannelNotFound, "port-id: %s, channel-id %s", req.PortId, req.ChannelId).Error(), + errorsmod.Wrapf(types.ErrChannelNotFound, "port-id: %s, channel-id %s", req.PortId, req.ChannelId).Error(), ) } @@ -176,7 +177,7 @@ func (q Keeper) ChannelConsensusState(c context.Context, req *types.QueryChannel if !found { return nil, status.Error( codes.NotFound, - sdkerrors.Wrapf(connectiontypes.ErrConnectionNotFound, "connection-id: %s", channel.ConnectionHops[0]).Error(), + errorsmod.Wrapf(connectiontypes.ErrConnectionNotFound, "connection-id: %s", channel.ConnectionHops[0]).Error(), ) } @@ -185,7 +186,7 @@ func (q Keeper) ChannelConsensusState(c context.Context, req *types.QueryChannel if !found { return nil, status.Error( codes.NotFound, - sdkerrors.Wrapf(clienttypes.ErrConsensusStateNotFound, "client-id: %s", connection.ClientId).Error(), + errorsmod.Wrapf(clienttypes.ErrConsensusStateNotFound, "client-id: %s", connection.ClientId).Error(), ) } @@ -483,7 +484,7 @@ func (q Keeper) NextSequenceReceive(c context.Context, req *types.QueryNextSeque if !found { return nil, status.Error( codes.NotFound, - sdkerrors.Wrapf(types.ErrSequenceReceiveNotFound, "port-id: %s, channel-id %s", req.PortId, req.ChannelId).Error(), + errorsmod.Wrapf(types.ErrSequenceReceiveNotFound, "port-id: %s, channel-id %s", req.PortId, req.ChannelId).Error(), ) } diff --git a/modules/core/04-channel/keeper/grpc_query_test.go b/modules/core/04-channel/keeper/grpc_query_test.go index 8e7ac77a57a..0082663a243 100644 --- a/modules/core/04-channel/keeper/grpc_query_test.go +++ b/modules/core/04-channel/keeper/grpc_query_test.go @@ -6,13 +6,15 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/query" - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - connectiontypes "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types" - "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" - "github.com/cosmos/ibc-go/v4/modules/core/exported" - ibctesting "github.com/cosmos/ibc-go/v4/testing" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + connectiontypes "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" + "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + "github.com/cosmos/ibc-go/v7/modules/core/exported" + ibctesting "github.com/cosmos/ibc-go/v7/testing" ) +const doesnotexist = "doesnotexist" + func (suite *KeeperTestSuite) TestQueryChannel() { var ( req *types.QueryChannelRequest @@ -372,7 +374,7 @@ func (suite *KeeperTestSuite) TestQueryChannelClientState() { channel := path.EndpointA.GetChannel() // update channel to reference a connection that does not exist - channel.ConnectionHops[0] = "doesnotexist" + channel.ConnectionHops[0] = doesnotexist // set connection hops to wrong connection ID suite.chainA.App.GetIBCKeeper().ChannelKeeper.SetChannel(suite.chainA.GetContext(), path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, channel) @@ -508,7 +510,7 @@ func (suite *KeeperTestSuite) TestQueryChannelConsensusState() { channel := path.EndpointA.GetChannel() // update channel to reference a connection that does not exist - channel.ConnectionHops[0] = "doesnotexist" + channel.ConnectionHops[0] = doesnotexist // set connection hops to wrong connection ID suite.chainA.App.GetIBCKeeper().ChannelKeeper.SetChannel(suite.chainA.GetContext(), path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, channel) diff --git a/modules/core/04-channel/keeper/handshake.go b/modules/core/04-channel/keeper/handshake.go index 1d9cfd6382c..6419a8984bd 100644 --- a/modules/core/04-channel/keeper/handshake.go +++ b/modules/core/04-channel/keeper/handshake.go @@ -3,16 +3,16 @@ package keeper import ( "fmt" + errorsmod "cosmossdk.io/errors" "github.com/cosmos/cosmos-sdk/telemetry" sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - connectiontypes "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types" - "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" - porttypes "github.com/cosmos/ibc-go/v4/modules/core/05-port/types" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" - "github.com/cosmos/ibc-go/v4/modules/core/exported" + connectiontypes "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" + "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + porttypes "github.com/cosmos/ibc-go/v7/modules/core/05-port/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + "github.com/cosmos/ibc-go/v7/modules/core/exported" ) // ChanOpenInit is called by a module to initiate a channel opening handshake with @@ -30,12 +30,12 @@ func (k Keeper) ChanOpenInit( // connection hop length checked on msg.ValidateBasic() connectionEnd, found := k.connectionKeeper.GetConnection(ctx, connectionHops[0]) if !found { - return "", nil, sdkerrors.Wrap(connectiontypes.ErrConnectionNotFound, connectionHops[0]) + return "", nil, errorsmod.Wrap(connectiontypes.ErrConnectionNotFound, connectionHops[0]) } getVersions := connectionEnd.GetVersions() if len(getVersions) != 1 { - return "", nil, sdkerrors.Wrapf( + return "", nil, errorsmod.Wrapf( connectiontypes.ErrInvalidVersion, "single version must be negotiated on connection before opening channel, got: %v", getVersions, @@ -43,7 +43,7 @@ func (k Keeper) ChanOpenInit( } if !connectiontypes.VerifySupportedFeature(getVersions[0], order.String()) { - return "", nil, sdkerrors.Wrapf( + return "", nil, errorsmod.Wrapf( connectiontypes.ErrInvalidVersion, "connection version %s does not support channel ordering: %s", getVersions[0], order.String(), @@ -51,14 +51,14 @@ func (k Keeper) ChanOpenInit( } if !k.portKeeper.Authenticate(ctx, portCap, portID) { - return "", nil, sdkerrors.Wrapf(porttypes.ErrInvalidPort, "caller does not own port capability for port ID %s", portID) + return "", nil, errorsmod.Wrapf(porttypes.ErrInvalidPort, "caller does not own port capability for port ID %s", portID) } channelID := k.GenerateChannelIdentifier(ctx) 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 "", nil, errorsmod.Wrapf(err, "could not create channel capability for port ID %s and channel ID %s", portID, channelID) } return channelID, capKey, nil @@ -89,7 +89,7 @@ func (k Keeper) WriteOpenInitChannel( telemetry.IncrCounter(1, "ibc", "channel", "open-init") }() - EmitChannelOpenInitEvent(ctx, portID, channelID, channel) + emitChannelOpenInitEvent(ctx, portID, channelID, channel) } // ChanOpenTry is called by a module to accept the first step of a channel opening @@ -107,23 +107,23 @@ func (k Keeper) ChanOpenTry( ) (string, *capabilitytypes.Capability, error) { // connection hops only supports a single connection if len(connectionHops) != 1 { - return "", nil, sdkerrors.Wrapf(types.ErrTooManyConnectionHops, "expected 1, got %d", len(connectionHops)) + return "", nil, errorsmod.Wrapf(types.ErrTooManyConnectionHops, "expected 1, got %d", len(connectionHops)) } // 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) + return "", nil, errorsmod.Wrapf(porttypes.ErrInvalidPort, "caller does not own port capability for port ID %s", portID) } connectionEnd, found := k.connectionKeeper.GetConnection(ctx, connectionHops[0]) if !found { - return "", nil, sdkerrors.Wrap(connectiontypes.ErrConnectionNotFound, connectionHops[0]) + return "", nil, errorsmod.Wrap(connectiontypes.ErrConnectionNotFound, connectionHops[0]) } if connectionEnd.GetState() != int32(connectiontypes.OPEN) { - return "", nil, sdkerrors.Wrapf( + return "", nil, errorsmod.Wrapf( connectiontypes.ErrInvalidConnectionState, "connection state is not OPEN (got %s)", connectiontypes.State(connectionEnd.GetState()).String(), ) @@ -131,7 +131,7 @@ func (k Keeper) ChanOpenTry( getVersions := connectionEnd.GetVersions() if len(getVersions) != 1 { - return "", nil, sdkerrors.Wrapf( + return "", nil, errorsmod.Wrapf( connectiontypes.ErrInvalidVersion, "single version must be negotiated on connection before opening channel, got: %v", getVersions, @@ -139,7 +139,7 @@ func (k Keeper) ChanOpenTry( } if !connectiontypes.VerifySupportedFeature(getVersions[0], order.String()) { - return "", nil, sdkerrors.Wrapf( + return "", nil, errorsmod.Wrapf( connectiontypes.ErrInvalidVersion, "connection version %s does not support channel ordering: %s", getVersions[0], order.String(), @@ -170,7 +170,7 @@ func (k Keeper) ChanOpenTry( 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 "", nil, errorsmod.Wrapf(err, "could not create channel capability for port ID %s and channel ID %s", portID, channelID) } return channelID, capKey, nil @@ -202,7 +202,7 @@ func (k Keeper) WriteOpenTryChannel( telemetry.IncrCounter(1, "ibc", "channel", "open-try") }() - EmitChannelOpenTryEvent(ctx, portID, channelID, channel) + emitChannelOpenTryEvent(ctx, portID, channelID, channel) } // ChanOpenAck is called by the handshake-originating module to acknowledge the @@ -219,24 +219,24 @@ func (k Keeper) ChanOpenAck( ) error { channel, found := k.GetChannel(ctx, portID, channelID) if !found { - return sdkerrors.Wrapf(types.ErrChannelNotFound, "port ID (%s) channel ID (%s)", portID, channelID) + return errorsmod.Wrapf(types.ErrChannelNotFound, "port ID (%s) channel ID (%s)", portID, channelID) } if channel.State != types.INIT { - return sdkerrors.Wrapf(types.ErrInvalidChannelState, "channel state should be INIT (got %s)", channel.State.String()) + return errorsmod.Wrapf(types.ErrInvalidChannelState, "channel state should be INIT (got %s)", channel.State.String()) } if !k.scopedKeeper.AuthenticateCapability(ctx, chanCap, host.ChannelCapabilityPath(portID, channelID)) { - return sdkerrors.Wrapf(types.ErrChannelCapabilityNotFound, "caller does not own capability for channel, port ID (%s) channel ID (%s)", portID, channelID) + return errorsmod.Wrapf(types.ErrChannelCapabilityNotFound, "caller does not own capability for channel, port ID (%s) channel ID (%s)", portID, channelID) } connectionEnd, found := k.connectionKeeper.GetConnection(ctx, channel.ConnectionHops[0]) if !found { - return sdkerrors.Wrap(connectiontypes.ErrConnectionNotFound, channel.ConnectionHops[0]) + return errorsmod.Wrap(connectiontypes.ErrConnectionNotFound, channel.ConnectionHops[0]) } if connectionEnd.GetState() != int32(connectiontypes.OPEN) { - return sdkerrors.Wrapf( + return errorsmod.Wrapf( connectiontypes.ErrInvalidConnectionState, "connection state is not OPEN (got %s)", connectiontypes.State(connectionEnd.GetState()).String(), ) @@ -287,11 +287,11 @@ func (k Keeper) WriteOpenAckChannel( telemetry.IncrCounter(1, "ibc", "channel", "open-ack") }() - EmitChannelOpenAckEvent(ctx, portID, channelID, channel) + emitChannelOpenAckEvent(ctx, portID, channelID, channel) } // ChanOpenConfirm is called by the counterparty module to close their end of the -// channel, since the other end has been closed. +// channel, since the other end has been closed. func (k Keeper) ChanOpenConfirm( ctx sdk.Context, portID, @@ -302,27 +302,27 @@ func (k Keeper) ChanOpenConfirm( ) error { channel, found := k.GetChannel(ctx, portID, channelID) if !found { - return sdkerrors.Wrapf(types.ErrChannelNotFound, "port ID (%s) channel ID (%s)", portID, channelID) + return errorsmod.Wrapf(types.ErrChannelNotFound, "port ID (%s) channel ID (%s)", portID, channelID) } if channel.State != types.TRYOPEN { - return sdkerrors.Wrapf( + return errorsmod.Wrapf( types.ErrInvalidChannelState, "channel state is not TRYOPEN (got %s)", channel.State.String(), ) } if !k.scopedKeeper.AuthenticateCapability(ctx, chanCap, host.ChannelCapabilityPath(portID, channelID)) { - return sdkerrors.Wrapf(types.ErrChannelCapabilityNotFound, "caller does not own capability for channel, port ID (%s) channel ID (%s)", portID, channelID) + return errorsmod.Wrapf(types.ErrChannelCapabilityNotFound, "caller does not own capability for channel, port ID (%s) channel ID (%s)", portID, channelID) } connectionEnd, found := k.connectionKeeper.GetConnection(ctx, channel.ConnectionHops[0]) if !found { - return sdkerrors.Wrap(connectiontypes.ErrConnectionNotFound, channel.ConnectionHops[0]) + return errorsmod.Wrap(connectiontypes.ErrConnectionNotFound, channel.ConnectionHops[0]) } if connectionEnd.GetState() != int32(connectiontypes.OPEN) { - return sdkerrors.Wrapf( + return errorsmod.Wrapf( connectiontypes.ErrInvalidConnectionState, "connection state is not OPEN (got %s)", connectiontypes.State(connectionEnd.GetState()).String(), ) @@ -367,7 +367,7 @@ func (k Keeper) WriteOpenConfirmChannel( telemetry.IncrCounter(1, "ibc", "channel", "open-confirm") }() - EmitChannelOpenConfirmEvent(ctx, portID, channelID, channel) + emitChannelOpenConfirmEvent(ctx, portID, channelID, channel) } // Closing Handshake @@ -384,25 +384,25 @@ func (k Keeper) ChanCloseInit( chanCap *capabilitytypes.Capability, ) error { if !k.scopedKeeper.AuthenticateCapability(ctx, chanCap, host.ChannelCapabilityPath(portID, channelID)) { - return sdkerrors.Wrapf(types.ErrChannelCapabilityNotFound, "caller does not own capability for channel, port ID (%s) channel ID (%s)", portID, channelID) + return errorsmod.Wrapf(types.ErrChannelCapabilityNotFound, "caller does not own capability for channel, port ID (%s) channel ID (%s)", portID, channelID) } channel, found := k.GetChannel(ctx, portID, channelID) if !found { - return sdkerrors.Wrapf(types.ErrChannelNotFound, "port ID (%s) channel ID (%s)", portID, channelID) + return errorsmod.Wrapf(types.ErrChannelNotFound, "port ID (%s) channel ID (%s)", portID, channelID) } if channel.State == types.CLOSED { - return sdkerrors.Wrap(types.ErrInvalidChannelState, "channel is already CLOSED") + return errorsmod.Wrap(types.ErrInvalidChannelState, "channel is already CLOSED") } connectionEnd, found := k.connectionKeeper.GetConnection(ctx, channel.ConnectionHops[0]) if !found { - return sdkerrors.Wrap(connectiontypes.ErrConnectionNotFound, channel.ConnectionHops[0]) + return errorsmod.Wrap(connectiontypes.ErrConnectionNotFound, channel.ConnectionHops[0]) } if connectionEnd.GetState() != int32(connectiontypes.OPEN) { - return sdkerrors.Wrapf( + return errorsmod.Wrapf( connectiontypes.ErrInvalidConnectionState, "connection state is not OPEN (got %s)", connectiontypes.State(connectionEnd.GetState()).String(), ) @@ -417,7 +417,7 @@ func (k Keeper) ChanCloseInit( channel.State = types.CLOSED k.SetChannel(ctx, portID, channelID, channel) - EmitChannelCloseInitEvent(ctx, portID, channelID, channel) + emitChannelCloseInitEvent(ctx, portID, channelID, channel) return nil } @@ -433,25 +433,25 @@ func (k Keeper) ChanCloseConfirm( proofHeight exported.Height, ) error { if !k.scopedKeeper.AuthenticateCapability(ctx, chanCap, host.ChannelCapabilityPath(portID, channelID)) { - return sdkerrors.Wrap(types.ErrChannelCapabilityNotFound, "caller does not own capability for channel, port ID (%s) channel ID (%s)") + return errorsmod.Wrap(types.ErrChannelCapabilityNotFound, "caller does not own capability for channel, port ID (%s) channel ID (%s)") } channel, found := k.GetChannel(ctx, portID, channelID) if !found { - return sdkerrors.Wrapf(types.ErrChannelNotFound, "port ID (%s) channel ID (%s)", portID, channelID) + return errorsmod.Wrapf(types.ErrChannelNotFound, "port ID (%s) channel ID (%s)", portID, channelID) } if channel.State == types.CLOSED { - return sdkerrors.Wrap(types.ErrInvalidChannelState, "channel is already CLOSED") + return errorsmod.Wrap(types.ErrInvalidChannelState, "channel is already CLOSED") } connectionEnd, found := k.connectionKeeper.GetConnection(ctx, channel.ConnectionHops[0]) if !found { - return sdkerrors.Wrap(connectiontypes.ErrConnectionNotFound, channel.ConnectionHops[0]) + return errorsmod.Wrap(connectiontypes.ErrConnectionNotFound, channel.ConnectionHops[0]) } if connectionEnd.GetState() != int32(connectiontypes.OPEN) { - return sdkerrors.Wrapf( + return errorsmod.Wrapf( connectiontypes.ErrInvalidConnectionState, "connection state is not OPEN (got %s)", connectiontypes.State(connectionEnd.GetState()).String(), ) @@ -482,7 +482,7 @@ func (k Keeper) ChanCloseConfirm( channel.State = types.CLOSED k.SetChannel(ctx, portID, channelID, channel) - EmitChannelCloseConfirmEvent(ctx, portID, channelID, channel) + emitChannelCloseConfirmEvent(ctx, portID, channelID, channel) return nil } diff --git a/modules/core/04-channel/keeper/handshake_test.go b/modules/core/04-channel/keeper/handshake_test.go index 88ca88887b4..bf77d7ed530 100644 --- a/modules/core/04-channel/keeper/handshake_test.go +++ b/modules/core/04-channel/keeper/handshake_test.go @@ -5,12 +5,12 @@ import ( capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - connectiontypes "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types" - "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" - "github.com/cosmos/ibc-go/v4/modules/core/exported" - ibctesting "github.com/cosmos/ibc-go/v4/testing" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + connectiontypes "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" + "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + "github.com/cosmos/ibc-go/v7/modules/core/exported" + ibctesting "github.com/cosmos/ibc-go/v7/testing" ) type testCase = struct { @@ -152,7 +152,8 @@ func (suite *KeeperTestSuite) TestChanOpenTry() { {"success", func() { suite.coordinator.SetupConnections(path) path.SetChannelOrdered() - path.EndpointA.ChanOpenInit() + err := path.EndpointA.ChanOpenInit() + suite.Require().NoError(err) suite.chainB.CreatePortCapability(suite.chainB.GetSimApp().ScopedIBCMockKeeper, ibctesting.MockPort) portCap = suite.chainB.GetPortCapability(ibctesting.MockPort) @@ -177,7 +178,8 @@ func (suite *KeeperTestSuite) TestChanOpenTry() { {"consensus state not found", func() { suite.coordinator.SetupConnections(path) path.SetChannelOrdered() - path.EndpointA.ChanOpenInit() + err := path.EndpointA.ChanOpenInit() + suite.Require().NoError(err) suite.chainB.CreatePortCapability(suite.chainB.GetSimApp().ScopedIBCMockKeeper, ibctesting.MockPort) portCap = suite.chainB.GetPortCapability(ibctesting.MockPort) @@ -192,14 +194,16 @@ func (suite *KeeperTestSuite) TestChanOpenTry() { {"port capability not found", func() { suite.coordinator.SetupConnections(path) path.SetChannelOrdered() - path.EndpointA.ChanOpenInit() + err := path.EndpointA.ChanOpenInit() + suite.Require().NoError(err) portCap = capabilitytypes.NewCapability(3) }, false}, {"connection version not negotiated", func() { suite.coordinator.SetupConnections(path) path.SetChannelOrdered() - path.EndpointA.ChanOpenInit() + err := path.EndpointA.ChanOpenInit() + suite.Require().NoError(err) // modify connB versions conn := path.EndpointB.GetConnection() @@ -217,7 +221,8 @@ func (suite *KeeperTestSuite) TestChanOpenTry() { {"connection does not support ORDERED channels", func() { suite.coordinator.SetupConnections(path) path.SetChannelOrdered() - path.EndpointA.ChanOpenInit() + err := path.EndpointA.ChanOpenInit() + suite.Require().NoError(err) // modify connA versions to only support UNORDERED channels conn := path.EndpointA.GetConnection() @@ -340,7 +345,7 @@ func (suite *KeeperTestSuite) TestChanOpenAck() { // set the channel's connection hops to wrong connection ID channel := path.EndpointA.GetChannel() - channel.ConnectionHops[0] = "doesnotexist" + channel.ConnectionHops[0] = doesnotexist suite.chainA.App.GetIBCKeeper().ChannelKeeper.SetChannel(suite.chainA.GetContext(), path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, channel) }, false}, {"connection is not OPEN", func() { @@ -405,7 +410,8 @@ func (suite *KeeperTestSuite) TestChanOpenAck() { err := path.EndpointA.ChanOpenInit() suite.Require().NoError(err) - path.EndpointB.ChanOpenTry() + err = path.EndpointB.ChanOpenTry() + suite.Require().NoError(err) channelCap = capabilitytypes.NewCapability(6) }, false}, @@ -496,7 +502,7 @@ func (suite *KeeperTestSuite) TestChanOpenConfirm() { // set the channel's connection hops to wrong connection ID channel := path.EndpointB.GetChannel() - channel.ConnectionHops[0] = "doesnotexist" + channel.ConnectionHops[0] = doesnotexist suite.chainB.App.GetIBCKeeper().ChannelKeeper.SetChannel(suite.chainB.GetContext(), path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, channel) }, false}, {"connection is not OPEN", func() { @@ -618,7 +624,7 @@ func (suite *KeeperTestSuite) TestChanCloseInit() { channelCap = suite.chainA.GetChannelCapability(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) // close channel - err := path.EndpointA.SetChannelClosed() + err := path.EndpointA.SetChannelState(types.CLOSED) suite.Require().NoError(err) }, false}, {"connection not found", func() { @@ -627,7 +633,7 @@ func (suite *KeeperTestSuite) TestChanCloseInit() { // set the channel's connection hops to wrong connection ID channel := path.EndpointA.GetChannel() - channel.ConnectionHops[0] = "doesnotexist" + channel.ConnectionHops[0] = doesnotexist suite.chainA.App.GetIBCKeeper().ChannelKeeper.SetChannel(suite.chainA.GetContext(), path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, channel) }, false}, {"connection is not OPEN", func() { @@ -639,6 +645,7 @@ func (suite *KeeperTestSuite) TestChanCloseInit() { // create channel in init path.SetChannelOrdered() err = path.EndpointA.ChanOpenInit() + suite.Require().NoError(err) // ensure channel capability check passes suite.chainA.CreateChannelCapability(suite.chainA.GetSimApp().ScopedIBCMockKeeper, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) @@ -686,7 +693,7 @@ func (suite *KeeperTestSuite) TestChanCloseConfirm() { suite.coordinator.Setup(path) channelCap = suite.chainB.GetChannelCapability(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID) - err := path.EndpointA.SetChannelClosed() + err := path.EndpointA.SetChannelState(types.CLOSED) suite.Require().NoError(err) }, true}, {"channel doesn't exist", func() { @@ -702,7 +709,7 @@ func (suite *KeeperTestSuite) TestChanCloseConfirm() { suite.coordinator.Setup(path) channelCap = suite.chainB.GetChannelCapability(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID) - err := path.EndpointB.SetChannelClosed() + err := path.EndpointB.SetChannelState(types.CLOSED) suite.Require().NoError(err) }, false}, {"connection not found", func() { @@ -711,7 +718,7 @@ func (suite *KeeperTestSuite) TestChanCloseConfirm() { // set the channel's connection hops to wrong connection ID channel := path.EndpointB.GetChannel() - channel.ConnectionHops[0] = "doesnotexist" + channel.ConnectionHops[0] = doesnotexist suite.chainB.App.GetIBCKeeper().ChannelKeeper.SetChannel(suite.chainB.GetContext(), path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, channel) }, false}, {"connection is not OPEN", func() { @@ -733,7 +740,7 @@ func (suite *KeeperTestSuite) TestChanCloseConfirm() { suite.coordinator.Setup(path) channelCap = suite.chainB.GetChannelCapability(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID) - err := path.EndpointA.SetChannelClosed() + err := path.EndpointA.SetChannelState(types.CLOSED) suite.Require().NoError(err) heightDiff = 3 @@ -747,7 +754,7 @@ func (suite *KeeperTestSuite) TestChanCloseConfirm() { suite.coordinator.Setup(path) channelCap = suite.chainB.GetChannelCapability(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID) - err := path.EndpointA.SetChannelClosed() + err := path.EndpointA.SetChannelState(types.CLOSED) suite.Require().NoError(err) channelCap = capabilitytypes.NewCapability(3) diff --git a/modules/core/04-channel/keeper/keeper.go b/modules/core/04-channel/keeper/keeper.go index 8d82129425f..3c800ad3795 100644 --- a/modules/core/04-channel/keeper/keeper.go +++ b/modules/core/04-channel/keeper/keeper.go @@ -5,19 +5,19 @@ import ( "strings" "github.com/cosmos/cosmos-sdk/codec" + storetypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - capabilitykeeper "github.com/cosmos/cosmos-sdk/x/capability/keeper" + errorsmod "github.com/cosmos/cosmos-sdk/types/errors" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" "github.com/tendermint/tendermint/libs/log" db "github.com/tendermint/tm-db" - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - connectiontypes "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types" - "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" - porttypes "github.com/cosmos/ibc-go/v4/modules/core/05-port/types" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" - "github.com/cosmos/ibc-go/v4/modules/core/exported" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + connectiontypes "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" + "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + porttypes "github.com/cosmos/ibc-go/v7/modules/core/05-port/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + "github.com/cosmos/ibc-go/v7/modules/core/exported" ) var _ porttypes.ICS4Wrapper = Keeper{} @@ -27,19 +27,19 @@ type Keeper struct { // implements gRPC QueryServer interface types.QueryServer - storeKey sdk.StoreKey + storeKey storetypes.StoreKey cdc codec.BinaryCodec clientKeeper types.ClientKeeper connectionKeeper types.ConnectionKeeper portKeeper types.PortKeeper - scopedKeeper capabilitykeeper.ScopedKeeper + scopedKeeper exported.ScopedKeeper } // NewKeeper creates a new IBC channel Keeper instance func NewKeeper( - cdc codec.BinaryCodec, key sdk.StoreKey, + cdc codec.BinaryCodec, key storetypes.StoreKey, clientKeeper types.ClientKeeper, connectionKeeper types.ConnectionKeeper, - portKeeper types.PortKeeper, scopedKeeper capabilitykeeper.ScopedKeeper, + portKeeper types.PortKeeper, scopedKeeper exported.ScopedKeeper, ) Keeper { return Keeper{ storeKey: key, @@ -53,7 +53,7 @@ func NewKeeper( // Logger returns a module-specific logger. func (k Keeper) Logger(ctx sdk.Context) log.Logger { - return ctx.Logger().With("module", "x/"+host.ModuleName+"/"+types.SubModuleName) + return ctx.Logger().With("module", "x/"+exported.ModuleName+"/"+types.SubModuleName) } // GenerateChannelIdentifier returns the next channel identifier. @@ -66,11 +66,17 @@ func (k Keeper) GenerateChannelIdentifier(ctx sdk.Context) string { return channelID } +// HasChannel true if the channel with the given identifiers exists in state. +func (k Keeper) HasChannel(ctx sdk.Context, portID, channelID string) bool { + store := ctx.KVStore(k.storeKey) + return store.Has(host.ChannelKey(portID, channelID)) +} + // GetChannel returns a channel with a particular identifier binded to a specific port func (k Keeper) GetChannel(ctx sdk.Context, portID, channelID string) (types.Channel, bool) { store := ctx.KVStore(k.storeKey) bz := store.Get(host.ChannelKey(portID, channelID)) - if bz == nil { + if len(bz) == 0 { return types.Channel{}, false } @@ -100,7 +106,7 @@ func (k Keeper) GetAppVersion(ctx sdk.Context, portID, channelID string) (string func (k Keeper) GetNextChannelSequence(ctx sdk.Context) uint64 { store := ctx.KVStore(k.storeKey) bz := store.Get([]byte(types.KeyNextChannelSequence)) - if bz == nil { + if len(bz) == 0 { panic("next channel sequence is nil") } @@ -118,7 +124,7 @@ func (k Keeper) SetNextChannelSequence(ctx sdk.Context, sequence uint64) { func (k Keeper) GetNextSequenceSend(ctx sdk.Context, portID, channelID string) (uint64, bool) { store := ctx.KVStore(k.storeKey) bz := store.Get(host.NextSequenceSendKey(portID, channelID)) - if bz == nil { + if len(bz) == 0 { return 0, false } @@ -136,7 +142,7 @@ func (k Keeper) SetNextSequenceSend(ctx sdk.Context, portID, channelID string, s func (k Keeper) GetNextSequenceRecv(ctx sdk.Context, portID, channelID string) (uint64, bool) { store := ctx.KVStore(k.storeKey) bz := store.Get(host.NextSequenceRecvKey(portID, channelID)) - if bz == nil { + if len(bz) == 0 { return 0, false } @@ -154,7 +160,7 @@ func (k Keeper) SetNextSequenceRecv(ctx sdk.Context, portID, channelID string, s func (k Keeper) GetNextSequenceAck(ctx sdk.Context, portID, channelID string) (uint64, bool) { store := ctx.KVStore(k.storeKey) bz := store.Get(host.NextSequenceAckKey(portID, channelID)) - if bz == nil { + if len(bz) == 0 { return 0, false } @@ -172,7 +178,7 @@ func (k Keeper) SetNextSequenceAck(ctx sdk.Context, portID, channelID string, se func (k Keeper) GetPacketReceipt(ctx sdk.Context, portID, channelID string, sequence uint64) (string, bool) { store := ctx.KVStore(k.storeKey) bz := store.Get(host.PacketReceiptKey(portID, channelID, sequence)) - if bz == nil { + if len(bz) == 0 { return "", false } @@ -219,7 +225,7 @@ func (k Keeper) SetPacketAcknowledgement(ctx sdk.Context, portID, channelID stri func (k Keeper) GetPacketAcknowledgement(ctx sdk.Context, portID, channelID string, sequence uint64) ([]byte, bool) { store := ctx.KVStore(k.storeKey) bz := store.Get(host.PacketAcknowledgementKey(portID, channelID, sequence)) - if bz == nil { + if len(bz) == 0 { return nil, false } return bz, true @@ -235,7 +241,7 @@ func (k Keeper) HasPacketAcknowledgement(ctx sdk.Context, portID, channelID stri // For each sequence, cb will be called. If the cb returns true, the iterator // will close and stop. func (k Keeper) IteratePacketSequence(ctx sdk.Context, iterator db.Iterator, cb func(portID, channelID string, sequence uint64) bool) { - defer iterator.Close() + defer sdk.LogDeferred(ctx.Logger(), func() error { return iterator.Close() }) for ; iterator.Valid(); iterator.Next() { portID, channelID, err := host.ParseChannelPath(string(iterator.Key())) if err != nil { @@ -371,7 +377,7 @@ func (k Keeper) IterateChannels(ctx sdk.Context, cb func(types.IdentifiedChannel store := ctx.KVStore(k.storeKey) iterator := sdk.KVStorePrefixIterator(store, []byte(host.KeyChannelEndPrefix)) - defer iterator.Close() + defer sdk.LogDeferred(ctx.Logger(), func() error { return iterator.Close() }) for ; iterator.Valid(); iterator.Next() { var channel types.Channel k.cdc.MustUnmarshal(iterator.Value(), &channel) @@ -384,6 +390,28 @@ func (k Keeper) IterateChannels(ctx sdk.Context, cb func(types.IdentifiedChannel } } +// GetAllChannelsWithPortPrefix returns all channels with the specified port prefix. If an empty prefix is provided +// all channels will be returned. +func (k Keeper) GetAllChannelsWithPortPrefix(ctx sdk.Context, portPrefix string) []types.IdentifiedChannel { + if strings.TrimSpace(portPrefix) == "" { + return k.GetAllChannels(ctx) + } + store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, types.FilteredPortPrefix(portPrefix)) + defer sdk.LogDeferred(ctx.Logger(), func() error { return iterator.Close() }) + + var filteredChannels []types.IdentifiedChannel + for ; iterator.Valid(); iterator.Next() { + var channel types.Channel + k.cdc.MustUnmarshal(iterator.Value(), &channel) + + portID, channelID := host.MustParseChannelPath(string(iterator.Key())) + identifiedChannel := types.NewIdentifiedChannel(portID, channelID, channel) + filteredChannels = append(filteredChannels, identifiedChannel) + } + return filteredChannels +} + // GetAllChannels returns all stored Channel objects. func (k Keeper) GetAllChannels(ctx sdk.Context) (channels []types.IdentifiedChannel) { k.IterateChannels(ctx, func(channel types.IdentifiedChannel) bool { @@ -397,17 +425,17 @@ func (k Keeper) GetAllChannels(ctx sdk.Context) (channels []types.IdentifiedChan func (k Keeper) GetChannelClientState(ctx sdk.Context, portID, channelID string) (string, exported.ClientState, error) { channel, found := k.GetChannel(ctx, portID, channelID) if !found { - return "", nil, sdkerrors.Wrapf(types.ErrChannelNotFound, "port-id: %s, channel-id: %s", portID, channelID) + return "", nil, errorsmod.Wrapf(types.ErrChannelNotFound, "port-id: %s, channel-id: %s", portID, channelID) } connection, found := k.connectionKeeper.GetConnection(ctx, channel.ConnectionHops[0]) if !found { - return "", nil, sdkerrors.Wrapf(connectiontypes.ErrConnectionNotFound, "connection-id: %s", channel.ConnectionHops[0]) + return "", nil, errorsmod.Wrapf(connectiontypes.ErrConnectionNotFound, "connection-id: %s", channel.ConnectionHops[0]) } clientState, found := k.clientKeeper.GetClientState(ctx, connection.ClientId) if !found { - return "", nil, sdkerrors.Wrapf(clienttypes.ErrClientNotFound, "client-id: %s", connection.ClientId) + return "", nil, errorsmod.Wrapf(clienttypes.ErrClientNotFound, "client-id: %s", connection.ClientId) } return connection.ClientId, clientState, nil @@ -417,7 +445,7 @@ func (k Keeper) GetChannelClientState(ctx sdk.Context, portID, channelID string) func (k Keeper) GetConnection(ctx sdk.Context, connectionID string) (exported.ConnectionI, error) { connection, found := k.connectionKeeper.GetConnection(ctx, connectionID) if !found { - return nil, sdkerrors.Wrapf(connectiontypes.ErrConnectionNotFound, "connection-id: %s", connectionID) + return nil, errorsmod.Wrapf(connectiontypes.ErrConnectionNotFound, "connection-id: %s", connectionID) } return connection, nil @@ -427,14 +455,14 @@ func (k Keeper) GetConnection(ctx sdk.Context, connectionID string) (exported.Co func (k Keeper) GetChannelConnection(ctx sdk.Context, portID, channelID string) (string, exported.ConnectionI, error) { channel, found := k.GetChannel(ctx, portID, channelID) if !found { - return "", nil, sdkerrors.Wrapf(types.ErrChannelNotFound, "port-id: %s, channel-id: %s", portID, channelID) + return "", nil, errorsmod.Wrapf(types.ErrChannelNotFound, "port-id: %s, channel-id: %s", portID, channelID) } connectionID := channel.ConnectionHops[0] connection, found := k.connectionKeeper.GetConnection(ctx, connectionID) if !found { - return "", nil, sdkerrors.Wrapf(connectiontypes.ErrConnectionNotFound, "connection-id: %s", connectionID) + return "", nil, errorsmod.Wrapf(connectiontypes.ErrConnectionNotFound, "connection-id: %s", connectionID) } return connectionID, connection, nil @@ -451,8 +479,8 @@ func (k Keeper) LookupModuleByChannel(ctx sdk.Context, portID, channelID string) } // common functionality for IteratePacketCommitment and IteratePacketAcknowledgement -func (k Keeper) iterateHashes(_ sdk.Context, iterator db.Iterator, cb func(portID, channelID string, sequence uint64, hash []byte) bool) { - defer iterator.Close() +func (k Keeper) iterateHashes(ctx sdk.Context, iterator db.Iterator, cb func(portID, channelID string, sequence uint64, hash []byte) bool) { + defer sdk.LogDeferred(ctx.Logger(), func() error { return iterator.Close() }) for ; iterator.Valid(); iterator.Next() { keySplit := strings.Split(string(iterator.Key()), "/") diff --git a/modules/core/04-channel/keeper/keeper_test.go b/modules/core/04-channel/keeper/keeper_test.go index f202d8bce78..92afaba9135 100644 --- a/modules/core/04-channel/keeper/keeper_test.go +++ b/modules/core/04-channel/keeper/keeper_test.go @@ -1,13 +1,15 @@ package keeper_test import ( + "reflect" "testing" "github.com/stretchr/testify/suite" - "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" + transfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" + "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + ibctesting "github.com/cosmos/ibc-go/v7/testing" + ibcmock "github.com/cosmos/ibc-go/v7/testing/mock" ) // KeeperTestSuite is a testing suite to test keeper functions. @@ -44,7 +46,7 @@ func (suite *KeeperTestSuite) TestSetChannel() { suite.coordinator.SetupConnections(path) // check for channel to be created on chainA - _, found := suite.chainA.App.GetIBCKeeper().ChannelKeeper.GetChannel(suite.chainA.GetContext(), path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) + found := suite.chainA.App.GetIBCKeeper().ChannelKeeper.HasChannel(suite.chainA.GetContext(), path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) suite.False(found) path.SetChannelOrdered() @@ -81,9 +83,89 @@ func (suite *KeeperTestSuite) TestGetAppVersion() { suite.Require().Equal(ibcmock.Version, channelVersion) } +// TestGetAllChannelsWithPortPrefix verifies ports are filtered correctly using a port prefix. +func (suite *KeeperTestSuite) TestGetAllChannelsWithPortPrefix() { + const ( + secondChannelID = "channel-1" + differentChannelPortID = "different-portid" + ) + + allChannels := []types.IdentifiedChannel{ + types.NewIdentifiedChannel(transfertypes.PortID, ibctesting.FirstChannelID, types.Channel{}), + types.NewIdentifiedChannel(differentChannelPortID, secondChannelID, types.Channel{}), + } + + tests := []struct { + name string + prefix string + allChannels []types.IdentifiedChannel + expectedChannels []types.IdentifiedChannel + }{ + { + name: "transfer channel is retrieved with prefix", + prefix: "tra", + allChannels: allChannels, + expectedChannels: []types.IdentifiedChannel{types.NewIdentifiedChannel(transfertypes.PortID, ibctesting.FirstChannelID, types.Channel{})}, + }, + { + name: "matches port with full name as prefix", + prefix: transfertypes.PortID, + allChannels: allChannels, + expectedChannels: []types.IdentifiedChannel{types.NewIdentifiedChannel(transfertypes.PortID, ibctesting.FirstChannelID, types.Channel{})}, + }, + { + name: "no ports match prefix", + prefix: "wont-match-anything", + allChannels: allChannels, + expectedChannels: nil, + }, + { + name: "empty prefix matches everything", + prefix: "", + allChannels: allChannels, + expectedChannels: allChannels, + }, + } + + for _, tc := range tests { + tc := tc + suite.Run(tc.name, func() { + suite.SetupTest() + + for _, ch := range tc.allChannels { + suite.chainA.GetSimApp().GetIBCKeeper().ChannelKeeper.SetChannel(suite.chainA.GetContext(), ch.PortId, ch.ChannelId, types.Channel{}) + } + + ctxA := suite.chainA.GetContext() + + actualChannels := suite.chainA.GetSimApp().GetIBCKeeper().ChannelKeeper.GetAllChannelsWithPortPrefix(ctxA, tc.prefix) + + suite.Require().True(containsAll(tc.expectedChannels, actualChannels)) + }) + } +} + +// containsAll verifies if all elements in the expected slice exist in the actual slice +// independent of order. +func containsAll(expected, actual []types.IdentifiedChannel) bool { + for _, expectedChannel := range expected { + foundMatch := false + for _, actualChannel := range actual { + if reflect.DeepEqual(actualChannel, expectedChannel) { + foundMatch = true + break + } + } + if !foundMatch { + return false + } + } + return true +} + // TestGetAllChannels creates multiple channels on chain A through various connections // and tests their retrieval. 2 channels are on connA0 and 1 channel is on connA1 -func (suite KeeperTestSuite) TestGetAllChannels() { +func (suite KeeperTestSuite) TestGetAllChannels() { //nolint:govet // this is a test, we are okay with copying locks path := ibctesting.NewPath(suite.chainA, suite.chainB) suite.coordinator.Setup(path) // channel0 on first connection on chainA @@ -147,7 +229,7 @@ func (suite KeeperTestSuite) TestGetAllChannels() { // TestGetAllSequences sets all packet sequences for two different channels on chain A and // tests their retrieval. -func (suite KeeperTestSuite) TestGetAllSequences() { +func (suite KeeperTestSuite) TestGetAllSequences() { //nolint:govet // this is a test, we are okay with copying locks path := ibctesting.NewPath(suite.chainA, suite.chainB) suite.coordinator.Setup(path) @@ -189,7 +271,7 @@ func (suite KeeperTestSuite) TestGetAllSequences() { // TestGetAllPacketState creates a set of acks, packet commitments, and receipts on two different // channels on chain A and tests their retrieval. -func (suite KeeperTestSuite) TestGetAllPacketState() { +func (suite KeeperTestSuite) TestGetAllPacketState() { //nolint:govet // this is a test, we are okay with copying locks path := ibctesting.NewPath(suite.chainA, suite.chainB) suite.coordinator.Setup(path) diff --git a/modules/core/04-channel/keeper/packet.go b/modules/core/04-channel/keeper/packet.go index 551e02e8b78..c69cdfd6554 100644 --- a/modules/core/04-channel/keeper/packet.go +++ b/modules/core/04-channel/keeper/packet.go @@ -5,139 +5,115 @@ import ( "strconv" "time" + errorsmod "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - connectiontypes "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types" - "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" - "github.com/cosmos/ibc-go/v4/modules/core/exported" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + connectiontypes "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" + "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + "github.com/cosmos/ibc-go/v7/modules/core/exported" ) -// SendPacket is called by a module in order to send an IBC packet on a channel -// end owned by the calling module to the corresponding module on the counterparty -// chain. +// SendPacket is called by a module in order to send an IBC packet on a channel. +// The packet sequence generated for the packet to be sent is returned. An error +// is returned if one occurs. func (k Keeper) SendPacket( ctx sdk.Context, channelCap *capabilitytypes.Capability, - packet exported.PacketI, -) error { - if err := packet.ValidateBasic(); err != nil { - return sdkerrors.Wrap(err, "packet failed basic validation") - } - - channel, found := k.GetChannel(ctx, packet.GetSourcePort(), packet.GetSourceChannel()) + sourcePort string, + sourceChannel string, + timeoutHeight clienttypes.Height, + timeoutTimestamp uint64, + data []byte, +) (uint64, error) { + channel, found := k.GetChannel(ctx, sourcePort, sourceChannel) if !found { - return sdkerrors.Wrap(types.ErrChannelNotFound, packet.GetSourceChannel()) + return 0, errorsmod.Wrap(types.ErrChannelNotFound, sourceChannel) } - if channel.State == types.CLOSED { - return sdkerrors.Wrapf( + if channel.State != types.OPEN { + return 0, errorsmod.Wrapf( types.ErrInvalidChannelState, - "channel is CLOSED (got %s)", channel.State.String(), + "channel is not OPEN (got %s)", channel.State.String(), ) } - if !k.scopedKeeper.AuthenticateCapability(ctx, channelCap, host.ChannelCapabilityPath(packet.GetSourcePort(), packet.GetSourceChannel())) { - return sdkerrors.Wrapf(types.ErrChannelCapabilityNotFound, "caller does not own capability for channel, port ID (%s) channel ID (%s)", packet.GetSourcePort(), packet.GetSourceChannel()) + if !k.scopedKeeper.AuthenticateCapability(ctx, channelCap, host.ChannelCapabilityPath(sourcePort, sourceChannel)) { + return 0, errorsmod.Wrapf(types.ErrChannelCapabilityNotFound, "caller does not own capability for channel, port ID (%s) channel ID (%s)", sourcePort, sourceChannel) } - if packet.GetDestPort() != channel.Counterparty.PortId { - return sdkerrors.Wrapf( - types.ErrInvalidPacket, - "packet destination port doesn't match the counterparty's port (%s ≠ %s)", packet.GetDestPort(), channel.Counterparty.PortId, + sequence, found := k.GetNextSequenceSend(ctx, sourcePort, sourceChannel) + if !found { + return 0, errorsmod.Wrapf( + types.ErrSequenceSendNotFound, + "source port: %s, source channel: %s", sourcePort, sourceChannel, ) } - if packet.GetDestChannel() != channel.Counterparty.ChannelId { - return sdkerrors.Wrapf( - types.ErrInvalidPacket, - "packet destination channel doesn't match the counterparty's channel (%s ≠ %s)", packet.GetDestChannel(), channel.Counterparty.ChannelId, - ) + // construct packet from given fields and channel state + packet := types.NewPacket(data, sequence, sourcePort, sourceChannel, + channel.Counterparty.PortId, channel.Counterparty.ChannelId, timeoutHeight, timeoutTimestamp) + + if err := packet.ValidateBasic(); err != nil { + return 0, errorsmod.Wrap(err, "constructed packet failed basic validation") } connectionEnd, found := k.connectionKeeper.GetConnection(ctx, channel.ConnectionHops[0]) if !found { - return sdkerrors.Wrap(connectiontypes.ErrConnectionNotFound, channel.ConnectionHops[0]) + return 0, errorsmod.Wrap(connectiontypes.ErrConnectionNotFound, channel.ConnectionHops[0]) } clientState, found := k.clientKeeper.GetClientState(ctx, connectionEnd.GetClientID()) if !found { - return clienttypes.ErrConsensusStateNotFound + return 0, clienttypes.ErrConsensusStateNotFound } // prevent accidental sends with clients that cannot be updated clientStore := k.clientKeeper.ClientStore(ctx, connectionEnd.GetClientID()) if status := clientState.Status(ctx, clientStore, k.cdc); status != exported.Active { - return sdkerrors.Wrapf(clienttypes.ErrClientNotActive, "cannot send packet using client (%s) with status %s", connectionEnd.GetClientID(), status) + return 0, errorsmod.Wrapf(clienttypes.ErrClientNotActive, "cannot send packet using client (%s) with status %s", connectionEnd.GetClientID(), status) } // check if packet is timed out on the receiving chain latestHeight := clientState.GetLatestHeight() - timeoutHeight := packet.GetTimeoutHeight() if !timeoutHeight.IsZero() && latestHeight.GTE(timeoutHeight) { - return sdkerrors.Wrapf( + return 0, errorsmod.Wrapf( types.ErrPacketTimeout, "receiving chain block height >= packet timeout height (%s >= %s)", latestHeight, timeoutHeight, ) } - clientType, _, err := clienttypes.ParseClientIdentifier(connectionEnd.GetClientID()) + latestTimestamp, err := k.connectionKeeper.GetTimestampAtHeight(ctx, connectionEnd, latestHeight) if err != nil { - return err - } - - // NOTE: this is a temporary fix. Solo machine does not support usage of 'GetTimestampAtHeight' - // A future change should move this function to be a ClientState callback. - if clientType != exported.Solomachine { - latestTimestamp, err := k.connectionKeeper.GetTimestampAtHeight(ctx, connectionEnd, latestHeight) - if err != nil { - return err - } - - if packet.GetTimeoutTimestamp() != 0 && latestTimestamp >= packet.GetTimeoutTimestamp() { - return sdkerrors.Wrapf( - types.ErrPacketTimeout, - "receiving chain block timestamp >= packet timeout timestamp (%s >= %s)", time.Unix(0, int64(latestTimestamp)), time.Unix(0, int64(packet.GetTimeoutTimestamp())), - ) - } - } - - nextSequenceSend, found := k.GetNextSequenceSend(ctx, packet.GetSourcePort(), packet.GetSourceChannel()) - if !found { - return sdkerrors.Wrapf( - types.ErrSequenceSendNotFound, - "source port: %s, source channel: %s", packet.GetSourcePort(), packet.GetSourceChannel(), - ) + return 0, err } - if packet.GetSequence() != nextSequenceSend { - return sdkerrors.Wrapf( - types.ErrInvalidPacket, - "packet sequence ≠ next send sequence (%d ≠ %d)", packet.GetSequence(), nextSequenceSend, + if packet.GetTimeoutTimestamp() != 0 && latestTimestamp >= packet.GetTimeoutTimestamp() { + return 0, errorsmod.Wrapf( + types.ErrPacketTimeout, + "receiving chain block timestamp >= packet timeout timestamp (%s >= %s)", time.Unix(0, int64(latestTimestamp)), time.Unix(0, int64(packet.GetTimeoutTimestamp())), ) } commitment := types.CommitPacket(k.cdc, packet) - nextSequenceSend++ - k.SetNextSequenceSend(ctx, packet.GetSourcePort(), packet.GetSourceChannel(), nextSequenceSend) - k.SetPacketCommitment(ctx, packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence(), commitment) + k.SetNextSequenceSend(ctx, sourcePort, sourceChannel, sequence+1) + k.SetPacketCommitment(ctx, sourcePort, sourceChannel, packet.GetSequence(), commitment) - EmitSendPacketEvent(ctx, packet, channel, timeoutHeight) + emitSendPacketEvent(ctx, packet, channel, timeoutHeight) k.Logger(ctx).Info( "packet sent", "sequence", strconv.FormatUint(packet.GetSequence(), 10), - "src_port", packet.GetSourcePort(), - "src_channel", packet.GetSourceChannel(), + "src_port", sourcePort, + "src_channel", sourceChannel, "dst_port", packet.GetDestPort(), "dst_channel", packet.GetDestChannel(), ) - return nil + return packet.GetSequence(), nil } // RecvPacket is called by a module in order to receive & process an IBC packet @@ -151,11 +127,11 @@ func (k Keeper) RecvPacket( ) error { channel, found := k.GetChannel(ctx, packet.GetDestPort(), packet.GetDestChannel()) if !found { - return sdkerrors.Wrap(types.ErrChannelNotFound, packet.GetDestChannel()) + return errorsmod.Wrap(types.ErrChannelNotFound, packet.GetDestChannel()) } if channel.State != types.OPEN { - return sdkerrors.Wrapf( + return errorsmod.Wrapf( types.ErrInvalidChannelState, "channel state is not OPEN (got %s)", channel.State.String(), ) @@ -164,7 +140,7 @@ func (k Keeper) RecvPacket( // Authenticate capability to ensure caller has authority to receive packet on this channel capName := host.ChannelCapabilityPath(packet.GetDestPort(), packet.GetDestChannel()) if !k.scopedKeeper.AuthenticateCapability(ctx, chanCap, capName) { - return sdkerrors.Wrapf( + return errorsmod.Wrapf( types.ErrInvalidChannelCapability, "channel capability failed authentication for capability name %s", capName, ) @@ -172,14 +148,14 @@ func (k Keeper) RecvPacket( // packet must come from the channel's counterparty if packet.GetSourcePort() != channel.Counterparty.PortId { - return sdkerrors.Wrapf( + return errorsmod.Wrapf( types.ErrInvalidPacket, "packet source port doesn't match the counterparty's port (%s ≠ %s)", packet.GetSourcePort(), channel.Counterparty.PortId, ) } if packet.GetSourceChannel() != channel.Counterparty.ChannelId { - return sdkerrors.Wrapf( + return errorsmod.Wrapf( types.ErrInvalidPacket, "packet source channel doesn't match the counterparty's channel (%s ≠ %s)", packet.GetSourceChannel(), channel.Counterparty.ChannelId, ) @@ -190,11 +166,11 @@ func (k Keeper) RecvPacket( // connection and channel must both be open connectionEnd, found := k.connectionKeeper.GetConnection(ctx, channel.ConnectionHops[0]) if !found { - return sdkerrors.Wrap(connectiontypes.ErrConnectionNotFound, channel.ConnectionHops[0]) + return errorsmod.Wrap(connectiontypes.ErrConnectionNotFound, channel.ConnectionHops[0]) } if connectionEnd.GetState() != int32(connectiontypes.OPEN) { - return sdkerrors.Wrapf( + return errorsmod.Wrapf( connectiontypes.ErrInvalidConnectionState, "connection state is not OPEN (got %s)", connectiontypes.State(connectionEnd.GetState()).String(), ) @@ -204,7 +180,7 @@ func (k Keeper) RecvPacket( selfHeight := clienttypes.GetSelfHeight(ctx) timeoutHeight := packet.GetTimeoutHeight() if !timeoutHeight.IsZero() && selfHeight.GTE(timeoutHeight) { - return sdkerrors.Wrapf( + return errorsmod.Wrapf( types.ErrPacketTimeout, "block height >= packet timeout height (%s >= %s)", selfHeight, timeoutHeight, ) @@ -212,7 +188,7 @@ func (k Keeper) RecvPacket( // check if packet timeouted by comparing it with the latest timestamp of the chain if packet.GetTimeoutTimestamp() != 0 && uint64(ctx.BlockTime().UnixNano()) >= packet.GetTimeoutTimestamp() { - return sdkerrors.Wrapf( + return errorsmod.Wrapf( types.ErrPacketTimeout, "block timestamp >= packet timeout timestamp (%s >= %s)", ctx.BlockTime(), time.Unix(0, int64(packet.GetTimeoutTimestamp())), ) @@ -226,7 +202,7 @@ func (k Keeper) RecvPacket( packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence(), commitment, ); err != nil { - return sdkerrors.Wrap(err, "couldn't verify counterparty packet commitment") + return errorsmod.Wrap(err, "couldn't verify counterparty packet commitment") } switch channel.Ordering { @@ -234,7 +210,7 @@ func (k Keeper) RecvPacket( // check if the packet receipt has been received already for unordered channels _, found := k.GetPacketReceipt(ctx, packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()) if found { - EmitRecvPacketEvent(ctx, packet, channel) + emitRecvPacketEvent(ctx, packet, channel) // This error indicates that the packet has already been relayed. Core IBC will // treat this error as a no-op in order to prevent an entire relay transaction // from failing and consuming unnecessary fees. @@ -251,14 +227,14 @@ func (k Keeper) RecvPacket( // check if the packet is being received in order nextSequenceRecv, found := k.GetNextSequenceRecv(ctx, packet.GetDestPort(), packet.GetDestChannel()) if !found { - return sdkerrors.Wrapf( + return errorsmod.Wrapf( types.ErrSequenceReceiveNotFound, "destination port: %s, destination channel: %s", packet.GetDestPort(), packet.GetDestChannel(), ) } if packet.GetSequence() < nextSequenceRecv { - EmitRecvPacketEvent(ctx, packet, channel) + emitRecvPacketEvent(ctx, packet, channel) // This error indicates that the packet has already been relayed. Core IBC will // treat this error as a no-op in order to prevent an entire relay transaction // from failing and consuming unnecessary fees. @@ -266,7 +242,7 @@ func (k Keeper) RecvPacket( } if packet.GetSequence() != nextSequenceRecv { - return sdkerrors.Wrapf( + return errorsmod.Wrapf( types.ErrPacketSequenceOutOfOrder, "packet sequence ≠ next receive sequence (%d ≠ %d)", packet.GetSequence(), nextSequenceRecv, ) @@ -293,7 +269,7 @@ func (k Keeper) RecvPacket( ) // emit an event that the relayer can query for - EmitRecvPacketEvent(ctx, packet, channel) + emitRecvPacketEvent(ctx, packet, channel) return nil } @@ -317,11 +293,11 @@ func (k Keeper) WriteAcknowledgement( ) error { channel, found := k.GetChannel(ctx, packet.GetDestPort(), packet.GetDestChannel()) if !found { - return sdkerrors.Wrap(types.ErrChannelNotFound, packet.GetDestChannel()) + return errorsmod.Wrap(types.ErrChannelNotFound, packet.GetDestChannel()) } if channel.State != types.OPEN { - return sdkerrors.Wrapf( + return errorsmod.Wrapf( types.ErrInvalidChannelState, "channel state is not OPEN (got %s)", channel.State.String(), ) @@ -330,7 +306,7 @@ func (k Keeper) WriteAcknowledgement( // Authenticate capability to ensure caller has authority to receive packet on this channel capName := host.ChannelCapabilityPath(packet.GetDestPort(), packet.GetDestChannel()) if !k.scopedKeeper.AuthenticateCapability(ctx, chanCap, capName) { - return sdkerrors.Wrapf( + return errorsmod.Wrapf( types.ErrInvalidChannelCapability, "channel capability failed authentication for capability name %s", capName, ) @@ -344,12 +320,12 @@ func (k Keeper) WriteAcknowledgement( } if acknowledgement == nil { - return sdkerrors.Wrap(types.ErrInvalidAcknowledgement, "acknowledgement cannot be nil") + return errorsmod.Wrap(types.ErrInvalidAcknowledgement, "acknowledgement cannot be nil") } bz := acknowledgement.Acknowledgement() if len(bz) == 0 { - return sdkerrors.Wrap(types.ErrInvalidAcknowledgement, "acknowledgement cannot be empty") + return errorsmod.Wrap(types.ErrInvalidAcknowledgement, "acknowledgement cannot be empty") } // set the acknowledgement so that it can be verified on the other side @@ -361,14 +337,14 @@ 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(), "dst_channel", packet.GetDestChannel(), ) - EmitWriteAcknowledgementEvent(ctx, packet, channel, bz) + emitWriteAcknowledgementEvent(ctx, packet, channel, bz) return nil } @@ -389,14 +365,14 @@ func (k Keeper) AcknowledgePacket( ) error { channel, found := k.GetChannel(ctx, packet.GetSourcePort(), packet.GetSourceChannel()) if !found { - return sdkerrors.Wrapf( + return errorsmod.Wrapf( types.ErrChannelNotFound, "port ID (%s) channel ID (%s)", packet.GetSourcePort(), packet.GetSourceChannel(), ) } if channel.State != types.OPEN { - return sdkerrors.Wrapf( + return errorsmod.Wrapf( types.ErrInvalidChannelState, "channel state is not OPEN (got %s)", channel.State.String(), ) @@ -405,7 +381,7 @@ func (k Keeper) AcknowledgePacket( // Authenticate capability to ensure caller has authority to receive packet on this channel capName := host.ChannelCapabilityPath(packet.GetSourcePort(), packet.GetSourceChannel()) if !k.scopedKeeper.AuthenticateCapability(ctx, chanCap, capName) { - return sdkerrors.Wrapf( + return errorsmod.Wrapf( types.ErrInvalidChannelCapability, "channel capability failed authentication for capability name %s", capName, ) @@ -413,14 +389,14 @@ func (k Keeper) AcknowledgePacket( // packet must have been sent to the channel's counterparty if packet.GetDestPort() != channel.Counterparty.PortId { - return sdkerrors.Wrapf( + return errorsmod.Wrapf( types.ErrInvalidPacket, "packet destination port doesn't match the counterparty's port (%s ≠ %s)", packet.GetDestPort(), channel.Counterparty.PortId, ) } if packet.GetDestChannel() != channel.Counterparty.ChannelId { - return sdkerrors.Wrapf( + return errorsmod.Wrapf( types.ErrInvalidPacket, "packet destination channel doesn't match the counterparty's channel (%s ≠ %s)", packet.GetDestChannel(), channel.Counterparty.ChannelId, ) @@ -428,11 +404,11 @@ func (k Keeper) AcknowledgePacket( connectionEnd, found := k.connectionKeeper.GetConnection(ctx, channel.ConnectionHops[0]) if !found { - return sdkerrors.Wrap(connectiontypes.ErrConnectionNotFound, channel.ConnectionHops[0]) + return errorsmod.Wrap(connectiontypes.ErrConnectionNotFound, channel.ConnectionHops[0]) } if connectionEnd.GetState() != int32(connectiontypes.OPEN) { - return sdkerrors.Wrapf( + return errorsmod.Wrapf( connectiontypes.ErrInvalidConnectionState, "connection state is not OPEN (got %s)", connectiontypes.State(connectionEnd.GetState()).String(), ) @@ -441,7 +417,7 @@ func (k Keeper) AcknowledgePacket( commitment := k.GetPacketCommitment(ctx, packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) if len(commitment) == 0 { - EmitAcknowledgePacketEvent(ctx, packet, channel) + emitAcknowledgePacketEvent(ctx, packet, channel) // This error indicates that the acknowledgement has already been relayed // or there is a misconfigured relayer attempting to prove an acknowledgement // for a packet never sent. Core IBC will treat this error as a no-op in order to @@ -453,7 +429,7 @@ func (k Keeper) AcknowledgePacket( // verify we sent the packet and haven't cleared it out yet if !bytes.Equal(commitment, packetCommitment) { - return sdkerrors.Wrapf(types.ErrInvalidPacket, "commitment bytes are not equal: got (%v), expected (%v)", packetCommitment, commitment) + return errorsmod.Wrapf(types.ErrInvalidPacket, "commitment bytes are not equal: got (%v), expected (%v)", packetCommitment, commitment) } if err := k.connectionKeeper.VerifyPacketAcknowledgement( @@ -467,14 +443,14 @@ func (k Keeper) AcknowledgePacket( if channel.Ordering == types.ORDERED { nextSequenceAck, found := k.GetNextSequenceAck(ctx, packet.GetSourcePort(), packet.GetSourceChannel()) if !found { - return sdkerrors.Wrapf( + return errorsmod.Wrapf( types.ErrSequenceAckNotFound, "source port: %s, source channel: %s", packet.GetSourcePort(), packet.GetSourceChannel(), ) } if packet.GetSequence() != nextSequenceAck { - return sdkerrors.Wrapf( + return errorsmod.Wrapf( types.ErrPacketSequenceOutOfOrder, "packet sequence ≠ next ack sequence (%d ≠ %d)", packet.GetSequence(), nextSequenceAck, ) @@ -503,7 +479,7 @@ func (k Keeper) AcknowledgePacket( ) // emit an event marking that we have processed the acknowledgement - EmitAcknowledgePacketEvent(ctx, packet, channel) + emitAcknowledgePacketEvent(ctx, packet, channel) return nil } diff --git a/modules/core/04-channel/keeper/packet_test.go b/modules/core/04-channel/keeper/packet_test.go index 0000edda0e7..64a1154a2a2 100644 --- a/modules/core/04-channel/keeper/packet_test.go +++ b/modules/core/04-channel/keeper/packet_test.go @@ -4,56 +4,59 @@ import ( "errors" "fmt" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + errorsmod "cosmossdk.io/errors" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - connectiontypes "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types" - "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" - "github.com/cosmos/ibc-go/v4/modules/core/exported" - ibctmtypes "github.com/cosmos/ibc-go/v4/modules/light-clients/07-tendermint/types" - ibctesting "github.com/cosmos/ibc-go/v4/testing" - ibcmock "github.com/cosmos/ibc-go/v4/testing/mock" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + connectiontypes "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" + "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + "github.com/cosmos/ibc-go/v7/modules/core/exported" + ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" + ibctesting "github.com/cosmos/ibc-go/v7/testing" + ibcmock "github.com/cosmos/ibc-go/v7/testing/mock" ) var ( disabledTimeoutTimestamp = uint64(0) disabledTimeoutHeight = clienttypes.ZeroHeight() - timeoutHeight = clienttypes.NewHeight(0, 100) + defaultTimeoutHeight = clienttypes.NewHeight(1, 100) // for when the testing package cannot be used - clientIDA = "clientA" - clientIDB = "clientB" - connIDA = "connA" - connIDB = "connB" - portID = "portid" - channelIDA = "channelidA" - channelIDB = "channelidB" + connIDA = "connA" + connIDB = "connB" ) // TestSendPacket tests SendPacket from chainA to chainB func (suite *KeeperTestSuite) TestSendPacket() { var ( - path *ibctesting.Path - packet exported.PacketI - channelCap *capabilitytypes.Capability + path *ibctesting.Path + sourcePort string + sourceChannel string + packetData []byte + timeoutHeight clienttypes.Height + timeoutTimestamp uint64 + channelCap *capabilitytypes.Capability ) testCases := []testCase{ {"success: UNORDERED channel", func() { suite.coordinator.Setup(path) - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, disabledTimeoutTimestamp) + sourceChannel = path.EndpointA.ChannelID + channelCap = suite.chainA.GetChannelCapability(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) }, true}, {"success: ORDERED channel", func() { path.SetChannelOrdered() suite.coordinator.Setup(path) - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, disabledTimeoutTimestamp) + sourceChannel = path.EndpointA.ChannelID + channelCap = suite.chainA.GetChannelCapability(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) }, true}, {"success with solomachine: UNORDERED channel", func() { suite.coordinator.Setup(path) + sourceChannel = path.EndpointA.ChannelID + // swap client with solo machine solomachine := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachinesingle", "testing", 1) path.EndpointA.ClientID = clienttypes.FormatClientIdentifier(exported.Solomachine, 10) @@ -62,12 +65,13 @@ func (suite *KeeperTestSuite) TestSendPacket() { connection.ClientId = path.EndpointA.ClientID path.EndpointA.SetConnection(connection) - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, disabledTimeoutTimestamp) channelCap = suite.chainA.GetChannelCapability(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) }, true}, {"success with solomachine: ORDERED channel", func() { path.SetChannelOrdered() suite.coordinator.Setup(path) + sourceChannel = path.EndpointA.ChannelID + // swap client with solomachine solomachine := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachinesingle", "testing", 1) path.EndpointA.ClientID = clienttypes.FormatClientIdentifier(exported.Solomachine, 10) @@ -76,111 +80,128 @@ func (suite *KeeperTestSuite) TestSendPacket() { connection.ClientId = path.EndpointA.ClientID path.EndpointA.SetConnection(connection) - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, disabledTimeoutTimestamp) channelCap = suite.chainA.GetChannelCapability(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) }, true}, - {"sending packet out of order on UNORDERED channel", func() { - // setup creates an unordered channel - suite.coordinator.Setup(path) - packet = types.NewPacket(ibctesting.MockPacketData, 5, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, disabledTimeoutTimestamp) - channelCap = suite.chainA.GetChannelCapability(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) - }, false}, - {"sending packet out of order on ORDERED channel", func() { - path.SetChannelOrdered() - suite.coordinator.Setup(path) - packet = types.NewPacket(ibctesting.MockPacketData, 5, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, disabledTimeoutTimestamp) - channelCap = suite.chainA.GetChannelCapability(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) - }, false}, {"packet basic validation failed, empty packet data", func() { suite.coordinator.Setup(path) - packet = types.NewPacket([]byte{}, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, disabledTimeoutTimestamp) + sourceChannel = path.EndpointA.ChannelID + channelCap = suite.chainA.GetChannelCapability(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) + packetData = []byte{} }, false}, {"channel not found", func() { // use wrong channel naming suite.coordinator.Setup(path) - packet = types.NewPacket(ibctesting.MockPacketData, 1, ibctesting.InvalidID, ibctesting.InvalidID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, disabledTimeoutTimestamp) + sourceChannel = ibctesting.InvalidID channelCap = suite.chainA.GetChannelCapability(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) }, false}, - {"channel closed", func() { + {"channel is in CLOSED state", func() { suite.coordinator.Setup(path) - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, disabledTimeoutTimestamp) + sourceChannel = path.EndpointA.ChannelID - err := path.EndpointA.SetChannelClosed() + err := path.EndpointA.SetChannelState(types.CLOSED) suite.Require().NoError(err) }, false}, - {"packet dest port ≠ channel counterparty port", func() { + {"channel is in INIT state", func() { suite.coordinator.Setup(path) - // use wrong port for dest - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, ibctesting.InvalidID, path.EndpointB.ChannelID, timeoutHeight, disabledTimeoutTimestamp) - channelCap = suite.chainA.GetChannelCapability(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) + sourceChannel = path.EndpointA.ChannelID + + err := path.EndpointA.SetChannelState(types.INIT) + suite.Require().NoError(err) }, false}, - {"packet dest channel ID ≠ channel counterparty channel ID", func() { + {"channel is in TRYOPEN stage", func() { suite.coordinator.Setup(path) - // use wrong channel for dest - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, ibctesting.InvalidID, timeoutHeight, disabledTimeoutTimestamp) - channelCap = suite.chainA.GetChannelCapability(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) + sourceChannel = path.EndpointA.ChannelID + + err := path.EndpointA.SetChannelState(types.TRYOPEN) + suite.Require().NoError(err) }, false}, {"connection not found", func() { // pass channel check - suite.chainA.App.GetIBCKeeper().ChannelKeeper.SetChannel( - suite.chainA.GetContext(), - path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, - types.NewChannel(types.OPEN, types.ORDERED, types.NewCounterparty(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID), []string{connIDA}, path.EndpointA.ChannelConfig.Version), - ) - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, disabledTimeoutTimestamp) - suite.chainA.CreateChannelCapability(suite.chainA.GetSimApp().ScopedIBCMockKeeper, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) + suite.coordinator.Setup(path) + sourceChannel = path.EndpointA.ChannelID + + channel := path.EndpointA.GetChannel() + channel.ConnectionHops[0] = "invalid-connection" + path.EndpointA.SetChannel(channel) + channelCap = suite.chainA.GetChannelCapability(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) }, false}, {"client state not found", func() { suite.coordinator.Setup(path) + sourceChannel = path.EndpointA.ChannelID // change connection client ID connection := path.EndpointA.GetConnection() connection.ClientId = ibctesting.InvalidID suite.chainA.App.GetIBCKeeper().ConnectionKeeper.SetConnection(suite.chainA.GetContext(), path.EndpointA.ConnectionID, connection) - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, disabledTimeoutTimestamp) channelCap = suite.chainA.GetChannelCapability(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) }, false}, {"client state is frozen", func() { suite.coordinator.Setup(path) + sourceChannel = path.EndpointA.ChannelID connection := path.EndpointA.GetConnection() clientState := path.EndpointA.GetClientState() - cs, ok := clientState.(*ibctmtypes.ClientState) + cs, ok := clientState.(*ibctm.ClientState) suite.Require().True(ok) // freeze client cs.FrozenHeight = clienttypes.NewHeight(0, 1) suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), connection.ClientId, cs) - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, disabledTimeoutTimestamp) channelCap = suite.chainA.GetChannelCapability(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) }, false}, {"timeout height passed", func() { suite.coordinator.Setup(path) + sourceChannel = path.EndpointA.ChannelID + // use client state latest height for timeout clientState := path.EndpointA.GetClientState() - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, clientState.GetLatestHeight().(clienttypes.Height), disabledTimeoutTimestamp) + timeoutHeight = clientState.GetLatestHeight().(clienttypes.Height) channelCap = suite.chainA.GetChannelCapability(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) }, false}, {"timeout timestamp passed", func() { suite.coordinator.Setup(path) + sourceChannel = path.EndpointA.ChannelID + // use latest time on client state clientState := path.EndpointA.GetClientState() connection := path.EndpointA.GetConnection() timestamp, err := suite.chainA.App.GetIBCKeeper().ConnectionKeeper.GetTimestampAtHeight(suite.chainA.GetContext(), connection, clientState.GetLatestHeight()) suite.Require().NoError(err) - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, disabledTimeoutHeight, timestamp) + timeoutHeight = disabledTimeoutHeight + timeoutTimestamp = timestamp + channelCap = suite.chainA.GetChannelCapability(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) + }, false}, + {"timeout timestamp passed with solomachine", func() { + suite.coordinator.Setup(path) + // swap client with solomachine + solomachine := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachinesingle", "testing", 1) + path.EndpointA.ClientID = clienttypes.FormatClientIdentifier(exported.Solomachine, 10) + path.EndpointA.SetClientState(solomachine.ClientState()) + connection := path.EndpointA.GetConnection() + connection.ClientId = path.EndpointA.ClientID + path.EndpointA.SetConnection(connection) + + clientState := path.EndpointA.GetClientState() + timestamp, err := suite.chainA.App.GetIBCKeeper().ConnectionKeeper.GetTimestampAtHeight(suite.chainA.GetContext(), connection, clientState.GetLatestHeight()) + suite.Require().NoError(err) + + sourceChannel = path.EndpointA.ChannelID + timeoutHeight = disabledTimeoutHeight + timeoutTimestamp = timestamp + channelCap = suite.chainA.GetChannelCapability(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) }, false}, {"next sequence send not found", func() { path := ibctesting.NewPath(suite.chainA, suite.chainB) + sourceChannel = path.EndpointA.ChannelID + suite.coordinator.SetupConnections(path) - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, disabledTimeoutTimestamp) // manually creating channel prevents next sequence from being set suite.chainA.App.GetIBCKeeper().ChannelKeeper.SetChannel( suite.chainA.GetContext(), @@ -190,15 +211,10 @@ func (suite *KeeperTestSuite) TestSendPacket() { suite.chainA.CreateChannelCapability(suite.chainA.GetSimApp().ScopedIBCMockKeeper, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) channelCap = suite.chainA.GetChannelCapability(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) }, false}, - {"next sequence wrong", func() { - suite.coordinator.Setup(path) - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, disabledTimeoutTimestamp) - suite.chainA.App.GetIBCKeeper().ChannelKeeper.SetNextSequenceSend(suite.chainA.GetContext(), path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, 5) - channelCap = suite.chainA.GetChannelCapability(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) - }, false}, {"channel capability not found", func() { suite.coordinator.Setup(path) - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, disabledTimeoutTimestamp) + sourceChannel = path.EndpointA.ChannelID + channelCap = capabilitytypes.NewCapability(5) }, false}, } @@ -209,12 +225,27 @@ func (suite *KeeperTestSuite) TestSendPacket() { suite.SetupTest() // reset path = ibctesting.NewPath(suite.chainA, suite.chainB) + // set default send packet arguments + // sourceChannel is set after path is setup + sourcePort = path.EndpointA.ChannelConfig.PortID + timeoutHeight = defaultTimeoutHeight + timeoutTimestamp = disabledTimeoutTimestamp + packetData = ibctesting.MockPacketData + + // malleate may modify send packet arguments above tc.malleate() - err := suite.chainA.App.GetIBCKeeper().ChannelKeeper.SendPacket(suite.chainA.GetContext(), channelCap, packet) + // only check if nextSequenceSend exists in no error case since it is a tested error case above. + expectedSequence, ok := suite.chainA.App.GetIBCKeeper().ChannelKeeper.GetNextSequenceSend(suite.chainA.GetContext(), sourcePort, sourceChannel) + + sequence, err := suite.chainA.App.GetIBCKeeper().ChannelKeeper.SendPacket(suite.chainA.GetContext(), channelCap, + sourcePort, sourceChannel, timeoutHeight, timeoutTimestamp, packetData) if tc.expPass { suite.Require().NoError(err) + // verify that the returned sequence matches expected value + suite.Require().True(ok) + suite.Require().Equal(expectedSequence, sequence, "send packet did not return the expected sequence of the outgoing packet") } else { suite.Require().Error(err) } @@ -230,7 +261,7 @@ func (suite *KeeperTestSuite) TestRecvPacket() { path *ibctesting.Path packet exported.PacketI channelCap *capabilitytypes.Capability - expError *sdkerrors.Error + expError *errorsmod.Error ) testCases := []testCase{ @@ -238,31 +269,28 @@ func (suite *KeeperTestSuite) TestRecvPacket() { path.SetChannelOrdered() suite.coordinator.Setup(path) - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, disabledTimeoutTimestamp) - err := path.EndpointA.SendPacket(packet) + sequence, err := path.EndpointA.SendPacket(defaultTimeoutHeight, disabledTimeoutTimestamp, ibctesting.MockPacketData) suite.Require().NoError(err) + packet = types.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, defaultTimeoutHeight, disabledTimeoutTimestamp) channelCap = suite.chainB.GetChannelCapability(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID) }, true}, {"success UNORDERED channel", func() { // setup uses an UNORDERED channel suite.coordinator.Setup(path) - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, disabledTimeoutTimestamp) - err := path.EndpointA.SendPacket(packet) + sequence, err := path.EndpointA.SendPacket(defaultTimeoutHeight, disabledTimeoutTimestamp, ibctesting.MockPacketData) suite.Require().NoError(err) + packet = types.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, defaultTimeoutHeight, disabledTimeoutTimestamp) channelCap = suite.chainB.GetChannelCapability(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID) }, true}, {"success with out of order packet: UNORDERED channel", func() { // setup uses an UNORDERED channel suite.coordinator.Setup(path) - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, disabledTimeoutTimestamp) - // send 2 packets - err := path.EndpointA.SendPacket(packet) + _, err := path.EndpointA.SendPacket(defaultTimeoutHeight, disabledTimeoutTimestamp, ibctesting.MockPacketData) suite.Require().NoError(err) - // set sequence to 2 - packet = types.NewPacket(ibctesting.MockPacketData, 2, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, disabledTimeoutTimestamp) - err = path.EndpointA.SendPacket(packet) + sequence, err := path.EndpointA.SendPacket(defaultTimeoutHeight, disabledTimeoutTimestamp, ibctesting.MockPacketData) suite.Require().NoError(err) + packet = types.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, defaultTimeoutHeight, disabledTimeoutTimestamp) // attempts to receive packet 2 without receiving packet 1 channelCap = suite.chainB.GetChannelCapability(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID) }, true}, @@ -272,11 +300,11 @@ func (suite *KeeperTestSuite) TestRecvPacket() { path.SetChannelOrdered() suite.coordinator.Setup(path) - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, disabledTimeoutTimestamp) - err := path.EndpointA.SendPacket(packet) + sequence, err := path.EndpointA.SendPacket(defaultTimeoutHeight, disabledTimeoutTimestamp, ibctesting.MockPacketData) suite.Require().NoError(err) channelCap = suite.chainB.GetChannelCapability(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID) + packet = types.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, defaultTimeoutHeight, disabledTimeoutTimestamp) err = path.EndpointB.RecvPacket(packet.(types.Packet)) suite.Require().NoError(err) }, false}, @@ -285,11 +313,11 @@ func (suite *KeeperTestSuite) TestRecvPacket() { // setup uses an UNORDERED channel suite.coordinator.Setup(path) - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, disabledTimeoutTimestamp) - err := path.EndpointA.SendPacket(packet) + sequence, err := path.EndpointA.SendPacket(defaultTimeoutHeight, disabledTimeoutTimestamp, ibctesting.MockPacketData) suite.Require().NoError(err) channelCap = suite.chainB.GetChannelCapability(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID) + packet = types.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, defaultTimeoutHeight, disabledTimeoutTimestamp) err = path.EndpointB.RecvPacket(packet.(types.Packet)) suite.Require().NoError(err) }, false}, @@ -298,15 +326,14 @@ func (suite *KeeperTestSuite) TestRecvPacket() { path.SetChannelOrdered() suite.coordinator.Setup(path) - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, disabledTimeoutTimestamp) + packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, defaultTimeoutHeight, disabledTimeoutTimestamp) // send 2 packets - err := path.EndpointA.SendPacket(packet) + _, err := path.EndpointA.SendPacket(defaultTimeoutHeight, disabledTimeoutTimestamp, ibctesting.MockPacketData) suite.Require().NoError(err) - // set sequence to 2 - packet = types.NewPacket(ibctesting.MockPacketData, 2, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, disabledTimeoutTimestamp) - err = path.EndpointA.SendPacket(packet) + sequence, err := path.EndpointA.SendPacket(defaultTimeoutHeight, disabledTimeoutTimestamp, ibctesting.MockPacketData) suite.Require().NoError(err) + packet = types.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, defaultTimeoutHeight, disabledTimeoutTimestamp) // attempts to receive packet 2 without receiving packet 1 channelCap = suite.chainB.GetChannelCapability(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID) }, false}, @@ -315,16 +342,16 @@ func (suite *KeeperTestSuite) TestRecvPacket() { // use wrong channel naming suite.coordinator.Setup(path) - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, ibctesting.InvalidID, ibctesting.InvalidID, timeoutHeight, disabledTimeoutTimestamp) + packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, ibctesting.InvalidID, ibctesting.InvalidID, defaultTimeoutHeight, disabledTimeoutTimestamp) channelCap = suite.chainB.GetChannelCapability(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID) }, false}, {"channel not open", func() { expError = types.ErrInvalidChannelState suite.coordinator.Setup(path) - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, disabledTimeoutTimestamp) + packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, defaultTimeoutHeight, disabledTimeoutTimestamp) - err := path.EndpointB.SetChannelClosed() + err := path.EndpointB.SetChannelState(types.CLOSED) suite.Require().NoError(err) channelCap = suite.chainB.GetChannelCapability(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID) }, false}, @@ -334,9 +361,9 @@ func (suite *KeeperTestSuite) TestRecvPacket() { path.SetChannelOrdered() suite.coordinator.Setup(path) - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, disabledTimeoutTimestamp) - err := path.EndpointA.SendPacket(packet) + sequence, err := path.EndpointA.SendPacket(defaultTimeoutHeight, disabledTimeoutTimestamp, ibctesting.MockPacketData) suite.Require().NoError(err) + packet = types.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, defaultTimeoutHeight, disabledTimeoutTimestamp) channelCap = capabilitytypes.NewCapability(3) }, false}, {"packet source port ≠ channel counterparty port", func() { @@ -344,7 +371,7 @@ func (suite *KeeperTestSuite) TestRecvPacket() { suite.coordinator.Setup(path) // use wrong port for dest - packet = types.NewPacket(ibctesting.MockPacketData, 1, ibctesting.InvalidID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, disabledTimeoutTimestamp) + packet = types.NewPacket(ibctesting.MockPacketData, 1, ibctesting.InvalidID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, defaultTimeoutHeight, disabledTimeoutTimestamp) channelCap = suite.chainB.GetChannelCapability(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID) }, false}, {"packet source channel ID ≠ channel counterparty channel ID", func() { @@ -352,7 +379,7 @@ func (suite *KeeperTestSuite) TestRecvPacket() { suite.coordinator.Setup(path) // use wrong port for dest - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, ibctesting.InvalidID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, disabledTimeoutTimestamp) + packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, ibctesting.InvalidID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, defaultTimeoutHeight, disabledTimeoutTimestamp) channelCap = suite.chainB.GetChannelCapability(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID) }, false}, {"connection not found", func() { @@ -365,7 +392,7 @@ func (suite *KeeperTestSuite) TestRecvPacket() { path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, types.NewChannel(types.OPEN, types.ORDERED, types.NewCounterparty(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID), []string{connIDB}, path.EndpointB.ChannelConfig.Version), ) - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, disabledTimeoutTimestamp) + packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, defaultTimeoutHeight, disabledTimeoutTimestamp) suite.chainB.CreateChannelCapability(suite.chainB.GetSimApp().ScopedIBCMockKeeper, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID) channelCap = suite.chainB.GetChannelCapability(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID) }, false}, @@ -383,7 +410,7 @@ func (suite *KeeperTestSuite) TestRecvPacket() { path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, types.NewChannel(types.OPEN, types.ORDERED, types.NewCounterparty(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID), []string{path.EndpointB.ConnectionID}, path.EndpointB.ChannelConfig.Version), ) - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, disabledTimeoutTimestamp) + packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, defaultTimeoutHeight, disabledTimeoutTimestamp) suite.chainB.CreateChannelCapability(suite.chainB.GetSimApp().ScopedIBCMockKeeper, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID) channelCap = suite.chainB.GetChannelCapability(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID) }, false}, @@ -415,7 +442,7 @@ func (suite *KeeperTestSuite) TestRecvPacket() { types.NewChannel(types.OPEN, types.ORDERED, types.NewCounterparty(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID), []string{path.EndpointB.ConnectionID}, path.EndpointB.ChannelConfig.Version), ) - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, disabledTimeoutTimestamp) + packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, defaultTimeoutHeight, disabledTimeoutTimestamp) // manually set packet commitment suite.chainA.App.GetIBCKeeper().ChannelKeeper.SetPacketCommitment(suite.chainA.GetContext(), path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, packet.GetSequence(), types.CommitPacket(suite.chainA.App.AppCodec(), packet)) @@ -423,16 +450,19 @@ func (suite *KeeperTestSuite) TestRecvPacket() { channelCap = suite.chainB.GetChannelCapability(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID) - path.EndpointA.UpdateClient() - path.EndpointB.UpdateClient() + err := path.EndpointA.UpdateClient() + suite.Require().NoError(err) + err = path.EndpointB.UpdateClient() + suite.Require().NoError(err) }, false}, {"receipt already stored", func() { expError = types.ErrNoOpMsg suite.coordinator.Setup(path) - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, disabledTimeoutTimestamp) - path.EndpointA.SendPacket(packet) - suite.chainB.App.GetIBCKeeper().ChannelKeeper.SetPacketReceipt(suite.chainB.GetContext(), path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, 1) + sequence, err := path.EndpointA.SendPacket(defaultTimeoutHeight, disabledTimeoutTimestamp, ibctesting.MockPacketData) + suite.Require().NoError(err) + suite.chainB.App.GetIBCKeeper().ChannelKeeper.SetPacketReceipt(suite.chainB.GetContext(), path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, sequence) + packet = types.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, defaultTimeoutHeight, disabledTimeoutTimestamp) channelCap = suite.chainB.GetChannelCapability(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID) }, false}, {"validation failed", func() { @@ -440,7 +470,7 @@ func (suite *KeeperTestSuite) TestRecvPacket() { // packet commitment not set resulting in invalid proof suite.coordinator.Setup(path) - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, disabledTimeoutTimestamp) + packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, defaultTimeoutHeight, disabledTimeoutTimestamp) channelCap = suite.chainB.GetChannelCapability(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID) }, false}, } @@ -501,7 +531,7 @@ func (suite *KeeperTestSuite) TestWriteAcknowledgement() { "success", func() { suite.coordinator.Setup(path) - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, disabledTimeoutTimestamp) + packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, defaultTimeoutHeight, disabledTimeoutTimestamp) ack = ibcmock.MockAcknowledgement channelCap = suite.chainB.GetChannelCapability(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID) }, @@ -510,16 +540,16 @@ func (suite *KeeperTestSuite) TestWriteAcknowledgement() { {"channel not found", func() { // use wrong channel naming suite.coordinator.Setup(path) - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, ibctesting.InvalidID, ibctesting.InvalidID, timeoutHeight, disabledTimeoutTimestamp) + packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, ibctesting.InvalidID, ibctesting.InvalidID, defaultTimeoutHeight, disabledTimeoutTimestamp) ack = ibcmock.MockAcknowledgement channelCap = suite.chainB.GetChannelCapability(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID) }, false}, {"channel not open", func() { suite.coordinator.Setup(path) - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, disabledTimeoutTimestamp) + packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, defaultTimeoutHeight, disabledTimeoutTimestamp) ack = ibcmock.MockAcknowledgement - err := path.EndpointB.SetChannelClosed() + err := path.EndpointB.SetChannelState(types.CLOSED) suite.Require().NoError(err) channelCap = suite.chainB.GetChannelCapability(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID) }, false}, @@ -527,7 +557,7 @@ func (suite *KeeperTestSuite) TestWriteAcknowledgement() { "capability authentication failed", func() { suite.coordinator.Setup(path) - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, disabledTimeoutTimestamp) + packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, defaultTimeoutHeight, disabledTimeoutTimestamp) ack = ibcmock.MockAcknowledgement channelCap = capabilitytypes.NewCapability(3) }, @@ -537,7 +567,7 @@ func (suite *KeeperTestSuite) TestWriteAcknowledgement() { "no-op, already acked", func() { suite.coordinator.Setup(path) - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, disabledTimeoutTimestamp) + packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, defaultTimeoutHeight, disabledTimeoutTimestamp) ack = ibcmock.MockAcknowledgement suite.chainB.App.GetIBCKeeper().ChannelKeeper.SetPacketAcknowledgement(suite.chainB.GetContext(), packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence(), ack.Acknowledgement()) channelCap = suite.chainB.GetChannelCapability(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID) @@ -548,8 +578,8 @@ func (suite *KeeperTestSuite) TestWriteAcknowledgement() { "empty acknowledgement", func() { suite.coordinator.Setup(path) - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, disabledTimeoutTimestamp) - ack = ibcmock.NewMockEmptyAcknowledgement() + packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, defaultTimeoutHeight, disabledTimeoutTimestamp) + ack = ibcmock.NewEmptyAcknowledgement() channelCap = suite.chainB.GetChannelCapability(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID) }, false, @@ -558,7 +588,7 @@ func (suite *KeeperTestSuite) TestWriteAcknowledgement() { "acknowledgement is nil", func() { suite.coordinator.Setup(path) - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, disabledTimeoutTimestamp) + packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, defaultTimeoutHeight, disabledTimeoutTimestamp) ack = nil channelCap = suite.chainB.GetChannelCapability(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID) }, @@ -592,19 +622,20 @@ func (suite *KeeperTestSuite) TestAcknowledgePacket() { ack = ibcmock.MockAcknowledgement channelCap *capabilitytypes.Capability - expError *sdkerrors.Error + expError *errorsmod.Error ) testCases := []testCase{ {"success on ordered channel", func() { path.SetChannelOrdered() suite.coordinator.Setup(path) - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, disabledTimeoutTimestamp) + // create packet commitment - err := path.EndpointA.SendPacket(packet) + sequence, err := path.EndpointA.SendPacket(defaultTimeoutHeight, disabledTimeoutTimestamp, ibctesting.MockPacketData) suite.Require().NoError(err) // create packet receipt and acknowledgement + packet = types.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, defaultTimeoutHeight, disabledTimeoutTimestamp) err = path.EndpointB.RecvPacket(packet) suite.Require().NoError(err) @@ -613,13 +644,13 @@ func (suite *KeeperTestSuite) TestAcknowledgePacket() { {"success on unordered channel", func() { // setup uses an UNORDERED channel suite.coordinator.Setup(path) - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, disabledTimeoutTimestamp) // create packet commitment - err := path.EndpointA.SendPacket(packet) + sequence, err := path.EndpointA.SendPacket(defaultTimeoutHeight, disabledTimeoutTimestamp, ibctesting.MockPacketData) suite.Require().NoError(err) // create packet receipt and acknowledgement + packet = types.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, defaultTimeoutHeight, disabledTimeoutTimestamp) err = path.EndpointB.RecvPacket(packet) suite.Require().NoError(err) @@ -630,12 +661,13 @@ func (suite *KeeperTestSuite) TestAcknowledgePacket() { path.SetChannelOrdered() suite.coordinator.Setup(path) - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, disabledTimeoutTimestamp) + // create packet commitment - err := path.EndpointA.SendPacket(packet) + sequence, err := path.EndpointA.SendPacket(defaultTimeoutHeight, disabledTimeoutTimestamp, ibctesting.MockPacketData) suite.Require().NoError(err) // create packet receipt and acknowledgement + packet = types.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, defaultTimeoutHeight, disabledTimeoutTimestamp) err = path.EndpointB.RecvPacket(packet) suite.Require().NoError(err) @@ -649,13 +681,13 @@ func (suite *KeeperTestSuite) TestAcknowledgePacket() { // setup uses an UNORDERED channel suite.coordinator.Setup(path) - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, disabledTimeoutTimestamp) // create packet commitment - err := path.EndpointA.SendPacket(packet) + sequence, err := path.EndpointA.SendPacket(defaultTimeoutHeight, disabledTimeoutTimestamp, ibctesting.MockPacketData) suite.Require().NoError(err) // create packet receipt and acknowledgement + packet = types.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, defaultTimeoutHeight, disabledTimeoutTimestamp) err = path.EndpointB.RecvPacket(packet) suite.Require().NoError(err) @@ -669,15 +701,15 @@ func (suite *KeeperTestSuite) TestAcknowledgePacket() { // use wrong channel naming suite.coordinator.Setup(path) - packet = types.NewPacket(ibctesting.MockPacketData, 1, ibctesting.InvalidID, ibctesting.InvalidID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, disabledTimeoutTimestamp) + packet = types.NewPacket(ibctesting.MockPacketData, 1, ibctesting.InvalidID, ibctesting.InvalidID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, defaultTimeoutHeight, disabledTimeoutTimestamp) }, false}, {"channel not open", func() { expError = types.ErrInvalidChannelState suite.coordinator.Setup(path) - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, disabledTimeoutTimestamp) + packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, defaultTimeoutHeight, disabledTimeoutTimestamp) - err := path.EndpointA.SetChannelClosed() + err := path.EndpointA.SetChannelState(types.CLOSED) suite.Require().NoError(err) channelCap = suite.chainA.GetChannelCapability(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) }, false}, @@ -687,12 +719,12 @@ func (suite *KeeperTestSuite) TestAcknowledgePacket() { path.SetChannelOrdered() suite.coordinator.Setup(path) - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, disabledTimeoutTimestamp) // create packet commitment - err := path.EndpointA.SendPacket(packet) + sequence, err := path.EndpointA.SendPacket(defaultTimeoutHeight, disabledTimeoutTimestamp, ibctesting.MockPacketData) suite.Require().NoError(err) // create packet receipt and acknowledgement + packet = types.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, defaultTimeoutHeight, disabledTimeoutTimestamp) err = path.EndpointB.RecvPacket(packet) suite.Require().NoError(err) @@ -703,7 +735,7 @@ func (suite *KeeperTestSuite) TestAcknowledgePacket() { suite.coordinator.Setup(path) // use wrong port for dest - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, ibctesting.InvalidID, path.EndpointB.ChannelID, timeoutHeight, disabledTimeoutTimestamp) + packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, ibctesting.InvalidID, path.EndpointB.ChannelID, defaultTimeoutHeight, disabledTimeoutTimestamp) channelCap = suite.chainA.GetChannelCapability(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) }, false}, {"packet destination channel ID ≠ channel counterparty channel ID", func() { @@ -711,7 +743,7 @@ func (suite *KeeperTestSuite) TestAcknowledgePacket() { suite.coordinator.Setup(path) // use wrong channel for dest - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, ibctesting.InvalidID, timeoutHeight, disabledTimeoutTimestamp) + packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, ibctesting.InvalidID, defaultTimeoutHeight, disabledTimeoutTimestamp) channelCap = suite.chainA.GetChannelCapability(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) }, false}, {"connection not found", func() { @@ -724,7 +756,7 @@ func (suite *KeeperTestSuite) TestAcknowledgePacket() { path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, types.NewChannel(types.OPEN, types.ORDERED, types.NewCounterparty(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID), []string{"connection-1000"}, path.EndpointA.ChannelConfig.Version), ) - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, disabledTimeoutTimestamp) + packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, defaultTimeoutHeight, disabledTimeoutTimestamp) suite.chainA.CreateChannelCapability(suite.chainA.GetSimApp().ScopedIBCMockKeeper, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) channelCap = suite.chainA.GetChannelCapability(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) }, false}, @@ -741,7 +773,7 @@ func (suite *KeeperTestSuite) TestAcknowledgePacket() { path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, types.NewChannel(types.OPEN, types.ORDERED, types.NewCounterparty(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID), []string{path.EndpointA.ConnectionID}, path.EndpointA.ChannelConfig.Version), ) - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, disabledTimeoutTimestamp) + packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, defaultTimeoutHeight, disabledTimeoutTimestamp) suite.chainA.CreateChannelCapability(suite.chainA.GetSimApp().ScopedIBCMockKeeper, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) channelCap = suite.chainA.GetChannelCapability(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) }, false}, @@ -750,7 +782,7 @@ func (suite *KeeperTestSuite) TestAcknowledgePacket() { // packet commitment never written suite.coordinator.Setup(path) - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, disabledTimeoutTimestamp) + packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, defaultTimeoutHeight, disabledTimeoutTimestamp) channelCap = suite.chainA.GetChannelCapability(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) }, false}, {"packet ack verification failed", func() { @@ -758,10 +790,11 @@ func (suite *KeeperTestSuite) TestAcknowledgePacket() { // ack never written suite.coordinator.Setup(path) - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, disabledTimeoutTimestamp) // create packet commitment - path.EndpointA.SendPacket(packet) + sequence, err := path.EndpointA.SendPacket(defaultTimeoutHeight, disabledTimeoutTimestamp, ibctesting.MockPacketData) + suite.Require().NoError(err) + packet = types.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, defaultTimeoutHeight, disabledTimeoutTimestamp) channelCap = suite.chainA.GetChannelCapability(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) }, false}, {"packet commitment bytes do not match", func() { @@ -769,13 +802,13 @@ func (suite *KeeperTestSuite) TestAcknowledgePacket() { // setup uses an UNORDERED channel suite.coordinator.Setup(path) - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, disabledTimeoutTimestamp) // create packet commitment - err := path.EndpointA.SendPacket(packet) + sequence, err := path.EndpointA.SendPacket(defaultTimeoutHeight, disabledTimeoutTimestamp, ibctesting.MockPacketData) suite.Require().NoError(err) // create packet receipt and acknowledgement + packet = types.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, defaultTimeoutHeight, disabledTimeoutTimestamp) err = path.EndpointB.RecvPacket(packet) suite.Require().NoError(err) @@ -797,7 +830,7 @@ func (suite *KeeperTestSuite) TestAcknowledgePacket() { types.NewChannel(types.OPEN, types.ORDERED, types.NewCounterparty(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID), []string{path.EndpointA.ConnectionID}, path.EndpointA.ChannelConfig.Version), ) - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, disabledTimeoutTimestamp) + packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, defaultTimeoutHeight, disabledTimeoutTimestamp) // manually set packet commitment suite.chainA.App.GetIBCKeeper().ChannelKeeper.SetPacketCommitment(suite.chainA.GetContext(), path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, packet.GetSequence(), types.CommitPacket(suite.chainA.App.AppCodec(), packet)) @@ -809,19 +842,22 @@ func (suite *KeeperTestSuite) TestAcknowledgePacket() { suite.coordinator.CommitBlock(path.EndpointA.Chain, path.EndpointB.Chain) - path.EndpointA.UpdateClient() - path.EndpointB.UpdateClient() + err := path.EndpointA.UpdateClient() + suite.Require().NoError(err) + err = path.EndpointB.UpdateClient() + suite.Require().NoError(err) }, false}, {"next ack sequence mismatch ORDERED", func() { expError = types.ErrPacketSequenceOutOfOrder path.SetChannelOrdered() suite.coordinator.Setup(path) - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, disabledTimeoutTimestamp) + // create packet commitment - err := path.EndpointA.SendPacket(packet) + sequence, err := path.EndpointA.SendPacket(defaultTimeoutHeight, disabledTimeoutTimestamp, ibctesting.MockPacketData) suite.Require().NoError(err) // create packet acknowledgement + packet = types.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, defaultTimeoutHeight, disabledTimeoutTimestamp) err = path.EndpointB.RecvPacket(packet) suite.Require().NoError(err) diff --git a/modules/core/04-channel/keeper/timeout.go b/modules/core/04-channel/keeper/timeout.go index 1076f333185..a0151520cbf 100644 --- a/modules/core/04-channel/keeper/timeout.go +++ b/modules/core/04-channel/keeper/timeout.go @@ -4,14 +4,14 @@ import ( "bytes" "strconv" + errorsmod "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - connectiontypes "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types" - "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" - "github.com/cosmos/ibc-go/v4/modules/core/exported" + connectiontypes "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" + "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + "github.com/cosmos/ibc-go/v7/modules/core/exported" ) // TimeoutPacket is called by a module which originally attempted to send a @@ -29,7 +29,7 @@ func (k Keeper) TimeoutPacket( ) error { channel, found := k.GetChannel(ctx, packet.GetSourcePort(), packet.GetSourceChannel()) if !found { - return sdkerrors.Wrapf( + return errorsmod.Wrapf( types.ErrChannelNotFound, "port ID (%s) channel ID (%s)", packet.GetSourcePort(), packet.GetSourceChannel(), ) @@ -39,14 +39,14 @@ func (k Keeper) TimeoutPacket( // so the capability authentication can be omitted here if packet.GetDestPort() != channel.Counterparty.PortId { - return sdkerrors.Wrapf( + return errorsmod.Wrapf( types.ErrInvalidPacket, "packet destination port doesn't match the counterparty's port (%s ≠ %s)", packet.GetDestPort(), channel.Counterparty.PortId, ) } if packet.GetDestChannel() != channel.Counterparty.ChannelId { - return sdkerrors.Wrapf( + return errorsmod.Wrapf( types.ErrInvalidPacket, "packet destination channel doesn't match the counterparty's channel (%s ≠ %s)", packet.GetDestChannel(), channel.Counterparty.ChannelId, ) @@ -54,7 +54,7 @@ func (k Keeper) TimeoutPacket( connectionEnd, found := k.connectionKeeper.GetConnection(ctx, channel.ConnectionHops[0]) if !found { - return sdkerrors.Wrap( + return errorsmod.Wrap( connectiontypes.ErrConnectionNotFound, channel.ConnectionHops[0], ) @@ -69,13 +69,13 @@ func (k Keeper) TimeoutPacket( timeoutHeight := packet.GetTimeoutHeight() if (timeoutHeight.IsZero() || proofHeight.LT(timeoutHeight)) && (packet.GetTimeoutTimestamp() == 0 || proofTimestamp < packet.GetTimeoutTimestamp()) { - return sdkerrors.Wrap(types.ErrPacketTimeout, "packet timeout has not been reached for height or timestamp") + return errorsmod.Wrap(types.ErrPacketTimeout, "packet timeout has not been reached for height or timestamp") } commitment := k.GetPacketCommitment(ctx, packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) if len(commitment) == 0 { - EmitTimeoutPacketEvent(ctx, packet, channel) + emitTimeoutPacketEvent(ctx, packet, channel) // This error indicates that the timeout has already been relayed // or there is a misconfigured relayer attempting to prove a timeout // for a packet never sent. Core IBC will treat this error as a no-op in order to @@ -84,7 +84,7 @@ func (k Keeper) TimeoutPacket( } if channel.State != types.OPEN { - return sdkerrors.Wrapf( + return errorsmod.Wrapf( types.ErrInvalidChannelState, "channel state is not OPEN (got %s)", channel.State.String(), ) @@ -94,14 +94,14 @@ func (k Keeper) TimeoutPacket( // verify we sent the packet and haven't cleared it out yet if !bytes.Equal(commitment, packetCommitment) { - return sdkerrors.Wrapf(types.ErrInvalidPacket, "packet commitment bytes are not equal: got (%v), expected (%v)", commitment, packetCommitment) + return errorsmod.Wrapf(types.ErrInvalidPacket, "packet commitment bytes are not equal: got (%v), expected (%v)", commitment, packetCommitment) } switch channel.Ordering { case types.ORDERED: // check that packet has not been received if nextSequenceRecv > packet.GetSequence() { - return sdkerrors.Wrapf( + return errorsmod.Wrapf( types.ErrPacketReceived, "packet already received, next sequence receive > packet sequence (%d > %d)", nextSequenceRecv, packet.GetSequence(), ) @@ -118,7 +118,7 @@ func (k Keeper) TimeoutPacket( packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence(), ) default: - panic(sdkerrors.Wrapf(types.ErrInvalidChannelOrdering, channel.Ordering.String())) + panic(errorsmod.Wrapf(types.ErrInvalidChannelOrdering, channel.Ordering.String())) } if err != nil { @@ -140,12 +140,12 @@ func (k Keeper) TimeoutExecuted( ) error { channel, found := k.GetChannel(ctx, packet.GetSourcePort(), packet.GetSourceChannel()) if !found { - return sdkerrors.Wrapf(types.ErrChannelNotFound, "port ID (%s) channel ID (%s)", packet.GetSourcePort(), packet.GetSourceChannel()) + return errorsmod.Wrapf(types.ErrChannelNotFound, "port ID (%s) channel ID (%s)", packet.GetSourcePort(), packet.GetSourceChannel()) } capName := host.ChannelCapabilityPath(packet.GetSourcePort(), packet.GetSourceChannel()) if !k.scopedKeeper.AuthenticateCapability(ctx, chanCap, capName) { - return sdkerrors.Wrapf( + return errorsmod.Wrapf( types.ErrChannelCapabilityNotFound, "caller does not own capability for channel with capability name %s", capName, ) @@ -168,10 +168,10 @@ func (k Keeper) TimeoutExecuted( ) // emit an event marking that we have processed the timeout - EmitTimeoutPacketEvent(ctx, packet, channel) + emitTimeoutPacketEvent(ctx, packet, channel) if channel.Ordering == types.ORDERED && channel.State == types.CLOSED { - EmitChannelClosedEvent(ctx, packet, channel) + emitChannelClosedEvent(ctx, packet, channel) } return nil @@ -191,26 +191,26 @@ func (k Keeper) TimeoutOnClose( ) error { channel, found := k.GetChannel(ctx, packet.GetSourcePort(), packet.GetSourceChannel()) if !found { - return sdkerrors.Wrapf(types.ErrChannelNotFound, "port ID (%s) channel ID (%s)", packet.GetSourcePort(), packet.GetSourceChannel()) + return errorsmod.Wrapf(types.ErrChannelNotFound, "port ID (%s) channel ID (%s)", packet.GetSourcePort(), packet.GetSourceChannel()) } capName := host.ChannelCapabilityPath(packet.GetSourcePort(), packet.GetSourceChannel()) if !k.scopedKeeper.AuthenticateCapability(ctx, chanCap, capName) { - return sdkerrors.Wrapf( + return errorsmod.Wrapf( types.ErrInvalidChannelCapability, "channel capability failed authentication with capability name %s", capName, ) } if packet.GetDestPort() != channel.Counterparty.PortId { - return sdkerrors.Wrapf( + return errorsmod.Wrapf( types.ErrInvalidPacket, "packet destination port doesn't match the counterparty's port (%s ≠ %s)", packet.GetDestPort(), channel.Counterparty.PortId, ) } if packet.GetDestChannel() != channel.Counterparty.ChannelId { - return sdkerrors.Wrapf( + return errorsmod.Wrapf( types.ErrInvalidPacket, "packet destination channel doesn't match the counterparty's channel (%s ≠ %s)", packet.GetDestChannel(), channel.Counterparty.ChannelId, ) @@ -218,13 +218,13 @@ func (k Keeper) TimeoutOnClose( connectionEnd, found := k.connectionKeeper.GetConnection(ctx, channel.ConnectionHops[0]) if !found { - return sdkerrors.Wrap(connectiontypes.ErrConnectionNotFound, channel.ConnectionHops[0]) + return errorsmod.Wrap(connectiontypes.ErrConnectionNotFound, channel.ConnectionHops[0]) } commitment := k.GetPacketCommitment(ctx, packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) if len(commitment) == 0 { - EmitTimeoutPacketEvent(ctx, packet, channel) + emitTimeoutPacketEvent(ctx, packet, channel) // This error indicates that the timeout has already been relayed // or there is a misconfigured relayer attempting to prove a timeout // for a packet never sent. Core IBC will treat this error as a no-op in order to @@ -236,7 +236,7 @@ func (k Keeper) TimeoutOnClose( // verify we sent the packet and haven't cleared it out yet if !bytes.Equal(commitment, packetCommitment) { - return sdkerrors.Wrapf(types.ErrInvalidPacket, "packet commitment bytes are not equal: got (%v), expected (%v)", commitment, packetCommitment) + return errorsmod.Wrapf(types.ErrInvalidPacket, "packet commitment bytes are not equal: got (%v), expected (%v)", commitment, packetCommitment) } counterpartyHops := []string{connectionEnd.GetCounterparty().GetConnectionID()} @@ -260,7 +260,7 @@ func (k Keeper) TimeoutOnClose( case types.ORDERED: // check that packet has not been received if nextSequenceRecv > packet.GetSequence() { - return sdkerrors.Wrapf(types.ErrInvalidPacket, "packet already received, next sequence receive > packet sequence (%d > %d", nextSequenceRecv, packet.GetSequence()) + return errorsmod.Wrapf(types.ErrInvalidPacket, "packet already received, next sequence receive > packet sequence (%d > %d", nextSequenceRecv, packet.GetSequence()) } // check that the recv sequence is as claimed @@ -274,7 +274,7 @@ func (k Keeper) TimeoutOnClose( packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence(), ) default: - panic(sdkerrors.Wrapf(types.ErrInvalidChannelOrdering, channel.Ordering.String())) + panic(errorsmod.Wrapf(types.ErrInvalidChannelOrdering, channel.Ordering.String())) } if err != nil { diff --git a/modules/core/04-channel/keeper/timeout_test.go b/modules/core/04-channel/keeper/timeout_test.go index 5dab582e947..e2f8ebc9305 100644 --- a/modules/core/04-channel/keeper/timeout_test.go +++ b/modules/core/04-channel/keeper/timeout_test.go @@ -4,15 +4,15 @@ import ( "errors" "fmt" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + errorsmod "cosmossdk.io/errors" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - connectiontypes "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types" - "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" - "github.com/cosmos/ibc-go/v4/modules/core/exported" - ibctesting "github.com/cosmos/ibc-go/v4/testing" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + connectiontypes "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" + "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + "github.com/cosmos/ibc-go/v7/modules/core/exported" + ibctesting "github.com/cosmos/ibc-go/v7/testing" ) // TestTimeoutPacket test the TimeoutPacket call on chainA by ensuring the timeout has passed @@ -24,85 +24,107 @@ func (suite *KeeperTestSuite) TestTimeoutPacket() { packet types.Packet nextSeqRecv uint64 ordered bool - expError *sdkerrors.Error + expError *errorsmod.Error ) testCases := []testCase{ {"success: ORDERED", func() { ordered = true path.SetChannelOrdered() - suite.coordinator.Setup(path) - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, clienttypes.GetSelfHeight(suite.chainB.GetContext()), uint64(suite.chainB.GetContext().BlockTime().UnixNano())) - path.EndpointA.SendPacket(packet) + + timeoutHeight := clienttypes.GetSelfHeight(suite.chainB.GetContext()) + timeoutTimestamp := uint64(suite.chainB.GetContext().BlockTime().UnixNano()) + + sequence, err := path.EndpointA.SendPacket(timeoutHeight, timeoutTimestamp, ibctesting.MockPacketData) + suite.Require().NoError(err) + packet = types.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, timeoutTimestamp) // need to update chainA's client representing chainB to prove missing ack - path.EndpointA.UpdateClient() + err = path.EndpointA.UpdateClient() + suite.Require().NoError(err) }, true}, {"success: UNORDERED", func() { ordered = false - suite.coordinator.Setup(path) - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, clienttypes.GetSelfHeight(suite.chainB.GetContext()), disabledTimeoutTimestamp) - path.EndpointA.SendPacket(packet) + + timeoutHeight := clienttypes.GetSelfHeight(suite.chainB.GetContext()) + + sequence, err := path.EndpointA.SendPacket(timeoutHeight, disabledTimeoutTimestamp, ibctesting.MockPacketData) + suite.Require().NoError(err) + packet = types.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, disabledTimeoutTimestamp) // need to update chainA's client representing chainB to prove missing ack - path.EndpointA.UpdateClient() + err = path.EndpointA.UpdateClient() + suite.Require().NoError(err) }, true}, {"packet already timed out: ORDERED", func() { expError = types.ErrNoOpMsg ordered = true path.SetChannelOrdered() - suite.coordinator.Setup(path) - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, clienttypes.GetSelfHeight(suite.chainB.GetContext()), uint64(suite.chainB.GetContext().BlockTime().UnixNano())) - path.EndpointA.SendPacket(packet) + + timeoutHeight := clienttypes.GetSelfHeight(suite.chainB.GetContext()) + timeoutTimestamp := uint64(suite.chainB.GetContext().BlockTime().UnixNano()) + + sequence, err := path.EndpointA.SendPacket(timeoutHeight, timeoutTimestamp, ibctesting.MockPacketData) + suite.Require().NoError(err) // need to update chainA's client representing chainB to prove missing ack - path.EndpointA.UpdateClient() + err = path.EndpointA.UpdateClient() + suite.Require().NoError(err) - err := path.EndpointA.TimeoutPacket(packet) + packet = types.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, timeoutTimestamp) + err = path.EndpointA.TimeoutPacket(packet) suite.Require().NoError(err) }, false}, {"packet already timed out: UNORDERED", func() { expError = types.ErrNoOpMsg ordered = false - suite.coordinator.Setup(path) - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, clienttypes.GetSelfHeight(suite.chainB.GetContext()), disabledTimeoutTimestamp) - path.EndpointA.SendPacket(packet) + + timeoutHeight := clienttypes.GetSelfHeight(suite.chainB.GetContext()) + + sequence, err := path.EndpointA.SendPacket(timeoutHeight, disabledTimeoutTimestamp, ibctesting.MockPacketData) + suite.Require().NoError(err) // need to update chainA's client representing chainB to prove missing ack - path.EndpointA.UpdateClient() + err = path.EndpointA.UpdateClient() + suite.Require().NoError(err) - err := path.EndpointA.TimeoutPacket(packet) + packet = types.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, disabledTimeoutTimestamp) + err = path.EndpointA.TimeoutPacket(packet) suite.Require().NoError(err) }, false}, {"channel not found", func() { expError = types.ErrChannelNotFound // use wrong channel naming suite.coordinator.Setup(path) - packet = types.NewPacket(ibctesting.MockPacketData, 1, ibctesting.InvalidID, ibctesting.InvalidID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, disabledTimeoutTimestamp) + packet = types.NewPacket(ibctesting.MockPacketData, 1, ibctesting.InvalidID, ibctesting.InvalidID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, defaultTimeoutHeight, disabledTimeoutTimestamp) }, false}, {"channel not open", func() { expError = types.ErrInvalidChannelState suite.coordinator.Setup(path) - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, path.EndpointA.GetClientState().GetLatestHeight().Increment().(clienttypes.Height), disabledTimeoutTimestamp) - err := path.EndpointA.SendPacket(packet) + + timeoutHeight := path.EndpointA.GetClientState().GetLatestHeight().Increment().(clienttypes.Height) + + sequence, err := path.EndpointA.SendPacket(timeoutHeight, disabledTimeoutTimestamp, ibctesting.MockPacketData) suite.Require().NoError(err) + packet = types.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, disabledTimeoutTimestamp) // need to update chainA's client representing chainB to prove missing ack - path.EndpointA.UpdateClient() + err = path.EndpointA.UpdateClient() + suite.Require().NoError(err) - err = path.EndpointA.SetChannelClosed() + err = path.EndpointA.SetChannelState(types.CLOSED) suite.Require().NoError(err) }, false}, {"packet destination port ≠ channel counterparty port", func() { expError = types.ErrInvalidPacket suite.coordinator.Setup(path) // use wrong port for dest - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, ibctesting.InvalidID, path.EndpointB.ChannelID, timeoutHeight, disabledTimeoutTimestamp) + packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, ibctesting.InvalidID, path.EndpointB.ChannelID, defaultTimeoutHeight, disabledTimeoutTimestamp) }, false}, {"packet destination channel ID ≠ channel counterparty channel ID", func() { expError = types.ErrInvalidPacket suite.coordinator.Setup(path) // use wrong channel for dest - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, ibctesting.InvalidID, timeoutHeight, disabledTimeoutTimestamp) + packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, ibctesting.InvalidID, defaultTimeoutHeight, disabledTimeoutTimestamp) }, false}, {"connection not found", func() { expError = connectiontypes.ErrConnectionNotFound @@ -112,26 +134,31 @@ func (suite *KeeperTestSuite) TestTimeoutPacket() { path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, types.NewChannel(types.OPEN, types.ORDERED, types.NewCounterparty(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID), []string{connIDA}, path.EndpointA.ChannelConfig.Version), ) - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, disabledTimeoutTimestamp) + packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, defaultTimeoutHeight, disabledTimeoutTimestamp) }, false}, {"timeout", func() { expError = types.ErrPacketTimeout suite.coordinator.Setup(path) - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, disabledTimeoutTimestamp) - path.EndpointA.SendPacket(packet) - path.EndpointA.UpdateClient() + sequence, err := path.EndpointA.SendPacket(defaultTimeoutHeight, disabledTimeoutTimestamp, ibctesting.MockPacketData) + suite.Require().NoError(err) + packet = types.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, defaultTimeoutHeight, disabledTimeoutTimestamp) + err = path.EndpointA.UpdateClient() + suite.Require().NoError(err) }, false}, {"packet already received ", func() { expError = types.ErrPacketReceived ordered = true path.SetChannelOrdered() + suite.coordinator.Setup(path) nextSeqRecv = 2 + timeoutTimestamp := uint64(suite.chainB.GetContext().BlockTime().UnixNano()) - suite.coordinator.Setup(path) - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, uint64(suite.chainB.GetContext().BlockTime().UnixNano())) - path.EndpointA.SendPacket(packet) - path.EndpointA.UpdateClient() + sequence, err := path.EndpointA.SendPacket(defaultTimeoutHeight, timeoutTimestamp, ibctesting.MockPacketData) + suite.Require().NoError(err) + packet = types.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, defaultTimeoutHeight, timeoutTimestamp) + err = path.EndpointA.UpdateClient() + suite.Require().NoError(err) }, false}, {"packet hasn't been sent", func() { expError = types.ErrNoOpMsg @@ -139,8 +166,9 @@ func (suite *KeeperTestSuite) TestTimeoutPacket() { path.SetChannelOrdered() suite.coordinator.Setup(path) - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, uint64(suite.chainB.GetContext().BlockTime().UnixNano())) - path.EndpointA.UpdateClient() + packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, defaultTimeoutHeight, uint64(suite.chainB.GetContext().BlockTime().UnixNano())) + err := path.EndpointA.UpdateClient() + suite.Require().NoError(err) }, false}, {"next seq receive verification failed", func() { // skip error check, error occurs in light-clients @@ -149,11 +177,15 @@ func (suite *KeeperTestSuite) TestTimeoutPacket() { ordered = false path.SetChannelOrdered() - suite.coordinator.Setup(path) - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, clienttypes.GetSelfHeight(suite.chainB.GetContext()), disabledTimeoutTimestamp) - path.EndpointA.SendPacket(packet) - path.EndpointA.UpdateClient() + + timeoutHeight := clienttypes.GetSelfHeight(suite.chainB.GetContext()) + + sequence, err := path.EndpointA.SendPacket(timeoutHeight, disabledTimeoutTimestamp, ibctesting.MockPacketData) + suite.Require().NoError(err) + packet = types.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, disabledTimeoutTimestamp) + err = path.EndpointA.UpdateClient() + suite.Require().NoError(err) }, false}, {"packet ack verification failed", func() { // skip error check, error occurs in light-clients @@ -162,9 +194,14 @@ func (suite *KeeperTestSuite) TestTimeoutPacket() { ordered = true suite.coordinator.Setup(path) - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, clienttypes.GetSelfHeight(suite.chainB.GetContext()), disabledTimeoutTimestamp) - path.EndpointA.SendPacket(packet) - path.EndpointA.UpdateClient() + + timeoutHeight := clienttypes.GetSelfHeight(suite.chainB.GetContext()) + + sequence, err := path.EndpointA.SendPacket(timeoutHeight, disabledTimeoutTimestamp, ibctesting.MockPacketData) + suite.Require().NoError(err) + packet = types.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, disabledTimeoutTimestamp) + err = path.EndpointA.UpdateClient() + suite.Require().NoError(err) }, false}, } @@ -204,7 +241,6 @@ func (suite *KeeperTestSuite) TestTimeoutPacket() { if expError != nil { suite.Require().True(errors.Is(err, expError)) } - } }) } @@ -224,23 +260,31 @@ func (suite *KeeperTestSuite) TestTimeoutExecuted() { path.SetChannelOrdered() suite.coordinator.Setup(path) - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, clienttypes.GetSelfHeight(suite.chainB.GetContext()), uint64(suite.chainB.GetContext().BlockTime().UnixNano())) - path.EndpointA.SendPacket(packet) + timeoutHeight := clienttypes.GetSelfHeight(suite.chainB.GetContext()) + timeoutTimestamp := uint64(suite.chainB.GetContext().BlockTime().UnixNano()) + sequence, err := path.EndpointA.SendPacket(timeoutHeight, timeoutTimestamp, ibctesting.MockPacketData) + suite.Require().NoError(err) + + packet = types.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, timeoutTimestamp) chanCap = suite.chainA.GetChannelCapability(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) }, true}, {"channel not found", func() { // use wrong channel naming suite.coordinator.Setup(path) - packet = types.NewPacket(ibctesting.MockPacketData, 1, ibctesting.InvalidID, ibctesting.InvalidID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, disabledTimeoutTimestamp) + packet = types.NewPacket(ibctesting.MockPacketData, 1, ibctesting.InvalidID, ibctesting.InvalidID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, defaultTimeoutHeight, disabledTimeoutTimestamp) }, false}, {"incorrect capability ORDERED", func() { path.SetChannelOrdered() suite.coordinator.Setup(path) - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, clienttypes.GetSelfHeight(suite.chainB.GetContext()), uint64(suite.chainB.GetContext().BlockTime().UnixNano())) - path.EndpointA.SendPacket(packet) + timeoutHeight := clienttypes.GetSelfHeight(suite.chainB.GetContext()) + timeoutTimestamp := uint64(suite.chainB.GetContext().BlockTime().UnixNano()) + sequence, err := path.EndpointA.SendPacket(timeoutHeight, timeoutTimestamp, ibctesting.MockPacketData) + suite.Require().NoError(err) + + packet = types.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, timeoutTimestamp) chanCap = capabilitytypes.NewCapability(100) }, false}, } @@ -283,40 +327,52 @@ func (suite *KeeperTestSuite) TestTimeoutOnClose() { path.SetChannelOrdered() suite.coordinator.Setup(path) - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, clienttypes.GetSelfHeight(suite.chainB.GetContext()), uint64(suite.chainB.GetContext().BlockTime().UnixNano())) - path.EndpointA.SendPacket(packet) - path.EndpointB.SetChannelClosed() + timeoutHeight := clienttypes.GetSelfHeight(suite.chainB.GetContext()) + timeoutTimestamp := uint64(suite.chainB.GetContext().BlockTime().UnixNano()) + + sequence, err := path.EndpointA.SendPacket(timeoutHeight, timeoutTimestamp, ibctesting.MockPacketData) + suite.Require().NoError(err) + err = path.EndpointB.SetChannelState(types.CLOSED) + suite.Require().NoError(err) // need to update chainA's client representing chainB to prove missing ack - path.EndpointA.UpdateClient() + err = path.EndpointA.UpdateClient() + suite.Require().NoError(err) + packet = types.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, timeoutTimestamp) chanCap = suite.chainA.GetChannelCapability(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) }, true}, {"success: UNORDERED", func() { ordered = false suite.coordinator.Setup(path) - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, clienttypes.GetSelfHeight(suite.chainB.GetContext()), disabledTimeoutTimestamp) - path.EndpointA.SendPacket(packet) - path.EndpointB.SetChannelClosed() + + timeoutHeight := clienttypes.GetSelfHeight(suite.chainB.GetContext()) + + sequence, err := path.EndpointA.SendPacket(timeoutHeight, disabledTimeoutTimestamp, ibctesting.MockPacketData) + suite.Require().NoError(err) + err = path.EndpointB.SetChannelState(types.CLOSED) + suite.Require().NoError(err) // need to update chainA's client representing chainB to prove missing ack - path.EndpointA.UpdateClient() + err = path.EndpointA.UpdateClient() + suite.Require().NoError(err) + packet = types.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, disabledTimeoutTimestamp) chanCap = suite.chainA.GetChannelCapability(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) }, true}, {"channel not found", func() { // use wrong channel naming suite.coordinator.Setup(path) - packet = types.NewPacket(ibctesting.MockPacketData, 1, ibctesting.InvalidID, ibctesting.InvalidID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, disabledTimeoutTimestamp) + packet = types.NewPacket(ibctesting.MockPacketData, 1, ibctesting.InvalidID, ibctesting.InvalidID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, defaultTimeoutHeight, disabledTimeoutTimestamp) }, false}, {"packet dest port ≠ channel counterparty port", func() { suite.coordinator.Setup(path) // use wrong port for dest - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, ibctesting.InvalidID, path.EndpointB.ChannelID, timeoutHeight, disabledTimeoutTimestamp) + packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, ibctesting.InvalidID, path.EndpointB.ChannelID, defaultTimeoutHeight, disabledTimeoutTimestamp) chanCap = suite.chainA.GetChannelCapability(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) }, false}, {"packet dest channel ID ≠ channel counterparty channel ID", func() { suite.coordinator.Setup(path) // use wrong channel for dest - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, ibctesting.InvalidID, timeoutHeight, disabledTimeoutTimestamp) + packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, ibctesting.InvalidID, defaultTimeoutHeight, disabledTimeoutTimestamp) chanCap = suite.chainA.GetChannelCapability(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) }, false}, {"connection not found", func() { @@ -326,7 +382,7 @@ func (suite *KeeperTestSuite) TestTimeoutOnClose() { path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, types.NewChannel(types.OPEN, types.ORDERED, types.NewCounterparty(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID), []string{connIDA}, path.EndpointA.ChannelConfig.Version), ) - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, disabledTimeoutTimestamp) + packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, defaultTimeoutHeight, disabledTimeoutTimestamp) // create chancap suite.chainA.CreateChannelCapability(suite.chainA.GetSimApp().ScopedIBCMockKeeper, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) @@ -345,42 +401,67 @@ func (suite *KeeperTestSuite) TestTimeoutOnClose() { ordered = true suite.coordinator.Setup(path) - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, clienttypes.GetSelfHeight(suite.chainB.GetContext()), uint64(suite.chainB.GetContext().BlockTime().UnixNano())) - path.EndpointA.SendPacket(packet) - path.EndpointB.SetChannelClosed() + timeoutHeight := clienttypes.GetSelfHeight(suite.chainB.GetContext()) + timeoutTimestamp := uint64(suite.chainB.GetContext().BlockTime().UnixNano()) + + sequence, err := path.EndpointA.SendPacket(timeoutHeight, timeoutTimestamp, ibctesting.MockPacketData) + suite.Require().NoError(err) + err = path.EndpointB.SetChannelState(types.CLOSED) + suite.Require().NoError(err) // need to update chainA's client representing chainB to prove missing ack - path.EndpointA.UpdateClient() + err = path.EndpointA.UpdateClient() + suite.Require().NoError(err) + packet = types.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, timeoutTimestamp) chanCap = suite.chainA.GetChannelCapability(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) }, false}, {"channel verification failed ORDERED", func() { ordered = true path.SetChannelOrdered() suite.coordinator.Setup(path) - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, clienttypes.GetSelfHeight(suite.chainB.GetContext()), uint64(suite.chainB.GetContext().BlockTime().UnixNano())) - path.EndpointA.SendPacket(packet) + + timeoutHeight := clienttypes.GetSelfHeight(suite.chainB.GetContext()) + timeoutTimestamp := uint64(suite.chainB.GetContext().BlockTime().UnixNano()) + + sequence, err := path.EndpointA.SendPacket(timeoutHeight, timeoutTimestamp, ibctesting.MockPacketData) + suite.Require().NoError(err) + packet = types.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, timeoutTimestamp) chanCap = suite.chainA.GetChannelCapability(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) }, false}, {"next seq receive verification failed ORDERED", func() { // set ordered to false providing the wrong proof for ORDERED case ordered = false - path.SetChannelOrdered() suite.coordinator.Setup(path) - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, clienttypes.GetSelfHeight(suite.chainB.GetContext()), uint64(suite.chainB.GetContext().BlockTime().UnixNano())) - path.EndpointA.SendPacket(packet) - path.EndpointB.SetChannelClosed() - path.EndpointA.UpdateClient() + + timeoutHeight := clienttypes.GetSelfHeight(suite.chainB.GetContext()) + timeoutTimestamp := uint64(suite.chainB.GetContext().BlockTime().UnixNano()) + + sequence, err := path.EndpointA.SendPacket(timeoutHeight, timeoutTimestamp, ibctesting.MockPacketData) + suite.Require().NoError(err) + err = path.EndpointB.SetChannelState(types.CLOSED) + suite.Require().NoError(err) + err = path.EndpointA.UpdateClient() + suite.Require().NoError(err) + + packet = types.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, clienttypes.GetSelfHeight(suite.chainB.GetContext()), uint64(suite.chainB.GetContext().BlockTime().UnixNano())) chanCap = suite.chainA.GetChannelCapability(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) }, false}, {"packet ack verification failed", func() { // set ordered to true providing the wrong proof for UNORDERED case ordered = true suite.coordinator.Setup(path) - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, clienttypes.GetSelfHeight(suite.chainB.GetContext()), disabledTimeoutTimestamp) - path.EndpointA.SendPacket(packet) - path.EndpointB.SetChannelClosed() - path.EndpointA.UpdateClient() + + timeoutHeight := clienttypes.GetSelfHeight(suite.chainB.GetContext()) + + sequence, err := path.EndpointA.SendPacket(timeoutHeight, disabledTimeoutTimestamp, ibctesting.MockPacketData) + suite.Require().NoError(err) + err = path.EndpointB.SetChannelState(types.CLOSED) + suite.Require().NoError(err) + err = path.EndpointA.UpdateClient() + suite.Require().NoError(err) + + packet = types.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, disabledTimeoutTimestamp) chanCap = suite.chainA.GetChannelCapability(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) }, false}, {"channel capability not found ORDERED", func() { @@ -388,12 +469,18 @@ func (suite *KeeperTestSuite) TestTimeoutOnClose() { path.SetChannelOrdered() suite.coordinator.Setup(path) - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, clienttypes.GetSelfHeight(suite.chainB.GetContext()), uint64(suite.chainB.GetContext().BlockTime().UnixNano())) - path.EndpointA.SendPacket(packet) - path.EndpointB.SetChannelClosed() + timeoutHeight := clienttypes.GetSelfHeight(suite.chainB.GetContext()) + timeoutTimestamp := uint64(suite.chainB.GetContext().BlockTime().UnixNano()) + + sequence, err := path.EndpointA.SendPacket(timeoutHeight, timeoutTimestamp, ibctesting.MockPacketData) + suite.Require().NoError(err) + err = path.EndpointB.SetChannelState(types.CLOSED) + suite.Require().NoError(err) // need to update chainA's client representing chainB to prove missing ack - path.EndpointA.UpdateClient() + err = path.EndpointA.UpdateClient() + suite.Require().NoError(err) + packet = types.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, clienttypes.GetSelfHeight(suite.chainB.GetContext()), uint64(suite.chainB.GetContext().BlockTime().UnixNano())) chanCap = capabilitytypes.NewCapability(100) }, false}, } diff --git a/modules/core/04-channel/module.go b/modules/core/04-channel/module.go index 867f8bd0351..5cc0f93b648 100644 --- a/modules/core/04-channel/module.go +++ b/modules/core/04-channel/module.go @@ -1,11 +1,11 @@ package channel import ( - "github.com/gogo/protobuf/grpc" + "github.com/cosmos/gogoproto/grpc" "github.com/spf13/cobra" - "github.com/cosmos/ibc-go/v4/modules/core/04-channel/client/cli" - "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" + "github.com/cosmos/ibc-go/v7/modules/core/04-channel/client/cli" + "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" ) // Name returns the IBC channel ICS name. diff --git a/modules/core/04-channel/simulation/decoder.go b/modules/core/04-channel/simulation/decoder.go index 5cec8179b84..7a66bc5b0c0 100644 --- a/modules/core/04-channel/simulation/decoder.go +++ b/modules/core/04-channel/simulation/decoder.go @@ -8,8 +8,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/kv" - "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" + "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" ) // NewDecodeStore returns a decoder function closure that unmarshals the KVPair's diff --git a/modules/core/04-channel/simulation/decoder_test.go b/modules/core/04-channel/simulation/decoder_test.go index 4649df72b7f..64fd06d89f9 100644 --- a/modules/core/04-channel/simulation/decoder_test.go +++ b/modules/core/04-channel/simulation/decoder_test.go @@ -8,10 +8,10 @@ import ( "github.com/cosmos/cosmos-sdk/types/kv" "github.com/stretchr/testify/require" - "github.com/cosmos/ibc-go/v4/modules/core/04-channel/simulation" - "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" - "github.com/cosmos/ibc-go/v4/testing/simapp" + "github.com/cosmos/ibc-go/v7/modules/core/04-channel/simulation" + "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + "github.com/cosmos/ibc-go/v7/testing/simapp" ) func TestDecodeStore(t *testing.T) { diff --git a/modules/core/04-channel/simulation/genesis.go b/modules/core/04-channel/simulation/genesis.go index 7bc7ad6415d..8d30c5eb601 100644 --- a/modules/core/04-channel/simulation/genesis.go +++ b/modules/core/04-channel/simulation/genesis.go @@ -5,7 +5,7 @@ import ( simtypes "github.com/cosmos/cosmos-sdk/types/simulation" - "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" + "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" ) // GenChannelGenesis returns the default channel genesis state. diff --git a/modules/core/04-channel/types/acknowledgement.go b/modules/core/04-channel/types/acknowledgement.go index 49c795d0d55..3e4d02a1180 100644 --- a/modules/core/04-channel/types/acknowledgement.go +++ b/modules/core/04-channel/types/acknowledgement.go @@ -5,8 +5,8 @@ import ( "reflect" "strings" + errorsmod "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ) const ( @@ -32,7 +32,7 @@ func NewResultAcknowledgement(result []byte) Acknowledgement { func NewErrorAcknowledgement(err error) Acknowledgement { // the ABCI code is included in the abcitypes.ResponseDeliverTx hash // constructed in Tendermint and is therefore deterministic - _, code, _ := sdkerrors.ABCIInfo(err, false) // discard non-determinstic codespace and log values + _, code, _ := errorsmod.ABCIInfo(err, false) // discard non-determinstic codespace and log values return Acknowledgement{ Response: &Acknowledgement_Error{ @@ -46,15 +46,15 @@ func (ack Acknowledgement) ValidateBasic() error { switch resp := ack.Response.(type) { case *Acknowledgement_Result: if len(resp.Result) == 0 { - return sdkerrors.Wrap(ErrInvalidAcknowledgement, "acknowledgement result cannot be empty") + return errorsmod.Wrap(ErrInvalidAcknowledgement, "acknowledgement result cannot be empty") } case *Acknowledgement_Error: if strings.TrimSpace(resp.Error) == "" { - return sdkerrors.Wrap(ErrInvalidAcknowledgement, "acknowledgement error cannot be empty") + return errorsmod.Wrap(ErrInvalidAcknowledgement, "acknowledgement error cannot be empty") } default: - return sdkerrors.Wrapf(ErrInvalidAcknowledgement, "unsupported acknowledgement response field type %T", resp) + return errorsmod.Wrapf(ErrInvalidAcknowledgement, "unsupported acknowledgement response field type %T", resp) } return nil } diff --git a/modules/core/04-channel/types/acknowledgement_test.go b/modules/core/04-channel/types/acknowledgement_test.go index 9552fadf996..13ca093fa91 100644 --- a/modules/core/04-channel/types/acknowledgement_test.go +++ b/modules/core/04-channel/types/acknowledgement_test.go @@ -3,12 +3,14 @@ package types_test import ( "fmt" + errorsmod "cosmossdk.io/errors" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" 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/core/04-channel/types" + ibcerrors "github.com/cosmos/ibc-go/v7/internal/errors" + "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" ) const ( @@ -17,7 +19,7 @@ const ( ) // tests acknowledgement.ValidateBasic and acknowledgement.GetBytes -func (suite TypesTestSuite) TestAcknowledgement() { +func (suite TypesTestSuite) TestAcknowledgement() { //nolint:govet // this is a test, we are okay with copying locks testCases := []struct { name string ack types.Acknowledgement @@ -91,27 +93,27 @@ func (suite TypesTestSuite) TestAcknowledgement() { // 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") + err := errorsmod.Wrap(ibcerrors.ErrOutOfGas, "error string 1") + errSameABCICode := errorsmod.Wrap(ibcerrors.ErrOutOfGas, "error string 2") // different ABCI error code used - errDifferentABCICode := sdkerrors.ErrNotFound + errDifferentABCICode := ibcerrors.ErrNotFound - deliverTx := sdkerrors.ResponseDeliverTx(err, gasUsed, gasWanted, false) + deliverTx := sdkerrors.ResponseDeliverTxWithEvents(err, gasUsed, gasWanted, []abcitypes.Event{}, false) responses := tmprotostate.ABCIResponses{ DeliverTxs: []*abcitypes.ResponseDeliverTx{ &deliverTx, }, } - deliverTxSameABCICode := sdkerrors.ResponseDeliverTx(errSameABCICode, gasUsed, gasWanted, false) + deliverTxSameABCICode := sdkerrors.ResponseDeliverTxWithEvents(errSameABCICode, gasUsed, gasWanted, []abcitypes.Event{}, false) responsesSameABCICode := tmprotostate.ABCIResponses{ DeliverTxs: []*abcitypes.ResponseDeliverTx{ &deliverTxSameABCICode, }, } - deliverTxDifferentABCICode := sdkerrors.ResponseDeliverTx(errDifferentABCICode, gasUsed, gasWanted, false) + deliverTxDifferentABCICode := sdkerrors.ResponseDeliverTxWithEvents(errDifferentABCICode, gasUsed, gasWanted, []abcitypes.Event{}, false) responsesDifferentABCICode := tmprotostate.ABCIResponses{ DeliverTxs: []*abcitypes.ResponseDeliverTx{ &deliverTxDifferentABCICode, @@ -130,11 +132,11 @@ func (suite *TypesTestSuite) TestABCICodeDeterminism() { // ABCI error code are used in constructing the acknowledgement error string func (suite *TypesTestSuite) TestAcknowledgementError() { // same ABCI error code used - err := sdkerrors.Wrap(sdkerrors.ErrOutOfGas, "error string 1") - errSameABCICode := sdkerrors.Wrap(sdkerrors.ErrOutOfGas, "error string 2") + err := errorsmod.Wrap(ibcerrors.ErrOutOfGas, "error string 1") + errSameABCICode := errorsmod.Wrap(ibcerrors.ErrOutOfGas, "error string 2") // different ABCI error code used - errDifferentABCICode := sdkerrors.ErrNotFound + errDifferentABCICode := ibcerrors.ErrNotFound ack := types.NewErrorAcknowledgement(err) ackSameABCICode := types.NewErrorAcknowledgement(errSameABCICode) diff --git a/modules/core/04-channel/types/channel.go b/modules/core/04-channel/types/channel.go index 6b02b19f381..012b571fb07 100644 --- a/modules/core/04-channel/types/channel.go +++ b/modules/core/04-channel/types/channel.go @@ -1,10 +1,10 @@ package types import ( - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + errorsmod "cosmossdk.io/errors" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" - "github.com/cosmos/ibc-go/v4/modules/core/exported" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + "github.com/cosmos/ibc-go/v7/modules/core/exported" ) var ( @@ -57,16 +57,16 @@ func (ch Channel) ValidateBasic() error { return ErrInvalidChannelState } if !(ch.Ordering == ORDERED || ch.Ordering == UNORDERED) { - return sdkerrors.Wrap(ErrInvalidChannelOrdering, ch.Ordering.String()) + return errorsmod.Wrap(ErrInvalidChannelOrdering, ch.Ordering.String()) } if len(ch.ConnectionHops) != 1 { - return sdkerrors.Wrap( + return errorsmod.Wrap( ErrTooManyConnectionHops, "current IBC version only supports one connection hop", ) } if err := host.ConnectionIdentifierValidator(ch.ConnectionHops[0]); err != nil { - return sdkerrors.Wrap(err, "invalid connection hop ID") + return errorsmod.Wrap(err, "invalid connection hop ID") } return ch.Counterparty.ValidateBasic() } @@ -92,11 +92,11 @@ func (c Counterparty) GetChannelID() string { // ValidateBasic performs a basic validation check of the identifiers func (c Counterparty) ValidateBasic() error { if err := host.PortIdentifierValidator(c.PortId); err != nil { - return sdkerrors.Wrap(err, "invalid counterparty port ID") + return errorsmod.Wrap(err, "invalid counterparty port ID") } if c.ChannelId != "" { if err := host.ChannelIdentifierValidator(c.ChannelId); err != nil { - return sdkerrors.Wrap(err, "invalid counterparty channel ID") + return errorsmod.Wrap(err, "invalid counterparty channel ID") } } return nil @@ -118,10 +118,10 @@ func NewIdentifiedChannel(portID, channelID string, ch Channel) IdentifiedChanne // ValidateBasic performs a basic validation of the identifiers and channel fields. func (ic IdentifiedChannel) ValidateBasic() error { if err := host.ChannelIdentifierValidator(ic.ChannelId); err != nil { - return sdkerrors.Wrap(err, "invalid channel ID") + return errorsmod.Wrap(err, "invalid channel ID") } if err := host.PortIdentifierValidator(ic.PortId); err != nil { - return sdkerrors.Wrap(err, "invalid port ID") + return errorsmod.Wrap(err, "invalid port ID") } channel := NewChannel(ic.State, ic.Ordering, ic.Counterparty, ic.ConnectionHops, ic.Version) return channel.ValidateBasic() diff --git a/modules/core/04-channel/types/channel.pb.go b/modules/core/04-channel/types/channel.pb.go index 3f52e4aa3dd..6d256367b07 100644 --- a/modules/core/04-channel/types/channel.pb.go +++ b/modules/core/04-channel/types/channel.pb.go @@ -5,9 +5,9 @@ package types import ( fmt "fmt" - types "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - _ "github.com/gogo/protobuf/gogoproto" - proto "github.com/gogo/protobuf/proto" + _ "github.com/cosmos/gogoproto/gogoproto" + proto "github.com/cosmos/gogoproto/proto" + types "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" io "io" math "math" math_bits "math/bits" @@ -511,68 +511,68 @@ func init() { func init() { proto.RegisterFile("ibc/core/channel/v1/channel.proto", fileDescriptor_c3a07336710636a0) } var fileDescriptor_c3a07336710636a0 = []byte{ - // 962 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x56, 0xcf, 0x8e, 0xda, 0xd6, - 0x17, 0xc6, 0x8c, 0x61, 0xe0, 0x30, 0xc3, 0x9f, 0x9b, 0xdf, 0x10, 0xff, 0xdc, 0x04, 0x3b, 0x56, - 0x54, 0x8d, 0x52, 0x05, 0x32, 0xe9, 0xa8, 0x55, 0xb3, 0xea, 0x00, 0x4e, 0xc7, 0x6a, 0x04, 0xc8, - 0x30, 0x8b, 0x66, 0x43, 0xc1, 0xbe, 0x05, 0x2b, 0xe0, 0x4b, 0xed, 0x0b, 0xa3, 0x79, 0x80, 0x4a, - 0x11, 0x9b, 0xf6, 0x05, 0x90, 0x2a, 0x55, 0xed, 0xb3, 0x64, 0x99, 0x65, 0x57, 0xa8, 0x9a, 0x59, - 0x74, 0xcf, 0x0b, 0xb4, 0xf2, 0xbd, 0x36, 0x7f, 0x26, 0x51, 0x96, 0xed, 0xa6, 0x2b, 0xdf, 0xf3, - 0x9d, 0xef, 0x9c, 0xef, 0xdc, 0x73, 0x0e, 0xc6, 0xf0, 0xc0, 0xe9, 0x5b, 0x15, 0x8b, 0x78, 0xb8, - 0x62, 0x0d, 0x7b, 0xae, 0x8b, 0x47, 0x95, 0xd9, 0x49, 0x74, 0x2c, 0x4f, 0x3c, 0x42, 0x09, 0xba, - 0xe3, 0xf4, 0xad, 0x72, 0x40, 0x29, 0x47, 0xf8, 0xec, 0x44, 0xfe, 0xdf, 0x80, 0x0c, 0x08, 0xf3, - 0x57, 0x82, 0x13, 0xa7, 0xca, 0xca, 0x26, 0xdb, 0xc8, 0xc1, 0x2e, 0x65, 0xc9, 0xd8, 0x89, 0x13, - 0xb4, 0x5f, 0xe3, 0xb0, 0x5f, 0xe3, 0x59, 0xd0, 0x13, 0x48, 0xf8, 0xb4, 0x47, 0xb1, 0x24, 0xa8, - 0xc2, 0x71, 0xf6, 0xa9, 0x5c, 0x7e, 0x8f, 0x4e, 0xb9, 0x1d, 0x30, 0x4c, 0x4e, 0x44, 0x9f, 0x41, - 0x8a, 0x78, 0x36, 0xf6, 0x1c, 0x77, 0x20, 0xc5, 0x3f, 0x10, 0xd4, 0x0c, 0x48, 0xe6, 0x9a, 0x8b, - 0xbe, 0x86, 0x03, 0x8b, 0x4c, 0x5d, 0x8a, 0xbd, 0x49, 0xcf, 0xa3, 0x57, 0xd2, 0x9e, 0x2a, 0x1c, - 0x67, 0x9e, 0x3e, 0x78, 0x6f, 0x6c, 0x6d, 0x8b, 0x58, 0x15, 0xdf, 0x2c, 0x95, 0x98, 0xb9, 0x13, - 0x8c, 0x6a, 0x90, 0xb3, 0x88, 0xeb, 0x62, 0x8b, 0x3a, 0xc4, 0xed, 0x0e, 0xc9, 0xc4, 0x97, 0x44, - 0x75, 0xef, 0x38, 0x5d, 0x95, 0x57, 0x4b, 0xa5, 0x78, 0xd5, 0x1b, 0x8f, 0x9e, 0x69, 0xb7, 0x08, - 0x9a, 0x99, 0xdd, 0x20, 0xe7, 0x64, 0xe2, 0x23, 0x09, 0xf6, 0x67, 0xd8, 0xf3, 0x1d, 0xe2, 0x4a, - 0x09, 0x55, 0x38, 0x4e, 0x9b, 0x91, 0xf9, 0x4c, 0x7c, 0xfd, 0xb3, 0x12, 0xd3, 0xfe, 0x8c, 0x43, - 0xc1, 0xb0, 0xb1, 0x4b, 0x9d, 0xef, 0x1c, 0x6c, 0xff, 0xd7, 0xb1, 0x0f, 0x74, 0x0c, 0xdd, 0x85, - 0xfd, 0x09, 0xf1, 0x68, 0xd7, 0xb1, 0xa5, 0x24, 0xf3, 0x24, 0x03, 0xd3, 0xb0, 0xd1, 0x7d, 0x80, - 0xb0, 0xcc, 0xc0, 0xb7, 0xcf, 0x7c, 0xe9, 0x10, 0x31, 0xec, 0xb0, 0xd3, 0x97, 0x70, 0xb0, 0x7d, - 0x01, 0xf4, 0xc9, 0x26, 0x5b, 0xd0, 0xe5, 0x74, 0x15, 0xad, 0x96, 0x4a, 0x96, 0x17, 0x19, 0x3a, - 0xb4, 0xb5, 0xc2, 0xe9, 0x8e, 0x42, 0x9c, 0xf1, 0x8f, 0x56, 0x4b, 0xa5, 0x10, 0x5e, 0x6a, 0xed, - 0xd3, 0xde, 0x15, 0xfe, 0x6b, 0x0f, 0x92, 0xad, 0x9e, 0xf5, 0x0a, 0x53, 0x24, 0x43, 0xca, 0xc7, - 0xdf, 0x4f, 0xb1, 0x6b, 0xf1, 0xd1, 0x8a, 0xe6, 0xda, 0x46, 0x9f, 0x43, 0xc6, 0x27, 0x53, 0xcf, - 0xc2, 0xdd, 0x40, 0x33, 0xd4, 0x28, 0xae, 0x96, 0x0a, 0xe2, 0x1a, 0x5b, 0x4e, 0xcd, 0x04, 0x6e, - 0xb5, 0x88, 0x47, 0xd1, 0x97, 0x90, 0x0d, 0x7d, 0xa1, 0x32, 0x1b, 0x62, 0xba, 0xfa, 0xff, 0xd5, - 0x52, 0x39, 0xda, 0x89, 0x0d, 0xfd, 0x9a, 0x79, 0xc8, 0x81, 0x68, 0xdd, 0x9e, 0x43, 0xde, 0xc6, - 0x3e, 0x75, 0xdc, 0x1e, 0x9b, 0x0b, 0xd3, 0x17, 0x59, 0x8e, 0x8f, 0x56, 0x4b, 0xe5, 0x2e, 0xcf, - 0x71, 0x9b, 0xa1, 0x99, 0xb9, 0x2d, 0x88, 0x55, 0xd2, 0x84, 0x3b, 0xdb, 0xac, 0xa8, 0x1c, 0x36, - 0xc6, 0x6a, 0x69, 0xb5, 0x54, 0xe4, 0x77, 0x53, 0xad, 0x6b, 0x42, 0x5b, 0x68, 0x54, 0x18, 0x02, - 0xd1, 0xee, 0xd1, 0x1e, 0x1b, 0xf7, 0x81, 0xc9, 0xce, 0xe8, 0x5b, 0xc8, 0x52, 0x67, 0x8c, 0xc9, - 0x94, 0x76, 0x87, 0xd8, 0x19, 0x0c, 0x29, 0x1b, 0x78, 0x66, 0x67, 0xdf, 0xf9, 0x9b, 0x68, 0x76, - 0x52, 0x3e, 0x67, 0x8c, 0xea, 0xfd, 0x60, 0x59, 0x37, 0xed, 0xd8, 0x8d, 0xd7, 0xcc, 0xc3, 0x10, - 0xe0, 0x6c, 0x64, 0x40, 0x21, 0x62, 0x04, 0x4f, 0x9f, 0xf6, 0xc6, 0x13, 0x29, 0x15, 0x8c, 0xab, - 0x7a, 0x6f, 0xb5, 0x54, 0xa4, 0xdd, 0x24, 0x6b, 0x8a, 0x66, 0xe6, 0x43, 0xac, 0x13, 0x41, 0xe1, - 0x06, 0xfc, 0x26, 0x40, 0x86, 0x6f, 0x00, 0xfb, 0xcd, 0xfe, 0x03, 0xab, 0xb7, 0xb3, 0x69, 0x7b, - 0xb7, 0x36, 0x2d, 0xea, 0xaa, 0xb8, 0xe9, 0x6a, 0x58, 0xe8, 0x8f, 0x02, 0xa4, 0x78, 0xa1, 0x86, - 0xfd, 0x2f, 0x57, 0x19, 0x56, 0xd4, 0x84, 0xdc, 0x99, 0xf5, 0xca, 0x25, 0x97, 0x23, 0x6c, 0x0f, - 0xf0, 0x18, 0xbb, 0x14, 0x49, 0x90, 0xf4, 0xb0, 0x3f, 0x1d, 0x51, 0xe9, 0x28, 0xb8, 0xc0, 0x79, - 0xcc, 0x0c, 0x6d, 0x54, 0x84, 0x04, 0xf6, 0x3c, 0xe2, 0x49, 0xc5, 0x40, 0xff, 0x3c, 0x66, 0x72, - 0xb3, 0x0a, 0x90, 0xf2, 0xb0, 0x3f, 0x21, 0xae, 0x8f, 0x1f, 0xfd, 0x10, 0x87, 0x44, 0x3b, 0x7c, - 0x65, 0x2a, 0xed, 0xce, 0x59, 0x47, 0xef, 0x5e, 0x34, 0x8c, 0x86, 0xd1, 0x31, 0xce, 0x5e, 0x18, - 0x2f, 0xf5, 0x7a, 0xf7, 0xa2, 0xd1, 0x6e, 0xe9, 0x35, 0xe3, 0xb9, 0xa1, 0xd7, 0xf3, 0x31, 0xb9, - 0x30, 0x5f, 0xa8, 0x87, 0x3b, 0x04, 0x24, 0x01, 0xf0, 0xb8, 0x00, 0xcc, 0x0b, 0x72, 0x6a, 0xbe, - 0x50, 0xc5, 0xe0, 0x8c, 0x4a, 0x70, 0xc8, 0x3d, 0x1d, 0xf3, 0x9b, 0x66, 0x4b, 0x6f, 0xe4, 0xe3, - 0x72, 0x66, 0xbe, 0x50, 0xf7, 0x43, 0x73, 0x13, 0xc9, 0x9c, 0x7b, 0x3c, 0x92, 0x79, 0xee, 0xc1, - 0x01, 0xf7, 0xd4, 0x5e, 0x34, 0xdb, 0x7a, 0x3d, 0x2f, 0xca, 0x30, 0x5f, 0xa8, 0x49, 0x6e, 0xa1, - 0x8f, 0xa1, 0xb0, 0x51, 0xbc, 0x68, 0x7d, 0x65, 0x9e, 0xd5, 0xf5, 0x7c, 0x42, 0xce, 0xcd, 0x17, - 0x6a, 0x66, 0x0b, 0x42, 0x0f, 0x21, 0xbf, 0xd6, 0x8f, 0x68, 0x49, 0x39, 0x3b, 0x5f, 0xa8, 0xb0, - 0x41, 0x64, 0xf1, 0xf5, 0x2f, 0xa5, 0xd8, 0xa3, 0x4b, 0x48, 0xb0, 0xff, 0x02, 0xf4, 0x10, 0x8a, - 0x4d, 0xb3, 0xae, 0x9b, 0xdd, 0x46, 0xb3, 0xa1, 0xdf, 0xba, 0x3d, 0x2b, 0x30, 0xc0, 0x91, 0x06, - 0x39, 0xce, 0xba, 0x68, 0xb0, 0xa7, 0x5e, 0xcf, 0x0b, 0xf2, 0xe1, 0x7c, 0xa1, 0xa6, 0xd7, 0x40, - 0x70, 0x7d, 0xce, 0x89, 0x18, 0xe1, 0xf5, 0x43, 0x93, 0x0b, 0x57, 0xdb, 0x6f, 0xae, 0x4b, 0xc2, - 0xdb, 0xeb, 0x92, 0xf0, 0xc7, 0x75, 0x49, 0xf8, 0xe9, 0xa6, 0x14, 0x7b, 0x7b, 0x53, 0x8a, 0xfd, - 0x7e, 0x53, 0x8a, 0xbd, 0xfc, 0x62, 0xe0, 0xd0, 0xe1, 0xb4, 0x5f, 0xb6, 0xc8, 0xb8, 0x62, 0x11, - 0x7f, 0x4c, 0xfc, 0x8a, 0xd3, 0xb7, 0x1e, 0x0f, 0x48, 0x65, 0x76, 0x5a, 0x19, 0x13, 0x7b, 0x3a, - 0xc2, 0x3e, 0xff, 0xe8, 0x78, 0x72, 0xfa, 0x38, 0xfa, 0x8a, 0xa1, 0x57, 0x13, 0xec, 0xf7, 0x93, - 0xec, 0xab, 0xe3, 0xd3, 0xbf, 0x03, 0x00, 0x00, 0xff, 0xff, 0xff, 0xee, 0x5e, 0xa5, 0xe6, 0x08, - 0x00, 0x00, + // 961 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x56, 0x4f, 0x8f, 0xda, 0x56, + 0x10, 0xc7, 0xac, 0x61, 0x61, 0xd8, 0xe5, 0xcf, 0x4b, 0x97, 0xb8, 0x6e, 0x82, 0x1d, 0x2b, 0xaa, + 0x56, 0xa9, 0x02, 0xd9, 0x34, 0x6a, 0xd4, 0x9c, 0xba, 0x80, 0xd3, 0xb5, 0x1a, 0x01, 0x32, 0xec, + 0xa1, 0xb9, 0x50, 0xb0, 0x5f, 0xc1, 0x0a, 0xf8, 0x51, 0xfb, 0xc1, 0x6a, 0x3f, 0x40, 0xa5, 0x88, + 0x4b, 0xfb, 0x05, 0x90, 0x2a, 0x55, 0xed, 0x67, 0xc9, 0x31, 0xc7, 0x9e, 0x50, 0xb5, 0x7b, 0xe8, + 0x9d, 0x2f, 0xd0, 0xca, 0xef, 0xd9, 0xfc, 0xd9, 0x44, 0x39, 0xb6, 0x97, 0x9e, 0xfc, 0xe6, 0x37, + 0xbf, 0x99, 0xdf, 0xbc, 0x99, 0xc1, 0x18, 0xee, 0x39, 0x7d, 0xab, 0x62, 0x11, 0x0f, 0x57, 0xac, + 0x61, 0xcf, 0x75, 0xf1, 0xa8, 0x32, 0x3b, 0x89, 0x8e, 0xe5, 0x89, 0x47, 0x28, 0x41, 0xb7, 0x9c, + 0xbe, 0x55, 0x0e, 0x28, 0xe5, 0x08, 0x9f, 0x9d, 0xc8, 0x1f, 0x0d, 0xc8, 0x80, 0x30, 0x7f, 0x25, + 0x38, 0x71, 0xaa, 0xac, 0x6c, 0xb2, 0x8d, 0x1c, 0xec, 0x52, 0x96, 0x8c, 0x9d, 0x38, 0x41, 0xfb, + 0x2d, 0x0e, 0xfb, 0x35, 0x9e, 0x05, 0x3d, 0x82, 0x84, 0x4f, 0x7b, 0x14, 0x4b, 0x82, 0x2a, 0x1c, + 0x67, 0x1f, 0xcb, 0xe5, 0xf7, 0xe8, 0x94, 0xdb, 0x01, 0xc3, 0xe4, 0x44, 0xf4, 0x05, 0xa4, 0x88, + 0x67, 0x63, 0xcf, 0x71, 0x07, 0x52, 0xfc, 0x03, 0x41, 0xcd, 0x80, 0x64, 0xae, 0xb9, 0xe8, 0x1b, + 0x38, 0xb0, 0xc8, 0xd4, 0xa5, 0xd8, 0x9b, 0xf4, 0x3c, 0x7a, 0x29, 0xed, 0xa9, 0xc2, 0x71, 0xe6, + 0xf1, 0xbd, 0xf7, 0xc6, 0xd6, 0xb6, 0x88, 0x55, 0xf1, 0xcd, 0x52, 0x89, 0x99, 0x3b, 0xc1, 0xa8, + 0x06, 0x39, 0x8b, 0xb8, 0x2e, 0xb6, 0xa8, 0x43, 0xdc, 0xee, 0x90, 0x4c, 0x7c, 0x49, 0x54, 0xf7, + 0x8e, 0xd3, 0x55, 0x79, 0xb5, 0x54, 0x8a, 0x97, 0xbd, 0xf1, 0xe8, 0x99, 0x76, 0x83, 0xa0, 0x99, + 0xd9, 0x0d, 0x72, 0x46, 0x26, 0x3e, 0x92, 0x60, 0x7f, 0x86, 0x3d, 0xdf, 0x21, 0xae, 0x94, 0x50, + 0x85, 0xe3, 0xb4, 0x19, 0x99, 0xcf, 0xc4, 0xd7, 0xbf, 0x28, 0x31, 0xed, 0xaf, 0x38, 0x14, 0x0c, + 0x1b, 0xbb, 0xd4, 0xf9, 0xde, 0xc1, 0xf6, 0xff, 0x1d, 0xfb, 0x40, 0xc7, 0xd0, 0x6d, 0xd8, 0x9f, + 0x10, 0x8f, 0x76, 0x1d, 0x5b, 0x4a, 0x32, 0x4f, 0x32, 0x30, 0x0d, 0x1b, 0xdd, 0x05, 0x08, 0xcb, + 0x0c, 0x7c, 0xfb, 0xcc, 0x97, 0x0e, 0x11, 0xc3, 0x0e, 0x3b, 0x7d, 0x01, 0x07, 0xdb, 0x17, 0x40, + 0x9f, 0x6d, 0xb2, 0x05, 0x5d, 0x4e, 0x57, 0xd1, 0x6a, 0xa9, 0x64, 0x79, 0x91, 0xa1, 0x43, 0x5b, + 0x2b, 0x3c, 0xd9, 0x51, 0x88, 0x33, 0xfe, 0xd1, 0x6a, 0xa9, 0x14, 0xc2, 0x4b, 0xad, 0x7d, 0xda, + 0xbb, 0xc2, 0x7f, 0xef, 0x41, 0xb2, 0xd5, 0xb3, 0x5e, 0x61, 0x8a, 0x64, 0x48, 0xf9, 0xf8, 0x87, + 0x29, 0x76, 0x2d, 0x3e, 0x5a, 0xd1, 0x5c, 0xdb, 0xe8, 0x29, 0x64, 0x7c, 0x32, 0xf5, 0x2c, 0xdc, + 0x0d, 0x34, 0x43, 0x8d, 0xe2, 0x6a, 0xa9, 0x20, 0xae, 0xb1, 0xe5, 0xd4, 0x4c, 0xe0, 0x56, 0x8b, + 0x78, 0x14, 0x7d, 0x05, 0xd9, 0xd0, 0x17, 0x2a, 0xb3, 0x21, 0xa6, 0xab, 0x1f, 0xaf, 0x96, 0xca, + 0xd1, 0x4e, 0x6c, 0xe8, 0xd7, 0xcc, 0x43, 0x0e, 0x44, 0xeb, 0xf6, 0x1c, 0xf2, 0x36, 0xf6, 0xa9, + 0xe3, 0xf6, 0xd8, 0x5c, 0x98, 0xbe, 0xc8, 0x72, 0x7c, 0xb2, 0x5a, 0x2a, 0xb7, 0x79, 0x8e, 0x9b, + 0x0c, 0xcd, 0xcc, 0x6d, 0x41, 0xac, 0x92, 0x26, 0xdc, 0xda, 0x66, 0x45, 0xe5, 0xb0, 0x31, 0x56, + 0x4b, 0xab, 0xa5, 0x22, 0xbf, 0x9b, 0x6a, 0x5d, 0x13, 0xda, 0x42, 0xa3, 0xc2, 0x10, 0x88, 0x76, + 0x8f, 0xf6, 0xd8, 0xb8, 0x0f, 0x4c, 0x76, 0x46, 0xdf, 0x41, 0x96, 0x3a, 0x63, 0x4c, 0xa6, 0xb4, + 0x3b, 0xc4, 0xce, 0x60, 0x48, 0xd9, 0xc0, 0x33, 0x3b, 0xfb, 0xce, 0xdf, 0x44, 0xb3, 0x93, 0xf2, + 0x19, 0x63, 0x54, 0xef, 0x06, 0xcb, 0xba, 0x69, 0xc7, 0x6e, 0xbc, 0x66, 0x1e, 0x86, 0x00, 0x67, + 0x23, 0x03, 0x0a, 0x11, 0x23, 0x78, 0xfa, 0xb4, 0x37, 0x9e, 0x48, 0xa9, 0x60, 0x5c, 0xd5, 0x3b, + 0xab, 0xa5, 0x22, 0xed, 0x26, 0x59, 0x53, 0x34, 0x33, 0x1f, 0x62, 0x9d, 0x08, 0x0a, 0x37, 0xe0, + 0x77, 0x01, 0x32, 0x7c, 0x03, 0xd8, 0x6f, 0xf6, 0x5f, 0x58, 0xbd, 0x9d, 0x4d, 0xdb, 0xbb, 0xb1, + 0x69, 0x51, 0x57, 0xc5, 0x4d, 0x57, 0xc3, 0x42, 0x7f, 0x12, 0x20, 0xc5, 0x0b, 0x35, 0xec, 0xff, + 0xb8, 0xca, 0xb0, 0xa2, 0x26, 0xe4, 0x4e, 0xad, 0x57, 0x2e, 0xb9, 0x18, 0x61, 0x7b, 0x80, 0xc7, + 0xd8, 0xa5, 0x48, 0x82, 0xa4, 0x87, 0xfd, 0xe9, 0x88, 0x4a, 0x47, 0xc1, 0x05, 0xce, 0x62, 0x66, + 0x68, 0xa3, 0x22, 0x24, 0xb0, 0xe7, 0x11, 0x4f, 0x2a, 0x06, 0xfa, 0x67, 0x31, 0x93, 0x9b, 0x55, + 0x80, 0x94, 0x87, 0xfd, 0x09, 0x71, 0x7d, 0xfc, 0xe0, 0xc7, 0x38, 0x24, 0xda, 0xe1, 0x2b, 0x53, + 0x69, 0x77, 0x4e, 0x3b, 0x7a, 0xf7, 0xbc, 0x61, 0x34, 0x8c, 0x8e, 0x71, 0xfa, 0xc2, 0x78, 0xa9, + 0xd7, 0xbb, 0xe7, 0x8d, 0x76, 0x4b, 0xaf, 0x19, 0xcf, 0x0d, 0xbd, 0x9e, 0x8f, 0xc9, 0x85, 0xf9, + 0x42, 0x3d, 0xdc, 0x21, 0x20, 0x09, 0x80, 0xc7, 0x05, 0x60, 0x5e, 0x90, 0x53, 0xf3, 0x85, 0x2a, + 0x06, 0x67, 0x54, 0x82, 0x43, 0xee, 0xe9, 0x98, 0xdf, 0x36, 0x5b, 0x7a, 0x23, 0x1f, 0x97, 0x33, + 0xf3, 0x85, 0xba, 0x1f, 0x9a, 0x9b, 0x48, 0xe6, 0xdc, 0xe3, 0x91, 0xcc, 0x73, 0x07, 0x0e, 0xb8, + 0xa7, 0xf6, 0xa2, 0xd9, 0xd6, 0xeb, 0x79, 0x51, 0x86, 0xf9, 0x42, 0x4d, 0x72, 0x0b, 0x7d, 0x0a, + 0x85, 0x8d, 0xe2, 0x79, 0xeb, 0x6b, 0xf3, 0xb4, 0xae, 0xe7, 0x13, 0x72, 0x6e, 0xbe, 0x50, 0x33, + 0x5b, 0x10, 0xba, 0x0f, 0xf9, 0xb5, 0x7e, 0x44, 0x4b, 0xca, 0xd9, 0xf9, 0x42, 0x85, 0x0d, 0x22, + 0x8b, 0xaf, 0x7f, 0x2d, 0xc5, 0x1e, 0x5c, 0x40, 0x82, 0xfd, 0x17, 0xa0, 0xfb, 0x50, 0x6c, 0x9a, + 0x75, 0xdd, 0xec, 0x36, 0x9a, 0x0d, 0xfd, 0xc6, 0xed, 0x59, 0x81, 0x01, 0x8e, 0x34, 0xc8, 0x71, + 0xd6, 0x79, 0x83, 0x3d, 0xf5, 0x7a, 0x5e, 0x90, 0x0f, 0xe7, 0x0b, 0x35, 0xbd, 0x06, 0x82, 0xeb, + 0x73, 0x4e, 0xc4, 0x08, 0xaf, 0x1f, 0x9a, 0x5c, 0xb8, 0xda, 0x7e, 0x73, 0x55, 0x12, 0xde, 0x5e, + 0x95, 0x84, 0x3f, 0xaf, 0x4a, 0xc2, 0xcf, 0xd7, 0xa5, 0xd8, 0xdb, 0xeb, 0x52, 0xec, 0x8f, 0xeb, + 0x52, 0xec, 0xe5, 0x97, 0x03, 0x87, 0x0e, 0xa7, 0xfd, 0xb2, 0x45, 0xc6, 0x15, 0x8b, 0xf8, 0x63, + 0xe2, 0x57, 0x9c, 0xbe, 0xf5, 0x70, 0x40, 0x2a, 0xb3, 0xa7, 0x95, 0x31, 0xb1, 0xa7, 0x23, 0xec, + 0xf3, 0x8f, 0x8e, 0x47, 0x4f, 0x1e, 0x46, 0x5f, 0x31, 0xf4, 0x72, 0x82, 0xfd, 0x7e, 0x92, 0x7d, + 0x75, 0x7c, 0xfe, 0x4f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x04, 0xf2, 0xc5, 0x76, 0xe6, 0x08, 0x00, + 0x00, } func (m *Channel) Marshal() (dAtA []byte, err error) { diff --git a/modules/core/04-channel/types/channel_test.go b/modules/core/04-channel/types/channel_test.go index 95c4dea4d43..0817af7fc81 100644 --- a/modules/core/04-channel/types/channel_test.go +++ b/modules/core/04-channel/types/channel_test.go @@ -5,7 +5,7 @@ import ( "github.com/stretchr/testify/require" - "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" + "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" ) func TestChannelValidateBasic(t *testing.T) { diff --git a/modules/core/04-channel/types/codec.go b/modules/core/04-channel/types/codec.go index 62114555778..78ec00e89bd 100644 --- a/modules/core/04-channel/types/codec.go +++ b/modules/core/04-channel/types/codec.go @@ -6,7 +6,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/msgservice" - "github.com/cosmos/ibc-go/v4/modules/core/exported" + "github.com/cosmos/ibc-go/v7/modules/core/exported" ) // RegisterInterfaces register the ibc channel submodule interfaces to protobuf diff --git a/modules/core/04-channel/types/errors.go b/modules/core/04-channel/types/errors.go index d557399a814..e8512831a77 100644 --- a/modules/core/04-channel/types/errors.go +++ b/modules/core/04-channel/types/errors.go @@ -1,43 +1,44 @@ package types import ( - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + errorsmod "cosmossdk.io/errors" ) // IBC channel sentinel errors var ( - ErrChannelExists = sdkerrors.Register(SubModuleName, 2, "channel already exists") - ErrChannelNotFound = sdkerrors.Register(SubModuleName, 3, "channel not found") - ErrInvalidChannel = sdkerrors.Register(SubModuleName, 4, "invalid channel") - ErrInvalidChannelState = sdkerrors.Register(SubModuleName, 5, "invalid channel state") - ErrInvalidChannelOrdering = sdkerrors.Register(SubModuleName, 6, "invalid channel ordering") - ErrInvalidCounterparty = sdkerrors.Register(SubModuleName, 7, "invalid counterparty channel") - ErrInvalidChannelCapability = sdkerrors.Register(SubModuleName, 8, "invalid channel capability") - ErrChannelCapabilityNotFound = sdkerrors.Register(SubModuleName, 9, "channel capability not found") - ErrSequenceSendNotFound = sdkerrors.Register(SubModuleName, 10, "sequence send not found") - ErrSequenceReceiveNotFound = sdkerrors.Register(SubModuleName, 11, "sequence receive not found") - ErrSequenceAckNotFound = sdkerrors.Register(SubModuleName, 12, "sequence acknowledgement not found") - ErrInvalidPacket = sdkerrors.Register(SubModuleName, 13, "invalid packet") - ErrPacketTimeout = sdkerrors.Register(SubModuleName, 14, "packet timeout") - ErrTooManyConnectionHops = sdkerrors.Register(SubModuleName, 15, "too many connection hops") - ErrInvalidAcknowledgement = sdkerrors.Register(SubModuleName, 16, "invalid acknowledgement") - ErrAcknowledgementExists = sdkerrors.Register(SubModuleName, 17, "acknowledgement for packet already exists") - ErrInvalidChannelIdentifier = sdkerrors.Register(SubModuleName, 18, "invalid channel identifier") + ErrChannelExists = errorsmod.Register(SubModuleName, 2, "channel already exists") + ErrChannelNotFound = errorsmod.Register(SubModuleName, 3, "channel not found") + ErrInvalidChannel = errorsmod.Register(SubModuleName, 4, "invalid channel") + ErrInvalidChannelState = errorsmod.Register(SubModuleName, 5, "invalid channel state") + ErrInvalidChannelOrdering = errorsmod.Register(SubModuleName, 6, "invalid channel ordering") + ErrInvalidCounterparty = errorsmod.Register(SubModuleName, 7, "invalid counterparty channel") + ErrInvalidChannelCapability = errorsmod.Register(SubModuleName, 8, "invalid channel capability") + ErrChannelCapabilityNotFound = errorsmod.Register(SubModuleName, 9, "channel capability not found") + ErrSequenceSendNotFound = errorsmod.Register(SubModuleName, 10, "sequence send not found") + ErrSequenceReceiveNotFound = errorsmod.Register(SubModuleName, 11, "sequence receive not found") + ErrSequenceAckNotFound = errorsmod.Register(SubModuleName, 12, "sequence acknowledgement not found") + ErrInvalidPacket = errorsmod.Register(SubModuleName, 13, "invalid packet") + ErrPacketTimeout = errorsmod.Register(SubModuleName, 14, "packet timeout") + ErrTooManyConnectionHops = errorsmod.Register(SubModuleName, 15, "too many connection hops") + ErrInvalidAcknowledgement = errorsmod.Register(SubModuleName, 16, "invalid acknowledgement") + ErrAcknowledgementExists = errorsmod.Register(SubModuleName, 17, "acknowledgement for packet already exists") + ErrInvalidChannelIdentifier = errorsmod.Register(SubModuleName, 18, "invalid channel identifier") // packets already relayed errors - ErrPacketReceived = sdkerrors.Register(SubModuleName, 19, "packet already received") - ErrPacketCommitmentNotFound = sdkerrors.Register(SubModuleName, 20, "packet commitment not found") // may occur for already received acknowledgements or timeouts and in rare cases for packets never sent + ErrPacketReceived = errorsmod.Register(SubModuleName, 19, "packet already received") + ErrPacketCommitmentNotFound = errorsmod.Register(SubModuleName, 20, "packet commitment not found") // may occur for already received acknowledgements or timeouts and in rare cases for packets never sent // ORDERED channel error - ErrPacketSequenceOutOfOrder = sdkerrors.Register(SubModuleName, 21, "packet sequence is out of order") + ErrPacketSequenceOutOfOrder = errorsmod.Register(SubModuleName, 21, "packet sequence is out of order") // Antehandler error - ErrRedundantTx = sdkerrors.Register(SubModuleName, 22, "packet messages are redundant") + ErrRedundantTx = errorsmod.Register(SubModuleName, 22, "packet messages are redundant") // Perform a no-op on the current Msg - ErrNoOpMsg = sdkerrors.Register(SubModuleName, 23, "message is redundant, no-op will be performed") + ErrNoOpMsg = errorsmod.Register(SubModuleName, 23, "message is redundant, no-op will be performed") - ErrInvalidChannelVersion = sdkerrors.Register(SubModuleName, 24, "invalid channel version") - ErrPacketNotSent = sdkerrors.Register(SubModuleName, 25, "packet has not been sent") - ErrInvalidUpgradeTimeout = sdkerrors.Register(SubModuleName, 26, "either timeout height or timeout timestamp must be non-zero") + ErrInvalidChannelVersion = errorsmod.Register(SubModuleName, 24, "invalid channel version") + ErrPacketNotSent = errorsmod.Register(SubModuleName, 25, "packet has not been sent") + ErrInvalidTimeout = errorsmod.Register(SubModuleName, 26, "invalid packet timeout") + ErrInvalidUpgradeTimeout = errorsmod.Register(SubModuleName, 27, "either timeout height or timeout timestamp must be non-zero") ) diff --git a/modules/core/04-channel/types/events.go b/modules/core/04-channel/types/events.go index ad9fcb922eb..3b58e1da8ae 100644 --- a/modules/core/04-channel/types/events.go +++ b/modules/core/04-channel/types/events.go @@ -3,7 +3,7 @@ package types import ( "fmt" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" + ibcexported "github.com/cosmos/ibc-go/v7/modules/core/exported" ) // IBC channel events @@ -22,9 +22,9 @@ const ( EventTypeTimeoutPacket = "timeout_packet" EventTypeTimeoutPacketOnClose = "timeout_on_close_packet" - // NOTE: DEPRECATED in favor of AttributeKeyDataHex + // Deprecated: in favor of AttributeKeyDataHex AttributeKeyData = "packet_data" - // NOTE: DEPRECATED in favor of AttributeKeyAckHex + // Deprecated: in favor of AttributeKeyAckHex AttributeKeyAck = "packet_ack" AttributeKeyDataHex = "packet_data_hex" @@ -50,5 +50,5 @@ var ( EventTypeChannelCloseConfirm = "channel_close_confirm" EventTypeChannelClosed = "channel_close" - AttributeValueCategory = fmt.Sprintf("%s_%s", host.ModuleName, SubModuleName) + AttributeValueCategory = fmt.Sprintf("%s_%s", ibcexported.ModuleName, SubModuleName) ) diff --git a/modules/core/04-channel/types/expected_keepers.go b/modules/core/04-channel/types/expected_keepers.go index 41be5a00e24..3a5460502c6 100644 --- a/modules/core/04-channel/types/expected_keepers.go +++ b/modules/core/04-channel/types/expected_keepers.go @@ -4,8 +4,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - connectiontypes "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types" - "github.com/cosmos/ibc-go/v4/modules/core/exported" + connectiontypes "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" + "github.com/cosmos/ibc-go/v7/modules/core/exported" ) // ClientKeeper expected account IBC client keeper diff --git a/modules/core/04-channel/types/genesis.go b/modules/core/04-channel/types/genesis.go index 78d8ae2b04a..372fdcd4633 100644 --- a/modules/core/04-channel/types/genesis.go +++ b/modules/core/04-channel/types/genesis.go @@ -4,7 +4,7 @@ import ( "errors" "fmt" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" ) // NewPacketState creates a new PacketState instance. @@ -76,7 +76,7 @@ func DefaultGenesisState() GenesisState { func (gs GenesisState) Validate() error { // keep track of the max sequence to ensure it is less than // the next sequence used in creating connection identifers. - var maxSequence uint64 = 0 + var maxSequence uint64 for i, channel := range gs.Channels { sequence, err := ParseChannelSequence(channel.ChannelId) diff --git a/modules/core/04-channel/types/genesis.pb.go b/modules/core/04-channel/types/genesis.pb.go index 6dc5f5b2f4c..4c6b61988f0 100644 --- a/modules/core/04-channel/types/genesis.pb.go +++ b/modules/core/04-channel/types/genesis.pb.go @@ -5,8 +5,8 @@ package types import ( fmt "fmt" - _ "github.com/gogo/protobuf/gogoproto" - proto "github.com/gogo/protobuf/proto" + _ "github.com/cosmos/gogoproto/gogoproto" + proto "github.com/cosmos/gogoproto/proto" io "io" math "math" math_bits "math/bits" @@ -195,39 +195,39 @@ func init() { func init() { proto.RegisterFile("ibc/core/channel/v1/genesis.proto", fileDescriptor_cb06ec201f452595) } var fileDescriptor_cb06ec201f452595 = []byte{ - // 506 bytes of a gzipped FileDescriptorProto + // 507 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x93, 0xcf, 0x6e, 0xd3, 0x40, - 0x10, 0x87, 0xe3, 0x26, 0x4d, 0xd3, 0x6d, 0x13, 0xd1, 0x6d, 0x23, 0x99, 0xa8, 0xd8, 0xc1, 0x48, - 0x28, 0x12, 0xaa, 0x4d, 0x21, 0x17, 0x38, 0x9a, 0x03, 0xe4, 0x86, 0xb6, 0x9c, 0x90, 0x50, 0x64, - 0xaf, 0xa7, 0xee, 0x2a, 0xb1, 0x37, 0x78, 0x37, 0x81, 0x3e, 0x05, 0x3c, 0x56, 0x8f, 0x3d, 0x72, - 0xb2, 0x50, 0xf2, 0x06, 0x39, 0x72, 0x42, 0xfe, 0x9b, 0x44, 0x8d, 0x10, 0xed, 0xcd, 0x3b, 0xf3, - 0x9b, 0xef, 0x9b, 0x83, 0x07, 0x3d, 0x65, 0x2e, 0xb5, 0x28, 0x8f, 0xc0, 0xa2, 0x57, 0x4e, 0x18, - 0xc2, 0xd8, 0x9a, 0x9d, 0x5b, 0x3e, 0x84, 0x20, 0x98, 0x30, 0x27, 0x11, 0x97, 0x1c, 0x1f, 0x33, - 0x97, 0x9a, 0x49, 0xc4, 0xcc, 0x23, 0xe6, 0xec, 0xbc, 0x73, 0xe2, 0x73, 0x9f, 0xa7, 0x7d, 0x2b, - 0xf9, 0xca, 0xa2, 0x9d, 0xad, 0xb4, 0x62, 0x2a, 0x8d, 0x18, 0x8b, 0x5d, 0x74, 0xf8, 0x3e, 0xe3, - 0x5f, 0x48, 0x47, 0x02, 0xfe, 0x82, 0x1a, 0x79, 0x42, 0xa8, 0x4a, 0xb7, 0xda, 0x3b, 0x78, 0xf5, - 0xdc, 0xdc, 0x62, 0x34, 0x07, 0x1e, 0x84, 0x92, 0x5d, 0x32, 0xf0, 0xde, 0x65, 0x45, 0xfb, 0xf1, - 0x4d, 0xac, 0x57, 0xfe, 0xc4, 0xfa, 0xd1, 0x9d, 0x16, 0x29, 0x91, 0x98, 0xa0, 0x47, 0x0e, 0x1d, - 0x85, 0xfc, 0xdb, 0x18, 0x3c, 0x1f, 0x02, 0x08, 0xa5, 0x50, 0x77, 0x52, 0x4d, 0x77, 0xab, 0xe6, - 0xa3, 0x43, 0x47, 0x20, 0xd3, 0xd5, 0xec, 0x5a, 0x22, 0x20, 0x77, 0xe6, 0xf1, 0x07, 0x74, 0x40, - 0x79, 0x10, 0x30, 0x99, 0xe1, 0xaa, 0xf7, 0xc2, 0xad, 0x8f, 0x62, 0x1b, 0x35, 0x22, 0xa0, 0xc0, - 0x26, 0x52, 0xa8, 0xb5, 0x7b, 0x61, 0xca, 0x39, 0xcc, 0x50, 0x4b, 0x40, 0xe8, 0x0d, 0x05, 0x7c, - 0x9d, 0x42, 0x48, 0x41, 0xa8, 0xbb, 0x29, 0xe9, 0xd9, 0xbf, 0x48, 0x79, 0xd6, 0x7e, 0x92, 0xc0, - 0x96, 0xb1, 0xde, 0xbe, 0x76, 0x82, 0xf1, 0x5b, 0x63, 0x13, 0x64, 0x90, 0x66, 0x52, 0x28, 0xc2, - 0xa9, 0x2a, 0x02, 0x3a, 0x5b, 0x53, 0xd5, 0x1f, 0xac, 0xda, 0x04, 0x19, 0xa4, 0x99, 0x14, 0x56, - 0xaa, 0x4b, 0xd4, 0x74, 0xe8, 0x68, 0xcd, 0xb4, 0xf7, 0xff, 0xa6, 0xd3, 0xdc, 0x74, 0x92, 0x99, - 0x36, 0x38, 0x06, 0x39, 0x74, 0xe8, 0x68, 0xe5, 0xf9, 0x84, 0xda, 0x21, 0x7c, 0x97, 0xc3, 0x9c, - 0x56, 0x06, 0xd5, 0x46, 0x57, 0xe9, 0xd5, 0xec, 0xee, 0x32, 0xd6, 0x4f, 0x33, 0xcc, 0xd6, 0x98, - 0x41, 0x8e, 0x93, 0x7a, 0xfe, 0xdf, 0x15, 0x58, 0xe3, 0x87, 0x82, 0x5a, 0x9b, 0x4b, 0xe1, 0x17, - 0x68, 0x6f, 0xc2, 0x23, 0x39, 0x64, 0x9e, 0xaa, 0x74, 0x95, 0xde, 0xbe, 0x8d, 0x97, 0xb1, 0xde, - 0xca, 0xd0, 0x79, 0xc3, 0x20, 0xf5, 0xe4, 0x6b, 0xe0, 0xe1, 0x3e, 0x42, 0x85, 0x89, 0x79, 0xea, - 0x4e, 0x9a, 0x6f, 0x2f, 0x63, 0xfd, 0x28, 0xcb, 0xaf, 0x7a, 0x06, 0xd9, 0xcf, 0x1f, 0x03, 0x0f, - 0x77, 0x50, 0xa3, 0x5c, 0xbf, 0x9a, 0xac, 0x4f, 0xca, 0xb7, 0x7d, 0x71, 0x33, 0xd7, 0x94, 0xdb, - 0xb9, 0xa6, 0xfc, 0x9e, 0x6b, 0xca, 0xcf, 0x85, 0x56, 0xb9, 0x5d, 0x68, 0x95, 0x5f, 0x0b, 0xad, - 0xf2, 0xf9, 0x8d, 0xcf, 0xe4, 0xd5, 0xd4, 0x35, 0x29, 0x0f, 0x2c, 0xca, 0x45, 0xc0, 0x85, 0xc5, - 0x5c, 0x7a, 0xe6, 0x73, 0x6b, 0xd6, 0xb7, 0x02, 0xee, 0x4d, 0xc7, 0x20, 0xb2, 0xa3, 0x7e, 0xd9, - 0x3f, 0x2b, 0xee, 0x5a, 0x5e, 0x4f, 0x40, 0xb8, 0xf5, 0xf4, 0xa6, 0x5f, 0xff, 0x0d, 0x00, 0x00, - 0xff, 0xff, 0x37, 0x95, 0x0a, 0xe0, 0x46, 0x04, 0x00, 0x00, + 0x10, 0x87, 0xe3, 0x36, 0x4d, 0xd3, 0x6d, 0x13, 0xd1, 0x6d, 0x23, 0x99, 0xa8, 0xd8, 0xc6, 0x48, + 0x28, 0x12, 0xaa, 0x4d, 0xa1, 0x12, 0x82, 0xa3, 0x39, 0x40, 0x6e, 0x68, 0xcb, 0x09, 0x09, 0x45, + 0xf6, 0x7a, 0xea, 0xae, 0x12, 0x7b, 0x83, 0x77, 0x13, 0xe8, 0x53, 0xc0, 0x63, 0xf5, 0xd8, 0x23, + 0x27, 0x0b, 0x25, 0x6f, 0x90, 0x23, 0x27, 0xe4, 0xbf, 0x49, 0xd4, 0x08, 0x51, 0x6e, 0xde, 0x99, + 0xdf, 0x7c, 0xdf, 0x1c, 0x3c, 0xe8, 0x31, 0xf3, 0xa8, 0x4d, 0x79, 0x0c, 0x36, 0xbd, 0x72, 0xa3, + 0x08, 0x46, 0xf6, 0xf4, 0xcc, 0x0e, 0x20, 0x02, 0xc1, 0x84, 0x35, 0x8e, 0xb9, 0xe4, 0xf8, 0x88, + 0x79, 0xd4, 0x4a, 0x23, 0x56, 0x11, 0xb1, 0xa6, 0x67, 0xdd, 0xe3, 0x80, 0x07, 0x3c, 0xeb, 0xdb, + 0xe9, 0x57, 0x1e, 0xed, 0x6e, 0xa4, 0x95, 0x53, 0x59, 0xc4, 0x9c, 0xef, 0xa0, 0x83, 0x77, 0x39, + 0xff, 0x42, 0xba, 0x12, 0xf0, 0x67, 0xd4, 0x2c, 0x12, 0x42, 0x55, 0x8c, 0xed, 0xde, 0xfe, 0x8b, + 0xa7, 0xd6, 0x06, 0xa3, 0xd5, 0xf7, 0x21, 0x92, 0xec, 0x92, 0x81, 0xff, 0x36, 0x2f, 0x3a, 0x0f, + 0x6f, 0x12, 0xbd, 0xf6, 0x3b, 0xd1, 0x0f, 0xef, 0xb4, 0x48, 0x85, 0xc4, 0x04, 0x3d, 0x70, 0xe9, + 0x30, 0xe2, 0x5f, 0x47, 0xe0, 0x07, 0x10, 0x42, 0x24, 0x85, 0xba, 0x95, 0x69, 0x8c, 0x8d, 0x9a, + 0x0f, 0x2e, 0x1d, 0x82, 0xcc, 0x56, 0x73, 0xea, 0xa9, 0x80, 0xdc, 0x99, 0xc7, 0xef, 0xd1, 0x3e, + 0xe5, 0x61, 0xc8, 0x64, 0x8e, 0xdb, 0xbe, 0x17, 0x6e, 0x75, 0x14, 0x3b, 0xa8, 0x19, 0x03, 0x05, + 0x36, 0x96, 0x42, 0xad, 0xdf, 0x0b, 0x53, 0xcd, 0x61, 0x86, 0xda, 0x02, 0x22, 0x7f, 0x20, 0xe0, + 0xcb, 0x04, 0x22, 0x0a, 0x42, 0xdd, 0xc9, 0x48, 0x4f, 0xfe, 0x46, 0x2a, 0xb2, 0xce, 0xa3, 0x14, + 0xb6, 0x48, 0xf4, 0xce, 0xb5, 0x1b, 0x8e, 0xde, 0x98, 0xeb, 0x20, 0x93, 0xb4, 0xd2, 0x42, 0x19, + 0xce, 0x54, 0x31, 0xd0, 0xe9, 0x8a, 0xaa, 0xf1, 0xdf, 0xaa, 0x75, 0x90, 0x49, 0x5a, 0x69, 0x61, + 0xa9, 0xba, 0x44, 0x2d, 0x97, 0x0e, 0x57, 0x4c, 0xbb, 0xff, 0x6e, 0x3a, 0x29, 0x4c, 0xc7, 0xb9, + 0x69, 0x8d, 0x63, 0x92, 0x03, 0x97, 0x0e, 0x97, 0x9e, 0x8f, 0xa8, 0x13, 0xc1, 0x37, 0x39, 0x28, + 0x68, 0x55, 0x50, 0x6d, 0x1a, 0x4a, 0xaf, 0xee, 0x18, 0x8b, 0x44, 0x3f, 0xc9, 0x31, 0x1b, 0x63, + 0x26, 0x39, 0x4a, 0xeb, 0xc5, 0x7f, 0x57, 0x62, 0xcd, 0xef, 0x0a, 0x6a, 0xaf, 0x2f, 0x85, 0x9f, + 0xa1, 0xdd, 0x31, 0x8f, 0xe5, 0x80, 0xf9, 0xaa, 0x62, 0x28, 0xbd, 0x3d, 0x07, 0x2f, 0x12, 0xbd, + 0x9d, 0xa3, 0x8b, 0x86, 0x49, 0x1a, 0xe9, 0x57, 0xdf, 0xc7, 0xe7, 0x08, 0x95, 0x26, 0xe6, 0xab, + 0x5b, 0x59, 0xbe, 0xb3, 0x48, 0xf4, 0xc3, 0x3c, 0xbf, 0xec, 0x99, 0x64, 0xaf, 0x78, 0xf4, 0x7d, + 0xdc, 0x45, 0xcd, 0x6a, 0xfd, 0xed, 0x74, 0x7d, 0x52, 0xbd, 0x9d, 0x8b, 0x9b, 0x99, 0xa6, 0xdc, + 0xce, 0x34, 0xe5, 0xd7, 0x4c, 0x53, 0x7e, 0xcc, 0xb5, 0xda, 0xed, 0x5c, 0xab, 0xfd, 0x9c, 0x6b, + 0xb5, 0x4f, 0xaf, 0x03, 0x26, 0xaf, 0x26, 0x9e, 0x45, 0x79, 0x68, 0x53, 0x2e, 0x42, 0x2e, 0x6c, + 0xe6, 0xd1, 0xd3, 0x80, 0xdb, 0xd3, 0x57, 0x76, 0xc8, 0xfd, 0xc9, 0x08, 0x44, 0x7e, 0xd4, 0xcf, + 0xcf, 0x4f, 0xcb, 0xbb, 0x96, 0xd7, 0x63, 0x10, 0x5e, 0x23, 0xbb, 0xe9, 0x97, 0x7f, 0x02, 0x00, + 0x00, 0xff, 0xff, 0xcc, 0x89, 0x91, 0x33, 0x46, 0x04, 0x00, 0x00, } func (m *GenesisState) Marshal() (dAtA []byte, err error) { diff --git a/modules/core/04-channel/types/genesis_test.go b/modules/core/04-channel/types/genesis_test.go index 0e1a64b02b6..10fd5248a59 100644 --- a/modules/core/04-channel/types/genesis_test.go +++ b/modules/core/04-channel/types/genesis_test.go @@ -5,7 +5,7 @@ import ( "github.com/stretchr/testify/require" - "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" + "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" ) const ( diff --git a/modules/core/04-channel/types/keys.go b/modules/core/04-channel/types/keys.go index 347fd91a677..d961abe3dc0 100644 --- a/modules/core/04-channel/types/keys.go +++ b/modules/core/04-channel/types/keys.go @@ -4,9 +4,9 @@ import ( "fmt" "regexp" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + errorsmod "cosmossdk.io/errors" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" ) const ( @@ -50,13 +50,18 @@ func IsValidChannelID(channelID string) bool { // ParseChannelSequence parses the channel sequence from the channel identifier. func ParseChannelSequence(channelID string) (uint64, error) { if !IsChannelIDFormat(channelID) { - return 0, sdkerrors.Wrap(host.ErrInvalidID, "channel identifier is not in the format: `channel-{N}`") + return 0, errorsmod.Wrap(host.ErrInvalidID, "channel identifier is not in the format: `channel-{N}`") } sequence, err := host.ParseIdentifier(channelID, ChannelPrefix) if err != nil { - return 0, sdkerrors.Wrap(err, "invalid channel identifier") + return 0, errorsmod.Wrap(err, "invalid channel identifier") } return sequence, nil } + +// FilteredPortPrefix returns the prefix key for the given port prefix. +func FilteredPortPrefix(portPrefix string) []byte { + return []byte(fmt.Sprintf("%s/%s/%s", host.KeyChannelEndPrefix, host.KeyPortPrefix, portPrefix)) +} diff --git a/modules/core/04-channel/types/keys_test.go b/modules/core/04-channel/types/keys_test.go index c6c2e3fcec2..72bd91b5613 100644 --- a/modules/core/04-channel/types/keys_test.go +++ b/modules/core/04-channel/types/keys_test.go @@ -5,7 +5,7 @@ import ( "github.com/stretchr/testify/require" - "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" + "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" ) // tests ParseChannelSequence and IsValidChannelID diff --git a/modules/core/04-channel/types/msgs.go b/modules/core/04-channel/types/msgs.go index 205f8be5b4a..d96e8fdb85f 100644 --- a/modules/core/04-channel/types/msgs.go +++ b/modules/core/04-channel/types/msgs.go @@ -3,19 +3,21 @@ package types import ( "encoding/base64" + errorsmod "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - commitmenttypes "github.com/cosmos/ibc-go/v4/modules/core/23-commitment/types" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" + ibcerrors "github.com/cosmos/ibc-go/v7/internal/errors" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + commitmenttypes "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" ) var _ sdk.Msg = &MsgChannelOpenInit{} // NewMsgChannelOpenInit creates a new MsgChannelOpenInit. It sets the counterparty channel // identifier to be empty. -// nolint:interfacer +// +//nolint:interfacer func NewMsgChannelOpenInit( portID, version string, channelOrder Order, connectionHops []string, counterpartyPortID string, signer string, @@ -32,20 +34,20 @@ func NewMsgChannelOpenInit( // ValidateBasic implements sdk.Msg func (msg MsgChannelOpenInit) ValidateBasic() error { if err := host.PortIdentifierValidator(msg.PortId); err != nil { - return sdkerrors.Wrap(err, "invalid port ID") + return errorsmod.Wrap(err, "invalid port ID") } if msg.Channel.State != INIT { - return sdkerrors.Wrapf(ErrInvalidChannelState, + return errorsmod.Wrapf(ErrInvalidChannelState, "channel state must be INIT in MsgChannelOpenInit. expected: %s, got: %s", INIT, msg.Channel.State, ) } if msg.Channel.Counterparty.ChannelId != "" { - return sdkerrors.Wrap(ErrInvalidCounterparty, "counterparty channel identifier must be empty") + return errorsmod.Wrap(ErrInvalidCounterparty, "counterparty channel identifier must be empty") } _, err := sdk.AccAddressFromBech32(msg.Signer) if err != nil { - return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) + return errorsmod.Wrapf(ibcerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) } return msg.Channel.ValidateBasic() } @@ -64,9 +66,10 @@ var _ sdk.Msg = &MsgChannelOpenTry{} // NewMsgChannelOpenTry creates a new MsgChannelOpenTry instance // The version string is deprecated and will be ignored by core IBC. // It is left as an argument for go API backwards compatibility. -// nolint:interfacer +// +//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 +77,6 @@ func NewMsgChannelOpenTry( channel := NewChannel(TRYOPEN, channelOrder, counterparty, connectionHops, version) return &MsgChannelOpenTry{ PortId: portID, - PreviousChannelId: previousChannelID, Channel: channel, CounterpartyVersion: counterpartyVersion, ProofInit: proofInit, @@ -86,33 +88,28 @@ func NewMsgChannelOpenTry( // ValidateBasic implements sdk.Msg func (msg MsgChannelOpenTry) ValidateBasic() error { if err := host.PortIdentifierValidator(msg.PortId); err != nil { - return sdkerrors.Wrap(err, "invalid port ID") + return errorsmod.Wrap(err, "invalid port ID") } if msg.PreviousChannelId != "" { - if !IsValidChannelID(msg.PreviousChannelId) { - return sdkerrors.Wrap(ErrInvalidChannelIdentifier, "invalid previous channel ID") - } + return errorsmod.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") - } - if msg.ProofHeight.IsZero() { - return sdkerrors.Wrap(sdkerrors.ErrInvalidHeight, "proof height must be non-zero") + return errorsmod.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit an empty proof init") } if msg.Channel.State != TRYOPEN { - return sdkerrors.Wrapf(ErrInvalidChannelState, + return errorsmod.Wrapf(ErrInvalidChannelState, "channel state must be TRYOPEN in MsgChannelOpenTry. expected: %s, got: %s", TRYOPEN, msg.Channel.State, ) } // counterparty validate basic allows empty counterparty channel identifiers if err := host.ChannelIdentifierValidator(msg.Channel.Counterparty.ChannelId); err != nil { - return sdkerrors.Wrap(err, "invalid counterparty channel ID") + return errorsmod.Wrap(err, "invalid counterparty channel ID") } _, err := sdk.AccAddressFromBech32(msg.Signer) if err != nil { - return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) + return errorsmod.Wrapf(ibcerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) } return msg.Channel.ValidateBasic() } @@ -129,7 +126,8 @@ func (msg MsgChannelOpenTry) GetSigners() []sdk.AccAddress { var _ sdk.Msg = &MsgChannelOpenAck{} // NewMsgChannelOpenAck creates a new MsgChannelOpenAck instance -// nolint:interfacer +// +//nolint:interfacer func NewMsgChannelOpenAck( portID, channelID, counterpartyChannelID string, cpv string, proofTry []byte, proofHeight clienttypes.Height, signer string, @@ -148,23 +146,20 @@ func NewMsgChannelOpenAck( // ValidateBasic implements sdk.Msg func (msg MsgChannelOpenAck) ValidateBasic() error { if err := host.PortIdentifierValidator(msg.PortId); err != nil { - return sdkerrors.Wrap(err, "invalid port ID") + return errorsmod.Wrap(err, "invalid port ID") } if !IsValidChannelID(msg.ChannelId) { return ErrInvalidChannelIdentifier } if err := host.ChannelIdentifierValidator(msg.CounterpartyChannelId); err != nil { - return sdkerrors.Wrap(err, "invalid counterparty channel ID") + return errorsmod.Wrap(err, "invalid counterparty channel ID") } if len(msg.ProofTry) == 0 { - return sdkerrors.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit an empty proof try") - } - if msg.ProofHeight.IsZero() { - return sdkerrors.Wrap(sdkerrors.ErrInvalidHeight, "proof height must be non-zero") + return errorsmod.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit an empty proof try") } _, err := sdk.AccAddressFromBech32(msg.Signer) if err != nil { - return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) + return errorsmod.Wrapf(ibcerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) } return nil } @@ -181,7 +176,8 @@ func (msg MsgChannelOpenAck) GetSigners() []sdk.AccAddress { var _ sdk.Msg = &MsgChannelOpenConfirm{} // NewMsgChannelOpenConfirm creates a new MsgChannelOpenConfirm instance -// nolint:interfacer +// +//nolint:interfacer func NewMsgChannelOpenConfirm( portID, channelID string, proofAck []byte, proofHeight clienttypes.Height, signer string, @@ -198,20 +194,17 @@ func NewMsgChannelOpenConfirm( // ValidateBasic implements sdk.Msg func (msg MsgChannelOpenConfirm) ValidateBasic() error { if err := host.PortIdentifierValidator(msg.PortId); err != nil { - return sdkerrors.Wrap(err, "invalid port ID") + return errorsmod.Wrap(err, "invalid port ID") } if !IsValidChannelID(msg.ChannelId) { return ErrInvalidChannelIdentifier } if len(msg.ProofAck) == 0 { - return sdkerrors.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit an empty proof ack") - } - if msg.ProofHeight.IsZero() { - return sdkerrors.Wrap(sdkerrors.ErrInvalidHeight, "proof height must be non-zero") + return errorsmod.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit an empty proof ack") } _, err := sdk.AccAddressFromBech32(msg.Signer) if err != nil { - return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) + return errorsmod.Wrapf(ibcerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) } return nil } @@ -228,7 +221,8 @@ func (msg MsgChannelOpenConfirm) GetSigners() []sdk.AccAddress { var _ sdk.Msg = &MsgChannelCloseInit{} // NewMsgChannelCloseInit creates a new MsgChannelCloseInit instance -// nolint:interfacer +// +//nolint:interfacer func NewMsgChannelCloseInit( portID string, channelID string, signer string, ) *MsgChannelCloseInit { @@ -242,14 +236,14 @@ func NewMsgChannelCloseInit( // ValidateBasic implements sdk.Msg func (msg MsgChannelCloseInit) ValidateBasic() error { if err := host.PortIdentifierValidator(msg.PortId); err != nil { - return sdkerrors.Wrap(err, "invalid port ID") + return errorsmod.Wrap(err, "invalid port ID") } if !IsValidChannelID(msg.ChannelId) { return ErrInvalidChannelIdentifier } _, err := sdk.AccAddressFromBech32(msg.Signer) if err != nil { - return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) + return errorsmod.Wrapf(ibcerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) } return nil } @@ -266,7 +260,8 @@ func (msg MsgChannelCloseInit) GetSigners() []sdk.AccAddress { var _ sdk.Msg = &MsgChannelCloseConfirm{} // NewMsgChannelCloseConfirm creates a new MsgChannelCloseConfirm instance -// nolint:interfacer +// +//nolint:interfacer func NewMsgChannelCloseConfirm( portID, channelID string, proofInit []byte, proofHeight clienttypes.Height, signer string, @@ -283,20 +278,17 @@ func NewMsgChannelCloseConfirm( // ValidateBasic implements sdk.Msg func (msg MsgChannelCloseConfirm) ValidateBasic() error { if err := host.PortIdentifierValidator(msg.PortId); err != nil { - return sdkerrors.Wrap(err, "invalid port ID") + return errorsmod.Wrap(err, "invalid port ID") } if !IsValidChannelID(msg.ChannelId) { return ErrInvalidChannelIdentifier } if len(msg.ProofInit) == 0 { - return sdkerrors.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit an empty proof init") - } - if msg.ProofHeight.IsZero() { - return sdkerrors.Wrap(sdkerrors.ErrInvalidHeight, "proof height must be non-zero") + return errorsmod.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit an empty proof init") } _, err := sdk.AccAddressFromBech32(msg.Signer) if err != nil { - return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) + return errorsmod.Wrapf(ibcerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) } return nil } @@ -313,7 +305,8 @@ func (msg MsgChannelCloseConfirm) GetSigners() []sdk.AccAddress { var _ sdk.Msg = &MsgRecvPacket{} // NewMsgRecvPacket constructs new MsgRecvPacket -// nolint:interfacer +// +//nolint:interfacer func NewMsgRecvPacket( packet Packet, proofCommitment []byte, proofHeight clienttypes.Height, signer string, @@ -329,14 +322,11 @@ func NewMsgRecvPacket( // ValidateBasic implements sdk.Msg func (msg MsgRecvPacket) ValidateBasic() error { if len(msg.ProofCommitment) == 0 { - return sdkerrors.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit an empty proof") - } - if msg.ProofHeight.IsZero() { - return sdkerrors.Wrap(sdkerrors.ErrInvalidHeight, "proof height must be non-zero") + return errorsmod.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit an empty proof") } _, err := sdk.AccAddressFromBech32(msg.Signer) if err != nil { - return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) + return errorsmod.Wrapf(ibcerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) } return msg.Packet.ValidateBasic() } @@ -360,7 +350,8 @@ func (msg MsgRecvPacket) GetSigners() []sdk.AccAddress { var _ sdk.Msg = &MsgTimeout{} // NewMsgTimeout constructs new MsgTimeout -// nolint:interfacer +// +//nolint:interfacer func NewMsgTimeout( packet Packet, nextSequenceRecv uint64, proofUnreceived []byte, proofHeight clienttypes.Height, signer string, @@ -377,17 +368,14 @@ func NewMsgTimeout( // ValidateBasic implements sdk.Msg func (msg MsgTimeout) ValidateBasic() error { if len(msg.ProofUnreceived) == 0 { - return sdkerrors.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit an empty unreceived proof") - } - if msg.ProofHeight.IsZero() { - return sdkerrors.Wrap(sdkerrors.ErrInvalidHeight, "proof height must be non-zero") + return errorsmod.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit an empty unreceived proof") } if msg.NextSequenceRecv == 0 { - return sdkerrors.Wrap(sdkerrors.ErrInvalidSequence, "next sequence receive cannot be 0") + return errorsmod.Wrap(ibcerrors.ErrInvalidSequence, "next sequence receive cannot be 0") } _, err := sdk.AccAddressFromBech32(msg.Signer) if err != nil { - return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) + return errorsmod.Wrapf(ibcerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) } return msg.Packet.ValidateBasic() } @@ -402,7 +390,8 @@ func (msg MsgTimeout) GetSigners() []sdk.AccAddress { } // NewMsgTimeoutOnClose constructs new MsgTimeoutOnClose -// nolint:interfacer +// +//nolint:interfacer func NewMsgTimeoutOnClose( packet Packet, nextSequenceRecv uint64, proofUnreceived, proofClose []byte, @@ -421,20 +410,17 @@ func NewMsgTimeoutOnClose( // ValidateBasic implements sdk.Msg func (msg MsgTimeoutOnClose) ValidateBasic() error { if msg.NextSequenceRecv == 0 { - return sdkerrors.Wrap(sdkerrors.ErrInvalidSequence, "next sequence receive cannot be 0") + return errorsmod.Wrap(ibcerrors.ErrInvalidSequence, "next sequence receive cannot be 0") } if len(msg.ProofUnreceived) == 0 { - return sdkerrors.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit an empty proof") + return errorsmod.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit an empty proof") } if len(msg.ProofClose) == 0 { - return sdkerrors.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit an empty proof of closed counterparty channel end") - } - if msg.ProofHeight.IsZero() { - return sdkerrors.Wrap(sdkerrors.ErrInvalidHeight, "proof height must be non-zero") + return errorsmod.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit an empty proof of closed counterparty channel end") } _, err := sdk.AccAddressFromBech32(msg.Signer) if err != nil { - return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) + return errorsmod.Wrapf(ibcerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) } return msg.Packet.ValidateBasic() } @@ -451,7 +437,8 @@ func (msg MsgTimeoutOnClose) GetSigners() []sdk.AccAddress { var _ sdk.Msg = &MsgAcknowledgement{} // NewMsgAcknowledgement constructs a new MsgAcknowledgement -// nolint:interfacer +// +//nolint:interfacer func NewMsgAcknowledgement( packet Packet, ack, proofAcked []byte, @@ -470,17 +457,14 @@ func NewMsgAcknowledgement( // ValidateBasic implements sdk.Msg func (msg MsgAcknowledgement) ValidateBasic() error { if len(msg.ProofAcked) == 0 { - return sdkerrors.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit an empty proof") - } - if msg.ProofHeight.IsZero() { - return sdkerrors.Wrap(sdkerrors.ErrInvalidHeight, "proof height must be non-zero") + return errorsmod.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit an empty proof") } if len(msg.Acknowledgement) == 0 { - return sdkerrors.Wrap(ErrInvalidAcknowledgement, "ack bytes cannot be empty") + return errorsmod.Wrap(ErrInvalidAcknowledgement, "ack bytes cannot be empty") } _, err := sdk.AccAddressFromBech32(msg.Signer) if err != nil { - return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) + return errorsmod.Wrapf(ibcerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) } return msg.Packet.ValidateBasic() } @@ -501,16 +485,16 @@ var _ sdk.Msg = &MsgChannelUpgradeInit{} func NewMsgChannelUpgradeInit( portID, channelID string, proposedUpgradeChannel Channel, - TimeoutHeight clienttypes.Height, - TimeoutTimestamp uint64, + timeoutHeight clienttypes.Height, + timeoutTimestamp uint64, signer string, ) *MsgChannelUpgradeInit { return &MsgChannelUpgradeInit{ PortId: portID, ChannelId: channelID, ProposedUpgradeChannel: proposedUpgradeChannel, - TimeoutHeight: TimeoutHeight, - TimeoutTimestamp: TimeoutTimestamp, + TimeoutHeight: timeoutHeight, + TimeoutTimestamp: timeoutTimestamp, Signer: signer, } } @@ -518,20 +502,20 @@ func NewMsgChannelUpgradeInit( // ValidateBasic implements sdk.Msg func (msg MsgChannelUpgradeInit) ValidateBasic() error { if err := host.PortIdentifierValidator(msg.PortId); err != nil { - return sdkerrors.Wrap(err, "invalid port ID") + return errorsmod.Wrap(err, "invalid port ID") } if !IsValidChannelID(msg.ChannelId) { return ErrInvalidChannelIdentifier } if msg.ProposedUpgradeChannel.State != INITUPGRADE { - return sdkerrors.Wrapf(ErrInvalidChannelState, "expected: %s, got: %s", INITUPGRADE, msg.ProposedUpgradeChannel.State) + return errorsmod.Wrapf(ErrInvalidChannelState, "expected: %s, got: %s", INITUPGRADE, msg.ProposedUpgradeChannel.State) } if msg.TimeoutHeight.IsZero() && msg.TimeoutTimestamp == 0 { - return sdkerrors.Wrap(ErrInvalidUpgradeTimeout, "timeout height and timeout timestamp cannot both be 0") + return errorsmod.Wrap(ErrInvalidUpgradeTimeout, "timeout height and timeout timestamp cannot both be 0") } _, err := sdk.AccAddressFromBech32(msg.Signer) if err != nil { - return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) + return errorsmod.Wrapf(ibcerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) } return nil @@ -583,41 +567,41 @@ func NewMsgChannelUpgradeTry( // ValidateBasic implements sdk.Msg func (msg MsgChannelUpgradeTry) ValidateBasic() error { if err := host.PortIdentifierValidator(msg.PortId); err != nil { - return sdkerrors.Wrap(err, "invalid port ID") + return errorsmod.Wrap(err, "invalid port ID") } if !IsValidChannelID(msg.ChannelId) { return ErrInvalidChannelIdentifier } if msg.CounterpartyChannel.State != INITUPGRADE { - return sdkerrors.Wrapf(ErrInvalidChannelState, "expected: %s, got: %s", INITUPGRADE, msg.CounterpartyChannel.State) + return errorsmod.Wrapf(ErrInvalidChannelState, "expected: %s, got: %s", INITUPGRADE, msg.CounterpartyChannel.State) } if msg.CounterpartySequence == 0 { - return sdkerrors.Wrap(sdkerrors.ErrInvalidSequence, "counterparty sequence cannot be 0") + return errorsmod.Wrap(ibcerrors.ErrInvalidSequence, "counterparty sequence cannot be 0") } if msg.ProposedUpgradeChannel.State != TRYUPGRADE { - return sdkerrors.Wrapf(ErrInvalidChannelState, "expected: %s, got: %s", TRYUPGRADE, msg.CounterpartyChannel.State) + return errorsmod.Wrapf(ErrInvalidChannelState, "expected: %s, got: %s", TRYUPGRADE, msg.CounterpartyChannel.State) } if msg.TimeoutHeight.IsZero() { - return sdkerrors.Wrap(sdkerrors.ErrInvalidHeight, "timeout height must be non-zero") + return errorsmod.Wrap(ibcerrors.ErrInvalidHeight, "timeout height must be non-zero") } if msg.TimeoutTimestamp == 0 && msg.TimeoutHeight.IsZero() { - return sdkerrors.Wrap(ErrInvalidUpgradeTimeout, "invalid upgrade timeout timestamp or timeout height") + return errorsmod.Wrap(ErrInvalidUpgradeTimeout, "invalid upgrade timeout timestamp or timeout height") } if len(msg.ProofChannel) == 0 { - return sdkerrors.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit an empty channel proof") + return errorsmod.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit an empty channel proof") } if len(msg.ProofUpgradeTimeout) == 0 { - return sdkerrors.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit an empty upgrade timeout proof") + return errorsmod.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit an empty upgrade timeout proof") } if len(msg.ProofUpgradeSequence) == 0 { - return sdkerrors.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit an empty upgrade sequence proof") + return errorsmod.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit an empty upgrade sequence proof") } if msg.ProofHeight.IsZero() { - return sdkerrors.Wrap(sdkerrors.ErrInvalidHeight, "proof height must be non-zero") + return errorsmod.Wrap(ibcerrors.ErrInvalidHeight, "proof height must be non-zero") } _, err := sdk.AccAddressFromBech32(msg.Signer) if err != nil { - return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) + return errorsmod.Wrapf(ibcerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) } return nil @@ -652,25 +636,25 @@ func NewMsgChannelUpgradeAck(portID, channelID string, counterpartyChannel Chann // ValidateBasic implements sdk.Msg func (msg MsgChannelUpgradeAck) ValidateBasic() error { if err := host.PortIdentifierValidator(msg.PortId); err != nil { - return sdkerrors.Wrap(err, "invalid port ID") + return errorsmod.Wrap(err, "invalid port ID") } if !IsValidChannelID(msg.ChannelId) { return ErrInvalidChannelIdentifier } if len(msg.ProofChannel) == 0 { - return sdkerrors.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit an empty channel proof") + return errorsmod.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit an empty channel proof") } if len(msg.ProofUpgradeSequence) == 0 { - return sdkerrors.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit an empty upgrade sequence proof") + return errorsmod.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit an empty upgrade sequence proof") } if msg.ProofHeight.IsZero() { - return sdkerrors.Wrap(sdkerrors.ErrInvalidHeight, "proof height must be non-zero") + return errorsmod.Wrap(ibcerrors.ErrInvalidHeight, "proof height must be non-zero") } if msg.CounterpartyChannel.State != TRYUPGRADE { - return sdkerrors.Wrapf(ErrInvalidChannelState, "expected: %s, got: %s", "TRYUPGRADE", msg.CounterpartyChannel.State) + return errorsmod.Wrapf(ErrInvalidChannelState, "expected: %s, got: %s", "TRYUPGRADE", msg.CounterpartyChannel.State) } if _, err := sdk.AccAddressFromBech32(msg.Signer); err != nil { - return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) + return errorsmod.Wrapf(ibcerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) } return nil @@ -715,7 +699,7 @@ func NewMsgChannelUpgradeConfirm( // ValidateBasic implements sdk.Msg func (msg MsgChannelUpgradeConfirm) ValidateBasic() error { if err := host.PortIdentifierValidator(msg.PortId); err != nil { - return sdkerrors.Wrap(err, "invalid port ID") + return errorsmod.Wrap(err, "invalid port ID") } if !IsValidChannelID(msg.ChannelId) { @@ -723,28 +707,28 @@ func (msg MsgChannelUpgradeConfirm) ValidateBasic() error { } if len(msg.ProofChannel) == 0 { - return sdkerrors.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit an empty channel proof") + return errorsmod.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit an empty channel proof") } if len(msg.ProofUpgradeError) == 0 { - return sdkerrors.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit an empty upgrade error proof") + return errorsmod.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit an empty upgrade error proof") } if len(msg.ProofUpgradeSequence) == 0 { - return sdkerrors.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit an empty upgrade sequence proof") + return errorsmod.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit an empty upgrade sequence proof") } if msg.ProofHeight.IsZero() { - return sdkerrors.Wrap(sdkerrors.ErrInvalidHeight, "proof height must be non-zero") + return errorsmod.Wrap(ibcerrors.ErrInvalidHeight, "proof height must be non-zero") } if msg.CounterpartyChannel.State != OPEN { - return sdkerrors.Wrapf(ErrInvalidChannelState, "expected: %s, got: %s", OPEN, msg.CounterpartyChannel.State) + return errorsmod.Wrapf(ErrInvalidChannelState, "expected: %s, got: %s", OPEN, msg.CounterpartyChannel.State) } _, err := sdk.AccAddressFromBech32(msg.Signer) if err != nil { - return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) + return errorsmod.Wrapf(ibcerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) } return nil @@ -786,7 +770,7 @@ func NewMsgChannelUpgradeTimeout( // ValidateBasic implements sdk.Msg func (msg MsgChannelUpgradeTimeout) ValidateBasic() error { if err := host.PortIdentifierValidator(msg.PortId); err != nil { - return sdkerrors.Wrap(err, "invalid port ID") + return errorsmod.Wrap(err, "invalid port ID") } if !IsValidChannelID(msg.ChannelId) { @@ -794,20 +778,20 @@ func (msg MsgChannelUpgradeTimeout) ValidateBasic() error { } if len(msg.ProofChannel) == 0 { - return sdkerrors.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit an empty proof") + return errorsmod.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit an empty proof") } if msg.ProofHeight.IsZero() { - return sdkerrors.Wrap(sdkerrors.ErrInvalidHeight, "proof height must be non-zero") + return errorsmod.Wrap(ibcerrors.ErrInvalidHeight, "proof height must be non-zero") } if msg.CounterpartyChannel.State != OPEN { - return sdkerrors.Wrapf(ErrInvalidChannelState, "expected: %s, got: %s", OPEN, msg.CounterpartyChannel.State) + return errorsmod.Wrapf(ErrInvalidChannelState, "expected: %s, got: %s", OPEN, msg.CounterpartyChannel.State) } _, err := sdk.AccAddressFromBech32(msg.Signer) if err != nil { - return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) + return errorsmod.Wrapf(ibcerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) } return nil @@ -847,7 +831,7 @@ func NewMsgChannelUpgradeCancel( // ValidateBasic implements sdk.Msg func (msg MsgChannelUpgradeCancel) ValidateBasic() error { if err := host.PortIdentifierValidator(msg.PortId); err != nil { - return sdkerrors.Wrap(err, "invalid port ID") + return errorsmod.Wrap(err, "invalid port ID") } if !IsValidChannelID(msg.ChannelId) { @@ -855,20 +839,20 @@ func (msg MsgChannelUpgradeCancel) ValidateBasic() error { } if len(msg.ProofErrorReceipt) == 0 { - return sdkerrors.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit an empty proof") + return errorsmod.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit an empty proof") } if msg.ProofHeight.IsZero() { - return sdkerrors.Wrap(sdkerrors.ErrInvalidHeight, "proof height must be non-zero") + return errorsmod.Wrap(ibcerrors.ErrInvalidHeight, "proof height must be non-zero") } if msg.ErrorReceipt.Sequence == 0 { - return sdkerrors.Wrap(sdkerrors.ErrInvalidSequence, "upgrade sequence cannot be 0") + return errorsmod.Wrap(ibcerrors.ErrInvalidSequence, "upgrade sequence cannot be 0") } _, err := sdk.AccAddressFromBech32(msg.Signer) if err != nil { - return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) + return errorsmod.Wrapf(ibcerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) } return nil diff --git a/modules/core/04-channel/types/msgs_test.go b/modules/core/04-channel/types/msgs_test.go index 87a36a0fa7a..54893665aa2 100644 --- a/modules/core/04-channel/types/msgs_test.go +++ b/modules/core/04-channel/types/msgs_test.go @@ -10,13 +10,14 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/suite" abci "github.com/tendermint/tendermint/abci/types" + log "github.com/tendermint/tendermint/libs/log" dbm "github.com/tendermint/tm-db" - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" - commitmenttypes "github.com/cosmos/ibc-go/v4/modules/core/23-commitment/types" - ibctesting "github.com/cosmos/ibc-go/v4/testing" - "github.com/cosmos/ibc-go/v4/testing/simapp" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + commitmenttypes "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/types" + ibctesting "github.com/cosmos/ibc-go/v7/testing" + "github.com/cosmos/ibc-go/v7/testing/simapp" ) const ( @@ -75,11 +76,13 @@ type TypesTestSuite struct { func (suite *TypesTestSuite) SetupTest() { app := simapp.Setup(false) db := dbm.NewMemDB() - store := rootmulti.NewStore(db) + dblog := log.TestingLogger() + store := rootmulti.NewStore(db, dblog) storeKey := storetypes.NewKVStoreKey("iavlStoreKey") store.MountStoreWithDB(storeKey, storetypes.StoreTypeIAVL, nil) - store.LoadVersion(0) + err := store.LoadVersion(0) + suite.Require().NoError(err) iavlStore := store.GetCommitStore(storeKey).(*iavl.Store) iavlStore.Set([]byte("KEY"), []byte("VALUE")) @@ -149,25 +152,22 @@ 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}, + {"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 { @@ -200,7 +200,6 @@ func (suite *TypesTestSuite) TestMsgChannelOpenAckValidateBasic() { {"channel id contains non-alpha", types.NewMsgChannelOpenAck(portid, invalidChannel, chanid, version, suite.proof, height, addr), false}, {"", types.NewMsgChannelOpenAck(portid, chanid, chanid, "", suite.proof, height, addr), true}, {"empty proof", types.NewMsgChannelOpenAck(portid, chanid, chanid, version, emptyProof, height, addr), false}, - {"proof height is zero", types.NewMsgChannelOpenAck(portid, chanid, chanid, version, suite.proof, clienttypes.ZeroHeight(), addr), false}, {"invalid counterparty channel id", types.NewMsgChannelOpenAck(portid, chanid, invalidShortChannel, version, suite.proof, height, addr), false}, } @@ -233,7 +232,6 @@ func (suite *TypesTestSuite) TestMsgChannelOpenConfirmValidateBasic() { {"too long channel id", types.NewMsgChannelOpenConfirm(portid, invalidLongChannel, suite.proof, height, addr), false}, {"channel id contains non-alpha", types.NewMsgChannelOpenConfirm(portid, invalidChannel, suite.proof, height, addr), false}, {"empty proof", types.NewMsgChannelOpenConfirm(portid, chanid, emptyProof, height, addr), false}, - {"proof height is zero", types.NewMsgChannelOpenConfirm(portid, chanid, suite.proof, clienttypes.ZeroHeight(), addr), false}, } for _, tc := range testCases { @@ -295,7 +293,6 @@ func (suite *TypesTestSuite) TestMsgChannelCloseConfirmValidateBasic() { {"too long channel id", types.NewMsgChannelCloseConfirm(portid, invalidLongChannel, suite.proof, height, addr), false}, {"channel id contains non-alpha", types.NewMsgChannelCloseConfirm(portid, invalidChannel, suite.proof, height, addr), false}, {"empty proof", types.NewMsgChannelCloseConfirm(portid, chanid, emptyProof, height, addr), false}, - {"proof height is zero", types.NewMsgChannelCloseConfirm(portid, chanid, suite.proof, clienttypes.ZeroHeight(), addr), false}, } for _, tc := range testCases { @@ -320,7 +317,6 @@ func (suite *TypesTestSuite) TestMsgRecvPacketValidateBasic() { expPass bool }{ {"success", types.NewMsgRecvPacket(packet, suite.proof, height, addr), true}, - {"proof height is zero", types.NewMsgRecvPacket(packet, suite.proof, clienttypes.ZeroHeight(), addr), false}, {"proof contain empty proof", types.NewMsgRecvPacket(packet, emptyProof, height, addr), false}, {"missing signer address", types.NewMsgRecvPacket(packet, suite.proof, height, emptyAddr), false}, {"invalid packet", types.NewMsgRecvPacket(invalidPacket, suite.proof, height, addr), false}, @@ -356,7 +352,6 @@ func (suite *TypesTestSuite) TestMsgTimeoutValidateBasic() { expPass bool }{ {"success", types.NewMsgTimeout(packet, 1, suite.proof, height, addr), true}, - {"proof height must be > 0", types.NewMsgTimeout(packet, 1, suite.proof, clienttypes.ZeroHeight(), addr), false}, {"seq 0", types.NewMsgTimeout(packet, 0, suite.proof, height, addr), false}, {"missing signer address", types.NewMsgTimeout(packet, 1, suite.proof, height, emptyAddr), false}, {"cannot submit an empty proof", types.NewMsgTimeout(packet, 1, emptyProof, height, addr), false}, @@ -388,7 +383,6 @@ func (suite *TypesTestSuite) TestMsgTimeoutOnCloseValidateBasic() { {"seq 0", types.NewMsgTimeoutOnClose(packet, 0, suite.proof, suite.proof, height, addr), false}, {"empty proof", types.NewMsgTimeoutOnClose(packet, 1, emptyProof, suite.proof, height, addr), false}, {"empty proof close", types.NewMsgTimeoutOnClose(packet, 1, suite.proof, emptyProof, height, addr), false}, - {"proof height is zero", types.NewMsgTimeoutOnClose(packet, 1, suite.proof, suite.proof, clienttypes.ZeroHeight(), addr), false}, {"signer address is empty", types.NewMsgTimeoutOnClose(packet, 1, suite.proof, suite.proof, height, emptyAddr), false}, {"invalid packet", types.NewMsgTimeoutOnClose(invalidPacket, 1, suite.proof, suite.proof, height, addr), false}, } @@ -415,7 +409,6 @@ func (suite *TypesTestSuite) TestMsgAcknowledgementValidateBasic() { expPass bool }{ {"success", types.NewMsgAcknowledgement(packet, packet.GetData(), suite.proof, height, addr), true}, - {"proof height must be > 0", types.NewMsgAcknowledgement(packet, packet.GetData(), suite.proof, clienttypes.ZeroHeight(), addr), false}, {"empty ack", types.NewMsgAcknowledgement(packet, nil, suite.proof, height, addr), false}, {"missing signer address", types.NewMsgAcknowledgement(packet, packet.GetData(), suite.proof, height, emptyAddr), false}, {"cannot submit an empty proof", types.NewMsgAcknowledgement(packet, packet.GetData(), emptyProof, height, addr), false}, diff --git a/modules/core/04-channel/types/packet.go b/modules/core/04-channel/types/packet.go index caba2fc2591..c007f3af9b0 100644 --- a/modules/core/04-channel/types/packet.go +++ b/modules/core/04-channel/types/packet.go @@ -3,13 +3,13 @@ package types import ( "crypto/sha256" + errorsmod "cosmossdk.io/errors" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" - "github.com/cosmos/ibc-go/v4/modules/core/exported" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + "github.com/cosmos/ibc-go/v7/modules/core/exported" ) // CommitPacket returns the packet commitment bytes. The commitment consists of: @@ -89,25 +89,25 @@ func (p Packet) GetTimeoutTimestamp() uint64 { return p.TimeoutTimestamp } // ValidateBasic implements PacketI interface func (p Packet) ValidateBasic() error { if err := host.PortIdentifierValidator(p.SourcePort); err != nil { - return sdkerrors.Wrap(err, "invalid source port ID") + return errorsmod.Wrap(err, "invalid source port ID") } if err := host.PortIdentifierValidator(p.DestinationPort); err != nil { - return sdkerrors.Wrap(err, "invalid destination port ID") + return errorsmod.Wrap(err, "invalid destination port ID") } if err := host.ChannelIdentifierValidator(p.SourceChannel); err != nil { - return sdkerrors.Wrap(err, "invalid source channel ID") + return errorsmod.Wrap(err, "invalid source channel ID") } if err := host.ChannelIdentifierValidator(p.DestinationChannel); err != nil { - return sdkerrors.Wrap(err, "invalid destination channel ID") + return errorsmod.Wrap(err, "invalid destination channel ID") } if p.Sequence == 0 { - return sdkerrors.Wrap(ErrInvalidPacket, "packet sequence cannot be 0") + return errorsmod.Wrap(ErrInvalidPacket, "packet sequence cannot be 0") } if p.TimeoutHeight.IsZero() && p.TimeoutTimestamp == 0 { - return sdkerrors.Wrap(ErrInvalidPacket, "packet timeout height and packet timeout timestamp cannot both be 0") + return errorsmod.Wrap(ErrInvalidPacket, "packet timeout height and packet timeout timestamp cannot both be 0") } if len(p.Data) == 0 { - return sdkerrors.Wrap(ErrInvalidPacket, "packet data bytes cannot be empty") + return errorsmod.Wrap(ErrInvalidPacket, "packet data bytes cannot be empty") } return nil } @@ -115,21 +115,21 @@ func (p Packet) ValidateBasic() error { // Validates a PacketId func (p PacketId) Validate() error { if err := host.PortIdentifierValidator(p.PortId); err != nil { - return sdkerrors.Wrap(err, "invalid source port ID") + return errorsmod.Wrap(err, "invalid source port ID") } if err := host.ChannelIdentifierValidator(p.ChannelId); err != nil { - return sdkerrors.Wrap(err, "invalid source channel ID") + return errorsmod.Wrap(err, "invalid source channel ID") } if p.Sequence == 0 { - return sdkerrors.Wrap(ErrInvalidPacket, "packet sequence cannot be 0") + return errorsmod.Wrap(ErrInvalidPacket, "packet sequence cannot be 0") } return nil } -// NewPacketId returns a new instance of PacketId -func NewPacketId(portId, channelId string, seq uint64) PacketId { - return PacketId{PortId: portId, ChannelId: channelId, Sequence: seq} +// NewPacketID returns a new instance of PacketId +func NewPacketID(portID, channelID string, seq uint64) PacketId { + return PacketId{PortId: portID, ChannelId: channelID, Sequence: seq} } diff --git a/modules/core/04-channel/types/packet_test.go b/modules/core/04-channel/types/packet_test.go index f1ed49b15a2..780844dbb54 100644 --- a/modules/core/04-channel/types/packet_test.go +++ b/modules/core/04-channel/types/packet_test.go @@ -7,8 +7,8 @@ import ( codectypes "github.com/cosmos/cosmos-sdk/codec/types" "github.com/stretchr/testify/require" - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" ) func TestCommitPacket(t *testing.T) { diff --git a/modules/core/04-channel/types/query.go b/modules/core/04-channel/types/query.go index 0a4662acc01..84c6fac61f2 100644 --- a/modules/core/04-channel/types/query.go +++ b/modules/core/04-channel/types/query.go @@ -3,8 +3,8 @@ package types import ( codectypes "github.com/cosmos/cosmos-sdk/codec/types" - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v4/modules/core/exported" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v7/modules/core/exported" ) var ( diff --git a/modules/core/04-channel/types/query.pb.go b/modules/core/04-channel/types/query.pb.go index 75970287318..ec6746d002c 100644 --- a/modules/core/04-channel/types/query.pb.go +++ b/modules/core/04-channel/types/query.pb.go @@ -8,10 +8,10 @@ import ( fmt "fmt" types1 "github.com/cosmos/cosmos-sdk/codec/types" query "github.com/cosmos/cosmos-sdk/types/query" - types "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - _ "github.com/gogo/protobuf/gogoproto" - grpc1 "github.com/gogo/protobuf/grpc" - proto "github.com/gogo/protobuf/proto" + _ "github.com/cosmos/gogoproto/gogoproto" + grpc1 "github.com/cosmos/gogoproto/grpc" + proto "github.com/cosmos/gogoproto/proto" + types "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" _ "google.golang.org/genproto/googleapis/api/annotations" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" @@ -1708,101 +1708,101 @@ func init() { func init() { proto.RegisterFile("ibc/core/channel/v1/query.proto", fileDescriptor_1034a1e9abc4cca1) } var fileDescriptor_1034a1e9abc4cca1 = []byte{ - // 1490 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xdc, 0x59, 0xcf, 0x6f, 0x13, 0xc7, - 0x17, 0xcf, 0x38, 0x06, 0x92, 0x07, 0x5f, 0x7e, 0x4c, 0x12, 0x08, 0x4b, 0x70, 0x82, 0xbf, 0x6a, - 0x09, 0x48, 0xec, 0x90, 0x40, 0x29, 0xad, 0x5a, 0x24, 0x12, 0xa9, 0x90, 0xaa, 0xfc, 0xda, 0x14, - 0x15, 0x90, 0x5a, 0x77, 0xbd, 0x1e, 0x9c, 0x55, 0xe2, 0xdd, 0xc5, 0xbb, 0x36, 0xa0, 0xd4, 0x55, - 0xd5, 0x03, 0x45, 0xea, 0xa5, 0x2a, 0x87, 0x4a, 0xbd, 0x54, 0xea, 0x8d, 0x43, 0x0f, 0xfd, 0x0b, - 0x7a, 0xe5, 0x56, 0x24, 0x7a, 0xa8, 0x84, 0x44, 0x2b, 0x82, 0x44, 0xaf, 0xbd, 0xf4, 0x5c, 0xed, - 0xfc, 0x58, 0xef, 0xda, 0xbb, 0x1b, 0x3b, 0x8e, 0x25, 0xd4, 0xdb, 0xee, 0xec, 0xbc, 0x37, 0x9f, - 0xcf, 0xe7, 0xcd, 0x7b, 0x79, 0xcf, 0x81, 0x49, 0xb3, 0x68, 0x10, 0xc3, 0xae, 0x52, 0x62, 0x2c, - 0xe9, 0x96, 0x45, 0x57, 0x48, 0x7d, 0x86, 0xdc, 0xaa, 0xd1, 0xea, 0x5d, 0xd5, 0xa9, 0xda, 0x9e, - 0x8d, 0x47, 0xcc, 0xa2, 0xa1, 0xfa, 0x1b, 0x54, 0xb1, 0x41, 0xad, 0xcf, 0x28, 0x21, 0xab, 0x15, - 0x93, 0x5a, 0x9e, 0x6f, 0xc4, 0x9f, 0xb8, 0x95, 0x72, 0xd4, 0xb0, 0xdd, 0x8a, 0xed, 0x92, 0xa2, - 0xee, 0x52, 0xee, 0x8e, 0xd4, 0x67, 0x8a, 0xd4, 0xd3, 0x67, 0x88, 0xa3, 0x97, 0x4d, 0x4b, 0xf7, - 0x4c, 0xdb, 0x12, 0x7b, 0x0f, 0xc5, 0x41, 0x90, 0x87, 0xf1, 0x2d, 0x13, 0x65, 0xdb, 0x2e, 0xaf, - 0x50, 0xa2, 0x3b, 0x26, 0xd1, 0x2d, 0xcb, 0xf6, 0x98, 0xbd, 0x2b, 0xbe, 0xee, 0x17, 0x5f, 0xd9, - 0x5b, 0xb1, 0x76, 0x93, 0xe8, 0x96, 0x40, 0xaf, 0x8c, 0x96, 0xed, 0xb2, 0xcd, 0x1e, 0x89, 0xff, - 0xc4, 0x57, 0xf3, 0x17, 0x60, 0xe4, 0x8a, 0x8f, 0x69, 0x9e, 0x1f, 0xa2, 0xd1, 0x5b, 0x35, 0xea, - 0x7a, 0x78, 0x1f, 0x6c, 0x73, 0xec, 0xaa, 0x57, 0x30, 0x4b, 0xe3, 0x68, 0x0a, 0x4d, 0x0f, 0x6b, - 0x5b, 0xfd, 0xd7, 0x85, 0x12, 0x3e, 0x08, 0x20, 0xf0, 0xf8, 0xdf, 0x32, 0xec, 0xdb, 0xb0, 0x58, - 0x59, 0x28, 0xe5, 0x1f, 0x22, 0x18, 0x8d, 0xfa, 0x73, 0x1d, 0xdb, 0x72, 0x29, 0x3e, 0x05, 0xdb, - 0xc4, 0x2e, 0xe6, 0x70, 0xfb, 0xec, 0x84, 0x1a, 0xa3, 0xa6, 0x2a, 0xcd, 0xe4, 0x66, 0x3c, 0x0a, - 0x5b, 0x9c, 0xaa, 0x6d, 0xdf, 0x64, 0x47, 0xed, 0xd0, 0xf8, 0x0b, 0x9e, 0x87, 0x1d, 0xec, 0xa1, - 0xb0, 0x44, 0xcd, 0xf2, 0x92, 0x37, 0x3e, 0xc8, 0x5c, 0x2a, 0x21, 0x97, 0x3c, 0x02, 0xf5, 0x19, - 0xf5, 0x3c, 0xdb, 0x31, 0x97, 0x7d, 0xf4, 0x6c, 0x72, 0x40, 0xdb, 0xce, 0xac, 0xf8, 0x52, 0xfe, - 0x93, 0x28, 0x54, 0x57, 0x72, 0x7f, 0x0f, 0xa0, 0x19, 0x18, 0x81, 0xf6, 0x75, 0x95, 0x47, 0x51, - 0xf5, 0xa3, 0xa8, 0xf2, 0x4b, 0x21, 0xa2, 0xa8, 0x5e, 0xd6, 0xcb, 0x54, 0xd8, 0x6a, 0x21, 0xcb, - 0xfc, 0x33, 0x04, 0x63, 0x2d, 0x07, 0x08, 0x31, 0xe6, 0x60, 0x48, 0xf0, 0x73, 0xc7, 0xd1, 0xd4, - 0x20, 0xf3, 0x1f, 0xa7, 0xc6, 0x42, 0x89, 0x5a, 0x9e, 0x79, 0xd3, 0xa4, 0x25, 0xa9, 0x4b, 0x60, - 0x87, 0xcf, 0x45, 0x50, 0x66, 0x18, 0xca, 0xc3, 0xeb, 0xa2, 0xe4, 0x00, 0xc2, 0x30, 0xf1, 0x69, - 0xd8, 0xda, 0xa5, 0x8a, 0x62, 0x7f, 0xfe, 0x3e, 0x82, 0x1c, 0x27, 0x68, 0x5b, 0x16, 0x35, 0x7c, - 0x6f, 0xad, 0x5a, 0xe6, 0x00, 0x8c, 0xe0, 0xa3, 0xb8, 0x4a, 0xa1, 0x95, 0x16, 0xad, 0x33, 0x1b, - 0xd6, 0xfa, 0x2f, 0x04, 0x93, 0x89, 0x50, 0xfe, 0x5b, 0xaa, 0x5f, 0x93, 0xa2, 0x73, 0x4c, 0xf3, - 0x6c, 0xf7, 0xa2, 0xa7, 0x7b, 0xb4, 0xd7, 0xe4, 0xfd, 0x23, 0x10, 0x31, 0xc6, 0xb5, 0x10, 0x51, - 0x87, 0x7d, 0x66, 0xa0, 0x4f, 0x81, 0x43, 0x2d, 0xb8, 0xfe, 0x16, 0x91, 0x29, 0x47, 0xe2, 0x88, - 0x84, 0x24, 0x0d, 0xf9, 0x1c, 0x33, 0xe3, 0x96, 0xfb, 0x99, 0xf2, 0x3f, 0x21, 0x38, 0x14, 0x61, - 0xe8, 0x73, 0xb2, 0xdc, 0x9a, 0xbb, 0x19, 0xfa, 0xe1, 0xc3, 0xb0, 0xab, 0x4a, 0xeb, 0xa6, 0x6b, - 0xda, 0x56, 0xc1, 0xaa, 0x55, 0x8a, 0xb4, 0xca, 0x50, 0x66, 0xb5, 0x9d, 0x72, 0xf9, 0x22, 0x5b, - 0x8d, 0x6c, 0x14, 0x74, 0xb2, 0xd1, 0x8d, 0x02, 0xef, 0x53, 0x04, 0xf9, 0x34, 0xbc, 0x22, 0x28, - 0xef, 0xc2, 0x2e, 0x43, 0x7e, 0x89, 0x04, 0x63, 0x54, 0xe5, 0x7f, 0x0f, 0x54, 0xf9, 0xf7, 0x40, - 0x3d, 0x6b, 0xdd, 0xd5, 0x76, 0x1a, 0x11, 0x37, 0xf8, 0x00, 0x0c, 0x8b, 0x40, 0x06, 0xac, 0x86, - 0xf8, 0xc2, 0x42, 0xa9, 0x19, 0x8d, 0xc1, 0xb4, 0x68, 0x64, 0x37, 0x12, 0x8d, 0x2a, 0x4c, 0x30, - 0x72, 0x97, 0x75, 0x63, 0x99, 0x7a, 0xf3, 0x76, 0xa5, 0x62, 0x7a, 0x15, 0x6a, 0x79, 0xbd, 0xc6, - 0x41, 0x81, 0x21, 0xd7, 0x77, 0x61, 0x19, 0x54, 0x04, 0x20, 0x78, 0xcf, 0x7f, 0x8f, 0xe0, 0x60, - 0xc2, 0xa1, 0x42, 0x4c, 0x56, 0xb2, 0xe4, 0x2a, 0x3b, 0x78, 0x87, 0x16, 0x5a, 0xe9, 0xe7, 0xf5, - 0xfc, 0x21, 0x09, 0x9c, 0xdb, 0xab, 0x24, 0xd1, 0x3a, 0x3b, 0xb8, 0xe1, 0x3a, 0xfb, 0x52, 0x96, - 0xfc, 0x18, 0x84, 0x41, 0x99, 0xdd, 0xde, 0x54, 0x4b, 0x56, 0xda, 0xa9, 0xd8, 0x4a, 0xcb, 0x9d, - 0xf0, 0xbb, 0x1c, 0x36, 0x7a, 0x15, 0xca, 0xac, 0x0d, 0xfb, 0x43, 0x44, 0x35, 0x6a, 0x50, 0xd3, - 0xe9, 0xeb, 0xcd, 0x7c, 0x80, 0x40, 0x89, 0x3b, 0x51, 0xc8, 0xaa, 0xc0, 0x50, 0xd5, 0x5f, 0xaa, - 0x53, 0xee, 0x77, 0x48, 0x0b, 0xde, 0xfb, 0x99, 0xa3, 0xb7, 0x45, 0xc1, 0xe4, 0xa0, 0xce, 0x1a, - 0xcb, 0x96, 0x7d, 0x7b, 0x85, 0x96, 0xca, 0xb4, 0xdf, 0x89, 0xfa, 0x50, 0x96, 0xbe, 0x84, 0x93, - 0x85, 0x2c, 0xd3, 0xb0, 0x4b, 0x8f, 0x7e, 0x12, 0x29, 0xdb, 0xba, 0xdc, 0xcf, 0xbc, 0x7d, 0x91, - 0x8a, 0xf5, 0x55, 0x49, 0x5e, 0x7c, 0x06, 0x0e, 0x38, 0x0c, 0x60, 0xa1, 0x99, 0x6b, 0x05, 0x29, - 0xb8, 0x3b, 0x9e, 0x9d, 0x1a, 0x9c, 0xce, 0x6a, 0xfb, 0x9d, 0x96, 0xcc, 0x5e, 0x94, 0x1b, 0xf2, - 0xff, 0x20, 0xf8, 0x7f, 0x2a, 0x4d, 0x11, 0x93, 0x0f, 0x60, 0x77, 0x8b, 0xf8, 0x9d, 0x97, 0x81, - 0x36, 0xcb, 0x57, 0xa1, 0x16, 0x7c, 0x27, 0xeb, 0xf2, 0x55, 0x4b, 0xe6, 0x1c, 0xc7, 0xdc, 0x73, - 0x68, 0xd7, 0x09, 0xc9, 0xe0, 0x7a, 0x21, 0xb9, 0x23, 0xca, 0x71, 0x0c, 0x30, 0x11, 0x8c, 0x09, - 0x18, 0x6e, 0xfa, 0x43, 0xcc, 0x5f, 0x73, 0x21, 0xa4, 0x49, 0xa6, 0x4b, 0x4d, 0xee, 0xc9, 0x72, - 0xd5, 0x3c, 0xfa, 0xac, 0xb1, 0xdc, 0xb3, 0x20, 0xc7, 0x61, 0x54, 0x08, 0xa2, 0x1b, 0xcb, 0x6d, - 0x4a, 0x60, 0x47, 0xde, 0xbc, 0xa6, 0x04, 0x35, 0x38, 0x10, 0x8b, 0xa3, 0xcf, 0xfc, 0xaf, 0x8b, - 0x5e, 0xf9, 0x22, 0xbd, 0x13, 0xc4, 0x43, 0xe3, 0x00, 0x7a, 0xed, 0xc3, 0x7f, 0x46, 0x30, 0x95, - 0xec, 0x5b, 0xf0, 0x9a, 0x85, 0x31, 0x8b, 0xde, 0x69, 0x5e, 0x96, 0x82, 0x60, 0xcf, 0x8e, 0xca, - 0x6a, 0x23, 0x56, 0xbb, 0x6d, 0x1f, 0x4b, 0xe0, 0xec, 0xd7, 0x7b, 0x61, 0x0b, 0xc3, 0x8c, 0x7f, - 0x44, 0xb0, 0x4d, 0xb4, 0xab, 0x78, 0x3a, 0x36, 0xdf, 0x63, 0x7e, 0x70, 0x50, 0x8e, 0x74, 0xb0, - 0x93, 0x33, 0xcf, 0xcf, 0x7d, 0xf9, 0xe4, 0xc5, 0x83, 0xcc, 0x3b, 0xf8, 0x6d, 0x92, 0xf2, 0x6b, - 0x89, 0x4b, 0x56, 0x9b, 0x12, 0x37, 0x88, 0x2f, 0xbc, 0x4b, 0x56, 0x45, 0x38, 0x1a, 0xf8, 0x3e, - 0x82, 0x21, 0x39, 0x20, 0xe2, 0xf5, 0xcf, 0x96, 0xd7, 0x5a, 0x39, 0xda, 0xc9, 0x56, 0x81, 0xf3, - 0x35, 0x86, 0x73, 0x12, 0x1f, 0x4c, 0xc5, 0x89, 0x7f, 0x41, 0x80, 0xdb, 0xa7, 0x56, 0x7c, 0x22, - 0xe5, 0xa4, 0xa4, 0x71, 0x5b, 0x39, 0xd9, 0x9d, 0x91, 0x00, 0x7a, 0x86, 0x01, 0x3d, 0x8d, 0x4f, - 0xc5, 0x03, 0x0d, 0x0c, 0x7d, 0x4d, 0x83, 0x97, 0x46, 0x93, 0xc1, 0x63, 0x9f, 0x41, 0xdb, 0xc8, - 0x98, 0xca, 0x20, 0x69, 0x76, 0x4d, 0x65, 0x90, 0x38, 0x95, 0xe6, 0x2f, 0x31, 0x06, 0x0b, 0xf8, - 0xdc, 0xc6, 0xaf, 0x04, 0x09, 0xcf, 0xb2, 0xf8, 0xdb, 0x0c, 0x8c, 0xc5, 0xce, 0x5c, 0xf8, 0xd4, - 0xfa, 0x00, 0xe3, 0x86, 0x4a, 0xe5, 0xcd, 0xae, 0xed, 0x04, 0xb7, 0xaf, 0x10, 0x23, 0xf7, 0x05, - 0xc2, 0x9f, 0xf7, 0xc2, 0x2e, 0x3a, 0x1f, 0x12, 0x39, 0x68, 0x92, 0xd5, 0x96, 0x91, 0xb5, 0x41, - 0x78, 0x19, 0x08, 0x7d, 0xe0, 0x0b, 0x0d, 0xfc, 0x14, 0xc1, 0xee, 0xd6, 0xbe, 0x1f, 0xcf, 0x24, - 0xf3, 0x4a, 0x98, 0xeb, 0x94, 0xd9, 0x6e, 0x4c, 0x84, 0x0a, 0x9f, 0x32, 0x11, 0x6e, 0xe0, 0x6b, - 0x3d, 0x68, 0xd0, 0xf6, 0x97, 0xd6, 0x25, 0xab, 0xb2, 0x7c, 0x36, 0xf0, 0x13, 0x04, 0x7b, 0xda, - 0xa6, 0x1a, 0xdc, 0x05, 0xd6, 0x20, 0x0b, 0x4f, 0x74, 0x65, 0x23, 0x08, 0x5e, 0x65, 0x04, 0x2f, - 0xe1, 0x0b, 0x9b, 0x4a, 0x10, 0xff, 0x8a, 0xe0, 0x7f, 0x91, 0x81, 0x02, 0xab, 0xeb, 0xa1, 0x8b, - 0xce, 0x3a, 0x0a, 0xe9, 0x78, 0xbf, 0x60, 0xf2, 0x31, 0x63, 0xf2, 0x11, 0xbe, 0xda, 0x3b, 0x93, - 0x2a, 0x77, 0x1d, 0x89, 0xd3, 0x1a, 0x82, 0xb1, 0xd8, 0x06, 0x34, 0x2d, 0x35, 0xd3, 0xc6, 0x97, - 0xb4, 0xd4, 0x4c, 0x1d, 0x3e, 0xf2, 0xd7, 0x19, 0xd3, 0x45, 0x7c, 0xa5, 0x77, 0xa6, 0xba, 0xb1, - 0x1c, 0x61, 0xf9, 0x12, 0xc1, 0xde, 0xf8, 0x36, 0x1b, 0x77, 0x0b, 0x37, 0xb8, 0x97, 0xa7, 0xbb, - 0x37, 0x14, 0x44, 0x6f, 0x30, 0xa2, 0x1f, 0x62, 0x6d, 0x53, 0x88, 0x46, 0xe9, 0xdc, 0xcb, 0xc0, - 0x9e, 0xb6, 0xf6, 0x35, 0x2d, 0xef, 0x92, 0x9a, 0xf0, 0xb4, 0xbc, 0x4b, 0xec, 0x8f, 0x37, 0xa9, - 0xbc, 0xc6, 0x95, 0x96, 0x94, 0xc6, 0xbe, 0x41, 0x6a, 0x01, 0xa0, 0x82, 0x23, 0x28, 0xff, 0x8d, - 0x60, 0x67, 0xb4, 0x89, 0xc5, 0xa4, 0x13, 0x46, 0xa1, 0xb6, 0x5b, 0x39, 0xde, 0xb9, 0x81, 0xe0, - 0xff, 0x19, 0xa3, 0x5f, 0xc7, 0x5e, 0x7f, 0xd8, 0x47, 0xba, 0xf8, 0x08, 0x6d, 0xff, 0xc6, 0xe3, - 0xdf, 0x10, 0x8c, 0xc4, 0x74, 0xb9, 0x38, 0xa5, 0x0d, 0x48, 0x6e, 0xb8, 0x95, 0x37, 0xba, 0xb4, - 0x12, 0x12, 0x5c, 0x66, 0x12, 0xbc, 0x8f, 0xcf, 0xf7, 0x20, 0x41, 0xa4, 0x17, 0x9f, 0x5b, 0x7c, - 0xf4, 0x3c, 0x87, 0x1e, 0x3f, 0xcf, 0xa1, 0x3f, 0x9f, 0xe7, 0xd0, 0x37, 0x6b, 0xb9, 0x81, 0xc7, - 0x6b, 0xb9, 0x81, 0xdf, 0xd7, 0x72, 0x03, 0x37, 0xde, 0x2a, 0x9b, 0xde, 0x52, 0xad, 0xa8, 0x1a, - 0x76, 0x85, 0x88, 0x7f, 0x0c, 0x9a, 0x45, 0xe3, 0x58, 0xd9, 0x26, 0xf5, 0x93, 0xa4, 0x62, 0x97, - 0x6a, 0x2b, 0xd4, 0xe5, 0x10, 0x8e, 0x9f, 0x3c, 0x26, 0x51, 0x78, 0x77, 0x1d, 0xea, 0x16, 0xb7, - 0xb2, 0x1f, 0x71, 0x4f, 0xfc, 0x1b, 0x00, 0x00, 0xff, 0xff, 0x9c, 0x44, 0xc0, 0x72, 0xa8, 0x1c, - 0x00, 0x00, + // 1489 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xdc, 0x59, 0xcd, 0x6f, 0xd4, 0x46, + 0x14, 0xcf, 0x6c, 0x16, 0x48, 0x1e, 0x94, 0x8f, 0x49, 0x02, 0xc1, 0x84, 0x4d, 0xd8, 0xaa, 0x25, + 0x20, 0xe1, 0x21, 0x81, 0x02, 0xad, 0x5a, 0x24, 0x12, 0xa9, 0x90, 0xaa, 0x7c, 0x39, 0x45, 0x05, + 0xa4, 0x76, 0xeb, 0xf5, 0x0e, 0x1b, 0x2b, 0x59, 0xdb, 0xac, 0xbd, 0x0b, 0x28, 0xdd, 0xaa, 0xea, + 0x81, 0x22, 0xf5, 0x52, 0x95, 0x43, 0xa5, 0x5e, 0x2a, 0xf5, 0xc6, 0xa1, 0x87, 0xfe, 0x05, 0xbd, + 0x72, 0x2b, 0x12, 0x3d, 0x54, 0x42, 0xa2, 0x15, 0x41, 0xa2, 0xd7, 0x5e, 0x7a, 0xae, 0x3c, 0x1f, + 0x5e, 0x7b, 0xd7, 0x76, 0xb2, 0xd9, 0xac, 0x14, 0xf5, 0x66, 0x8f, 0xe7, 0xbd, 0xf9, 0xfd, 0x7e, + 0x6f, 0xde, 0xcb, 0x7b, 0x1b, 0x18, 0x37, 0x8b, 0x06, 0x31, 0xec, 0x2a, 0x25, 0xc6, 0x82, 0x6e, + 0x59, 0x74, 0x89, 0xd4, 0xa7, 0xc8, 0xed, 0x1a, 0xad, 0xde, 0x53, 0x9d, 0xaa, 0xed, 0xd9, 0x78, + 0xc8, 0x2c, 0x1a, 0xaa, 0xbf, 0x41, 0x15, 0x1b, 0xd4, 0xfa, 0x94, 0x12, 0xb2, 0x5a, 0x32, 0xa9, + 0xe5, 0xf9, 0x46, 0xfc, 0x89, 0x5b, 0x29, 0x47, 0x0d, 0xdb, 0xad, 0xd8, 0x2e, 0x29, 0xea, 0x2e, + 0xe5, 0xee, 0x48, 0x7d, 0xaa, 0x48, 0x3d, 0x7d, 0x8a, 0x38, 0x7a, 0xd9, 0xb4, 0x74, 0xcf, 0xb4, + 0x2d, 0xb1, 0xf7, 0x50, 0x1c, 0x04, 0x79, 0x18, 0xdf, 0x32, 0x56, 0xb6, 0xed, 0xf2, 0x12, 0x25, + 0xba, 0x63, 0x12, 0xdd, 0xb2, 0x6c, 0x8f, 0xd9, 0xbb, 0xe2, 0xeb, 0x7e, 0xf1, 0x95, 0xbd, 0x15, + 0x6b, 0xb7, 0x88, 0x6e, 0x09, 0xf4, 0xca, 0x70, 0xd9, 0x2e, 0xdb, 0xec, 0x91, 0xf8, 0x4f, 0x7c, + 0x35, 0x7f, 0x11, 0x86, 0xae, 0xfa, 0x98, 0x66, 0xf9, 0x21, 0x1a, 0xbd, 0x5d, 0xa3, 0xae, 0x87, + 0xf7, 0xc1, 0x36, 0xc7, 0xae, 0x7a, 0x05, 0xb3, 0x34, 0x8a, 0x26, 0xd0, 0xe4, 0xa0, 0xb6, 0xd5, + 0x7f, 0x9d, 0x2b, 0xe1, 0x83, 0x00, 0x02, 0x8f, 0xff, 0x2d, 0xc3, 0xbe, 0x0d, 0x8a, 0x95, 0xb9, + 0x52, 0xfe, 0x11, 0x82, 0xe1, 0xa8, 0x3f, 0xd7, 0xb1, 0x2d, 0x97, 0xe2, 0x53, 0xb0, 0x4d, 0xec, + 0x62, 0x0e, 0xb7, 0x4f, 0x8f, 0xa9, 0x31, 0x6a, 0xaa, 0xd2, 0x4c, 0x6e, 0xc6, 0xc3, 0xb0, 0xc5, + 0xa9, 0xda, 0xf6, 0x2d, 0x76, 0xd4, 0x0e, 0x8d, 0xbf, 0xe0, 0x59, 0xd8, 0xc1, 0x1e, 0x0a, 0x0b, + 0xd4, 0x2c, 0x2f, 0x78, 0xa3, 0xfd, 0xcc, 0xa5, 0x12, 0x72, 0xc9, 0x23, 0x50, 0x9f, 0x52, 0x2f, + 0xb0, 0x1d, 0x33, 0xd9, 0xc7, 0xcf, 0xc7, 0xfb, 0xb4, 0xed, 0xcc, 0x8a, 0x2f, 0xe5, 0x3f, 0x8d, + 0x42, 0x75, 0x25, 0xf7, 0xf7, 0x01, 0x9a, 0x81, 0x11, 0x68, 0xdf, 0x54, 0x79, 0x14, 0x55, 0x3f, + 0x8a, 0x2a, 0xbf, 0x14, 0x22, 0x8a, 0xea, 0x15, 0xbd, 0x4c, 0x85, 0xad, 0x16, 0xb2, 0xcc, 0x3f, + 0x47, 0x30, 0xd2, 0x72, 0x80, 0x10, 0x63, 0x06, 0x06, 0x04, 0x3f, 0x77, 0x14, 0x4d, 0xf4, 0x33, + 0xff, 0x71, 0x6a, 0xcc, 0x95, 0xa8, 0xe5, 0x99, 0xb7, 0x4c, 0x5a, 0x92, 0xba, 0x04, 0x76, 0xf8, + 0x7c, 0x04, 0x65, 0x86, 0xa1, 0x3c, 0xbc, 0x2a, 0x4a, 0x0e, 0x20, 0x0c, 0x13, 0x9f, 0x81, 0xad, + 0x1d, 0xaa, 0x28, 0xf6, 0xe7, 0x1f, 0x20, 0xc8, 0x71, 0x82, 0xb6, 0x65, 0x51, 0xc3, 0xf7, 0xd6, + 0xaa, 0x65, 0x0e, 0xc0, 0x08, 0x3e, 0x8a, 0xab, 0x14, 0x5a, 0x69, 0xd1, 0x3a, 0xb3, 0x6e, 0xad, + 0xff, 0x46, 0x30, 0x9e, 0x08, 0xe5, 0xff, 0xa5, 0xfa, 0x75, 0x29, 0x3a, 0xc7, 0x34, 0xcb, 0x76, + 0xcf, 0x7b, 0xba, 0x47, 0xbb, 0x4d, 0xde, 0x3f, 0x03, 0x11, 0x63, 0x5c, 0x0b, 0x11, 0x75, 0xd8, + 0x67, 0x06, 0xfa, 0x14, 0x38, 0xd4, 0x82, 0xeb, 0x6f, 0x11, 0x99, 0x72, 0x24, 0x8e, 0x48, 0x48, + 0xd2, 0x90, 0xcf, 0x11, 0x33, 0x6e, 0xb9, 0x97, 0x29, 0xff, 0x33, 0x82, 0x43, 0x11, 0x86, 0x3e, + 0x27, 0xcb, 0xad, 0xb9, 0x1b, 0xa1, 0x1f, 0x3e, 0x0c, 0xbb, 0xaa, 0xb4, 0x6e, 0xba, 0xa6, 0x6d, + 0x15, 0xac, 0x5a, 0xa5, 0x48, 0xab, 0x0c, 0x65, 0x56, 0xdb, 0x29, 0x97, 0x2f, 0xb1, 0xd5, 0xc8, + 0x46, 0x41, 0x27, 0x1b, 0xdd, 0x28, 0xf0, 0x3e, 0x43, 0x90, 0x4f, 0xc3, 0x2b, 0x82, 0xf2, 0x1e, + 0xec, 0x32, 0xe4, 0x97, 0x48, 0x30, 0x86, 0x55, 0xfe, 0xf7, 0x40, 0x95, 0x7f, 0x0f, 0xd4, 0x73, + 0xd6, 0x3d, 0x6d, 0xa7, 0x11, 0x71, 0x83, 0x0f, 0xc0, 0xa0, 0x08, 0x64, 0xc0, 0x6a, 0x80, 0x2f, + 0xcc, 0x95, 0x9a, 0xd1, 0xe8, 0x4f, 0x8b, 0x46, 0x76, 0x3d, 0xd1, 0xa8, 0xc2, 0x18, 0x23, 0x77, + 0x45, 0x37, 0x16, 0xa9, 0x37, 0x6b, 0x57, 0x2a, 0xa6, 0x57, 0xa1, 0x96, 0xd7, 0x6d, 0x1c, 0x14, + 0x18, 0x70, 0x7d, 0x17, 0x96, 0x41, 0x45, 0x00, 0x82, 0xf7, 0xfc, 0x0f, 0x08, 0x0e, 0x26, 0x1c, + 0x2a, 0xc4, 0x64, 0x25, 0x4b, 0xae, 0xb2, 0x83, 0x77, 0x68, 0xa1, 0x95, 0x5e, 0x5e, 0xcf, 0x1f, + 0x93, 0xc0, 0xb9, 0xdd, 0x4a, 0x12, 0xad, 0xb3, 0xfd, 0xeb, 0xae, 0xb3, 0xaf, 0x64, 0xc9, 0x8f, + 0x41, 0x18, 0x94, 0xd9, 0xed, 0x4d, 0xb5, 0x64, 0xa5, 0x9d, 0x88, 0xad, 0xb4, 0xdc, 0x09, 0xbf, + 0xcb, 0x61, 0xa3, 0xcd, 0x50, 0x66, 0x6d, 0xd8, 0x1f, 0x22, 0xaa, 0x51, 0x83, 0x9a, 0x4e, 0x4f, + 0x6f, 0xe6, 0x43, 0x04, 0x4a, 0xdc, 0x89, 0x42, 0x56, 0x05, 0x06, 0xaa, 0xfe, 0x52, 0x9d, 0x72, + 0xbf, 0x03, 0x5a, 0xf0, 0xde, 0xcb, 0x1c, 0xbd, 0x23, 0x0a, 0x26, 0x07, 0x75, 0xce, 0x58, 0xb4, + 0xec, 0x3b, 0x4b, 0xb4, 0x54, 0xa6, 0xbd, 0x4e, 0xd4, 0x47, 0xb2, 0xf4, 0x25, 0x9c, 0x2c, 0x64, + 0x99, 0x84, 0x5d, 0x7a, 0xf4, 0x93, 0x48, 0xd9, 0xd6, 0xe5, 0x5e, 0xe6, 0xed, 0xcb, 0x54, 0xac, + 0x9b, 0x25, 0x79, 0xf1, 0x59, 0x38, 0xe0, 0x30, 0x80, 0x85, 0x66, 0xae, 0x15, 0xa4, 0xe0, 0xee, + 0x68, 0x76, 0xa2, 0x7f, 0x32, 0xab, 0xed, 0x77, 0x5a, 0x32, 0x7b, 0x5e, 0x6e, 0xc8, 0xff, 0x8b, + 0xe0, 0xf5, 0x54, 0x9a, 0x22, 0x26, 0x1f, 0xc2, 0xee, 0x16, 0xf1, 0xd7, 0x5e, 0x06, 0xda, 0x2c, + 0x37, 0x43, 0x2d, 0xf8, 0x5e, 0xd6, 0xe5, 0x6b, 0x96, 0xcc, 0x39, 0x8e, 0xb9, 0xeb, 0xd0, 0xae, + 0x12, 0x92, 0xfe, 0xd5, 0x42, 0x72, 0x57, 0x94, 0xe3, 0x18, 0x60, 0x22, 0x18, 0x63, 0x30, 0xd8, + 0xf4, 0x87, 0x98, 0xbf, 0xe6, 0x42, 0x48, 0x93, 0x4c, 0x87, 0x9a, 0xdc, 0x97, 0xe5, 0xaa, 0x79, + 0xf4, 0x39, 0x63, 0xb1, 0x6b, 0x41, 0x8e, 0xc3, 0xb0, 0x10, 0x44, 0x37, 0x16, 0xdb, 0x94, 0xc0, + 0x8e, 0xbc, 0x79, 0x4d, 0x09, 0x6a, 0x70, 0x20, 0x16, 0x47, 0x8f, 0xf9, 0xdf, 0x10, 0xbd, 0xf2, + 0x25, 0x7a, 0x37, 0x88, 0x87, 0xc6, 0x01, 0x74, 0xdb, 0x87, 0xff, 0x82, 0x60, 0x22, 0xd9, 0xb7, + 0xe0, 0x35, 0x0d, 0x23, 0x16, 0xbd, 0xdb, 0xbc, 0x2c, 0x05, 0xc1, 0x9e, 0x1d, 0x95, 0xd5, 0x86, + 0xac, 0x76, 0xdb, 0x1e, 0x96, 0xc0, 0xe9, 0x6f, 0xf6, 0xc2, 0x16, 0x86, 0x19, 0xff, 0x84, 0x60, + 0x9b, 0x68, 0x57, 0xf1, 0x64, 0x6c, 0xbe, 0xc7, 0xfc, 0xe0, 0xa0, 0x1c, 0x59, 0xc3, 0x4e, 0xce, + 0x3c, 0x3f, 0xf3, 0xd5, 0xd3, 0x97, 0x0f, 0x33, 0xef, 0xe2, 0x77, 0x48, 0xca, 0xaf, 0x25, 0x2e, + 0x59, 0x6e, 0x4a, 0xdc, 0x20, 0xbe, 0xf0, 0x2e, 0x59, 0x16, 0xe1, 0x68, 0xe0, 0x07, 0x08, 0x06, + 0xe4, 0x80, 0x88, 0x57, 0x3f, 0x5b, 0x5e, 0x6b, 0xe5, 0xe8, 0x5a, 0xb6, 0x0a, 0x9c, 0x6f, 0x30, + 0x9c, 0xe3, 0xf8, 0x60, 0x2a, 0x4e, 0xfc, 0x2b, 0x02, 0xdc, 0x3e, 0xb5, 0xe2, 0x13, 0x29, 0x27, + 0x25, 0x8d, 0xdb, 0xca, 0xc9, 0xce, 0x8c, 0x04, 0xd0, 0xb3, 0x0c, 0xe8, 0x19, 0x7c, 0x2a, 0x1e, + 0x68, 0x60, 0xe8, 0x6b, 0x1a, 0xbc, 0x34, 0x9a, 0x0c, 0x9e, 0xf8, 0x0c, 0xda, 0x46, 0xc6, 0x54, + 0x06, 0x49, 0xb3, 0x6b, 0x2a, 0x83, 0xc4, 0xa9, 0x34, 0x7f, 0x99, 0x31, 0x98, 0xc3, 0xe7, 0xd7, + 0x7f, 0x25, 0x48, 0x78, 0x96, 0xc5, 0xdf, 0x65, 0x60, 0x24, 0x76, 0xe6, 0xc2, 0xa7, 0x56, 0x07, + 0x18, 0x37, 0x54, 0x2a, 0xa7, 0x3b, 0xb6, 0x13, 0xdc, 0xbe, 0x46, 0x8c, 0xdc, 0x97, 0x08, 0x7f, + 0xd1, 0x0d, 0xbb, 0xe8, 0x7c, 0x48, 0xe4, 0xa0, 0x49, 0x96, 0x5b, 0x46, 0xd6, 0x06, 0xe1, 0x65, + 0x20, 0xf4, 0x81, 0x2f, 0x34, 0xf0, 0x33, 0x04, 0xbb, 0x5b, 0xfb, 0x7e, 0x3c, 0x95, 0xcc, 0x2b, + 0x61, 0xae, 0x53, 0xa6, 0x3b, 0x31, 0x11, 0x2a, 0x7c, 0xc6, 0x44, 0xb8, 0x89, 0xaf, 0x77, 0xa1, + 0x41, 0xdb, 0x5f, 0x5a, 0x97, 0x2c, 0xcb, 0xf2, 0xd9, 0xc0, 0x4f, 0x11, 0xec, 0x69, 0x9b, 0x6a, + 0x70, 0x07, 0x58, 0x83, 0x2c, 0x3c, 0xd1, 0x91, 0x8d, 0x20, 0x78, 0x8d, 0x11, 0xbc, 0x8c, 0x2f, + 0x6e, 0x28, 0x41, 0xfc, 0x1b, 0x82, 0xd7, 0x22, 0x03, 0x05, 0x56, 0x57, 0x43, 0x17, 0x9d, 0x75, + 0x14, 0xb2, 0xe6, 0xfd, 0x82, 0xc9, 0x27, 0x8c, 0xc9, 0xc7, 0xf8, 0x5a, 0xf7, 0x4c, 0xaa, 0xdc, + 0x75, 0x24, 0x4e, 0x2b, 0x08, 0x46, 0x62, 0x1b, 0xd0, 0xb4, 0xd4, 0x4c, 0x1b, 0x5f, 0xd2, 0x52, + 0x33, 0x75, 0xf8, 0xc8, 0xdf, 0x60, 0x4c, 0xe7, 0xf1, 0xd5, 0xee, 0x99, 0xea, 0xc6, 0x62, 0x84, + 0xe5, 0x2b, 0x04, 0x7b, 0xe3, 0xdb, 0x6c, 0xdc, 0x29, 0xdc, 0xe0, 0x5e, 0x9e, 0xe9, 0xdc, 0x50, + 0x10, 0xbd, 0xc9, 0x88, 0x7e, 0x84, 0xb5, 0x0d, 0x21, 0x1a, 0xa5, 0x73, 0x3f, 0x03, 0x7b, 0xda, + 0xda, 0xd7, 0xb4, 0xbc, 0x4b, 0x6a, 0xc2, 0xd3, 0xf2, 0x2e, 0xb1, 0x3f, 0xde, 0xa0, 0xf2, 0x1a, + 0x57, 0x5a, 0x52, 0x1a, 0xfb, 0x06, 0xa9, 0x05, 0x80, 0x0a, 0x8e, 0xa0, 0xfc, 0x0f, 0x82, 0x9d, + 0xd1, 0x26, 0x16, 0x93, 0xb5, 0x30, 0x0a, 0xb5, 0xdd, 0xca, 0xf1, 0xb5, 0x1b, 0x08, 0xfe, 0x9f, + 0x33, 0xfa, 0x75, 0xec, 0xf5, 0x86, 0x7d, 0xa4, 0x8b, 0x8f, 0xd0, 0xf6, 0x6f, 0x3c, 0xfe, 0x1d, + 0xc1, 0x50, 0x4c, 0x97, 0x8b, 0x53, 0xda, 0x80, 0xe4, 0x86, 0x5b, 0x79, 0xab, 0x43, 0x2b, 0x21, + 0xc1, 0x15, 0x26, 0xc1, 0x07, 0xf8, 0x42, 0x17, 0x12, 0x44, 0x7a, 0xf1, 0x99, 0xf9, 0xc7, 0x2f, + 0x72, 0xe8, 0xc9, 0x8b, 0x1c, 0xfa, 0xeb, 0x45, 0x0e, 0x7d, 0xbb, 0x92, 0xeb, 0x7b, 0xb2, 0x92, + 0xeb, 0xfb, 0x63, 0x25, 0xd7, 0x77, 0xf3, 0xed, 0xb2, 0xe9, 0x2d, 0xd4, 0x8a, 0xaa, 0x61, 0x57, + 0x88, 0xf8, 0xc7, 0xa0, 0x59, 0x34, 0x8e, 0x95, 0x6d, 0x52, 0x3f, 0x4d, 0x2a, 0x76, 0xa9, 0xb6, + 0x44, 0x5d, 0x0e, 0xe1, 0xf8, 0xc9, 0x63, 0x12, 0x85, 0x77, 0xcf, 0xa1, 0x6e, 0x71, 0x2b, 0xfb, + 0x11, 0xf7, 0xc4, 0x7f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x67, 0x58, 0x5b, 0xa1, 0xa8, 0x1c, 0x00, + 0x00, } // Reference imports to suppress errors if they are not otherwise used. diff --git a/modules/core/04-channel/types/query.pb.gw.go b/modules/core/04-channel/types/query.pb.gw.go index 9e59c03b536..0f9ed6eaab3 100644 --- a/modules/core/04-channel/types/query.pb.gw.go +++ b/modules/core/04-channel/types/query.pb.gw.go @@ -20,6 +20,7 @@ import ( "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/grpclog" + "google.golang.org/grpc/metadata" "google.golang.org/grpc/status" ) @@ -30,6 +31,7 @@ var _ status.Status var _ = runtime.String var _ = utilities.NewDoubleArray var _ = descriptor.ForMessage +var _ = metadata.Join func request_Query_Channel_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq QueryChannelRequest @@ -1168,12 +1170,14 @@ func local_request_Query_NextSequenceReceive_0(ctx context.Context, marshaler ru // RegisterQueryHandlerServer registers the http handlers for service Query to "mux". // UnaryRPC :call QueryServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. -// Note that using this registration option will cause many gRPC library features (such as grpc.SendHeader, etc) to stop working. Consider using RegisterQueryHandlerFromEndpoint instead. +// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterQueryHandlerFromEndpoint instead. func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, server QueryServer) error { mux.Handle("GET", pattern_Query_Channel_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { @@ -1181,6 +1185,7 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv return } resp, md, err := local_request_Query_Channel_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) @@ -1194,6 +1199,8 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv mux.Handle("GET", pattern_Query_Channels_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { @@ -1201,6 +1208,7 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv return } resp, md, err := local_request_Query_Channels_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) @@ -1214,6 +1222,8 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv mux.Handle("GET", pattern_Query_ConnectionChannels_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { @@ -1221,6 +1231,7 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv return } resp, md, err := local_request_Query_ConnectionChannels_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) @@ -1234,6 +1245,8 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv mux.Handle("GET", pattern_Query_ChannelClientState_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { @@ -1241,6 +1254,7 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv return } resp, md, err := local_request_Query_ChannelClientState_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) @@ -1254,6 +1268,8 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv mux.Handle("GET", pattern_Query_ChannelConsensusState_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { @@ -1261,6 +1277,7 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv return } resp, md, err := local_request_Query_ChannelConsensusState_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) @@ -1274,6 +1291,8 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv mux.Handle("GET", pattern_Query_PacketCommitment_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { @@ -1281,6 +1300,7 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv return } resp, md, err := local_request_Query_PacketCommitment_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) @@ -1294,6 +1314,8 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv mux.Handle("GET", pattern_Query_PacketCommitments_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { @@ -1301,6 +1323,7 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv return } resp, md, err := local_request_Query_PacketCommitments_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) @@ -1314,6 +1337,8 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv mux.Handle("GET", pattern_Query_PacketReceipt_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { @@ -1321,6 +1346,7 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv return } resp, md, err := local_request_Query_PacketReceipt_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) @@ -1334,6 +1360,8 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv mux.Handle("GET", pattern_Query_PacketAcknowledgement_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { @@ -1341,6 +1369,7 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv return } resp, md, err := local_request_Query_PacketAcknowledgement_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) @@ -1354,6 +1383,8 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv mux.Handle("GET", pattern_Query_PacketAcknowledgements_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { @@ -1361,6 +1392,7 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv return } resp, md, err := local_request_Query_PacketAcknowledgements_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) @@ -1374,6 +1406,8 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv mux.Handle("GET", pattern_Query_UnreceivedPackets_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { @@ -1381,6 +1415,7 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv return } resp, md, err := local_request_Query_UnreceivedPackets_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) @@ -1394,6 +1429,8 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv mux.Handle("GET", pattern_Query_UnreceivedAcks_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { @@ -1401,6 +1438,7 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv return } resp, md, err := local_request_Query_UnreceivedAcks_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) @@ -1414,6 +1452,8 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv mux.Handle("GET", pattern_Query_NextSequenceReceive_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { @@ -1421,6 +1461,7 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv return } resp, md, err := local_request_Query_NextSequenceReceive_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) @@ -1736,31 +1777,31 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie } var ( - pattern_Query_Channel_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7}, []string{"ibc", "core", "channel", "v1", "channels", "channel_id", "ports", "port_id"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_Channel_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7}, []string{"ibc", "core", "channel", "v1", "channels", "channel_id", "ports", "port_id"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_Channels_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"ibc", "core", "channel", "v1", "channels"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_Channels_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"ibc", "core", "channel", "v1", "channels"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_ConnectionChannels_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6}, []string{"ibc", "core", "channel", "v1", "connections", "connection", "channels"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_ConnectionChannels_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6}, []string{"ibc", "core", "channel", "v1", "connections", "connection", "channels"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_ChannelClientState_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 2, 8}, []string{"ibc", "core", "channel", "v1", "channels", "channel_id", "ports", "port_id", "client_state"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_ChannelClientState_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 2, 8}, []string{"ibc", "core", "channel", "v1", "channels", "channel_id", "ports", "port_id", "client_state"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_ChannelConsensusState_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 2, 8, 2, 9, 1, 0, 4, 1, 5, 10, 2, 11, 1, 0, 4, 1, 5, 12}, []string{"ibc", "core", "channel", "v1", "channels", "channel_id", "ports", "port_id", "consensus_state", "revision", "revision_number", "height", "revision_height"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_ChannelConsensusState_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 2, 8, 2, 9, 1, 0, 4, 1, 5, 10, 2, 11, 1, 0, 4, 1, 5, 12}, []string{"ibc", "core", "channel", "v1", "channels", "channel_id", "ports", "port_id", "consensus_state", "revision", "revision_number", "height", "revision_height"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_PacketCommitment_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 2, 8, 1, 0, 4, 1, 5, 9}, []string{"ibc", "core", "channel", "v1", "channels", "channel_id", "ports", "port_id", "packet_commitments", "sequence"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_PacketCommitment_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 2, 8, 1, 0, 4, 1, 5, 9}, []string{"ibc", "core", "channel", "v1", "channels", "channel_id", "ports", "port_id", "packet_commitments", "sequence"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_PacketCommitments_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 2, 8}, []string{"ibc", "core", "channel", "v1", "channels", "channel_id", "ports", "port_id", "packet_commitments"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_PacketCommitments_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 2, 8}, []string{"ibc", "core", "channel", "v1", "channels", "channel_id", "ports", "port_id", "packet_commitments"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_PacketReceipt_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 2, 8, 1, 0, 4, 1, 5, 9}, []string{"ibc", "core", "channel", "v1", "channels", "channel_id", "ports", "port_id", "packet_receipts", "sequence"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_PacketReceipt_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 2, 8, 1, 0, 4, 1, 5, 9}, []string{"ibc", "core", "channel", "v1", "channels", "channel_id", "ports", "port_id", "packet_receipts", "sequence"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_PacketAcknowledgement_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 2, 8, 1, 0, 4, 1, 5, 9}, []string{"ibc", "core", "channel", "v1", "channels", "channel_id", "ports", "port_id", "packet_acks", "sequence"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_PacketAcknowledgement_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 2, 8, 1, 0, 4, 1, 5, 9}, []string{"ibc", "core", "channel", "v1", "channels", "channel_id", "ports", "port_id", "packet_acks", "sequence"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_PacketAcknowledgements_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 2, 8}, []string{"ibc", "core", "channel", "v1", "channels", "channel_id", "ports", "port_id", "packet_acknowledgements"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_PacketAcknowledgements_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 2, 8}, []string{"ibc", "core", "channel", "v1", "channels", "channel_id", "ports", "port_id", "packet_acknowledgements"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_UnreceivedPackets_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 2, 8, 1, 0, 4, 1, 5, 9, 2, 10}, []string{"ibc", "core", "channel", "v1", "channels", "channel_id", "ports", "port_id", "packet_commitments", "packet_commitment_sequences", "unreceived_packets"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_UnreceivedPackets_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 2, 8, 1, 0, 4, 1, 5, 9, 2, 10}, []string{"ibc", "core", "channel", "v1", "channels", "channel_id", "ports", "port_id", "packet_commitments", "packet_commitment_sequences", "unreceived_packets"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_UnreceivedAcks_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 2, 8, 1, 0, 4, 1, 5, 9, 2, 10}, []string{"ibc", "core", "channel", "v1", "channels", "channel_id", "ports", "port_id", "packet_commitments", "packet_ack_sequences", "unreceived_acks"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_UnreceivedAcks_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 2, 8, 1, 0, 4, 1, 5, 9, 2, 10}, []string{"ibc", "core", "channel", "v1", "channels", "channel_id", "ports", "port_id", "packet_commitments", "packet_ack_sequences", "unreceived_acks"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_NextSequenceReceive_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 2, 8}, []string{"ibc", "core", "channel", "v1", "channels", "channel_id", "ports", "port_id", "next_sequence"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_NextSequenceReceive_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 2, 8}, []string{"ibc", "core", "channel", "v1", "channels", "channel_id", "ports", "port_id", "next_sequence"}, "", runtime.AssumeColonVerbOpt(false))) ) var ( diff --git a/modules/core/04-channel/types/tx.pb.go b/modules/core/04-channel/types/tx.pb.go index 359060b801f..47b4b3992f6 100644 --- a/modules/core/04-channel/types/tx.pb.go +++ b/modules/core/04-channel/types/tx.pb.go @@ -6,10 +6,10 @@ package types import ( context "context" fmt "fmt" - types "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - _ "github.com/gogo/protobuf/gogoproto" - grpc1 "github.com/gogo/protobuf/grpc" - proto "github.com/gogo/protobuf/proto" + _ "github.com/cosmos/gogoproto/gogoproto" + grpc1 "github.com/cosmos/gogoproto/grpc" + proto "github.com/cosmos/gogoproto/proto" + types "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" @@ -205,7 +205,8 @@ var xxx_messageInfo_MsgChannelOpenTry proto.InternalMessageInfo // MsgChannelOpenTryResponse defines the Msg/ChannelOpenTry response type. type MsgChannelOpenTryResponse struct { - Version string `protobuf:"bytes,1,opt,name=version,proto3" json:"version,omitempty"` + Version string `protobuf:"bytes,1,opt,name=version,proto3" json:"version,omitempty"` + ChannelId string `protobuf:"bytes,2,opt,name=channel_id,json=channelId,proto3" json:"channel_id,omitempty" yaml:"channel_id"` } func (m *MsgChannelOpenTryResponse) Reset() { *m = MsgChannelOpenTryResponse{} } @@ -248,6 +249,13 @@ func (m *MsgChannelOpenTryResponse) GetVersion() string { return "" } +func (m *MsgChannelOpenTryResponse) GetChannelId() string { + if m != nil { + return m.ChannelId + } + return "" +} + // MsgChannelOpenAck defines a msg sent by a Relayer to Chain A to acknowledge // the change of channel state to TRYOPEN on Chain B. type MsgChannelOpenAck struct { @@ -1420,125 +1428,126 @@ func init() { func init() { proto.RegisterFile("ibc/core/channel/v1/tx.proto", fileDescriptor_bc4637e0ac3fc7b7) } var fileDescriptor_bc4637e0ac3fc7b7 = []byte{ - // 1886 bytes of a gzipped FileDescriptorProto + // 1891 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xdc, 0x5a, 0xcb, 0x6f, 0xe3, 0xc6, 0x19, 0xd7, 0x5b, 0xf6, 0xe7, 0x97, 0x4c, 0xd9, 0x5e, 0x2d, 0x6d, 0x8b, 0x32, 0x8b, 0xc6, 0xee, - 0xb6, 0x96, 0x62, 0xc7, 0x46, 0x91, 0x45, 0x8b, 0xc2, 0x52, 0x15, 0xd4, 0x68, 0xfd, 0x00, 0x25, - 0xb7, 0xe8, 0xb6, 0xa8, 0x2a, 0x53, 0x13, 0x99, 0x90, 0x44, 0x2a, 0x24, 0xa5, 0x44, 0x05, 0xfa, - 0xb8, 0x04, 0x08, 0xf6, 0x94, 0x73, 0x80, 0x05, 0x52, 0xf4, 0x54, 0xf4, 0xd0, 0xfc, 0x19, 0x41, - 0x4f, 0xb9, 0x35, 0xed, 0x41, 0x08, 0x76, 0x2f, 0x3d, 0xeb, 0x2f, 0x28, 0x38, 0x1c, 0x52, 0x7c, - 0xae, 0x29, 0xc7, 0x92, 0xd3, 0x9e, 0x44, 0xce, 0xfc, 0xe6, 0xfb, 0x66, 0x7e, 0xdf, 0x6f, 0xbe, - 0x79, 0x50, 0xb0, 0x25, 0x5c, 0xf3, 0x05, 0x5e, 0x92, 0x51, 0x81, 0xbf, 0xa9, 0x8b, 0x22, 0x6a, - 0x17, 0xfa, 0x07, 0x05, 0xf5, 0x83, 0x7c, 0x57, 0x96, 0x54, 0x89, 0x4a, 0x0b, 0xd7, 0x7c, 0x5e, - 0xab, 0xcd, 0x93, 0xda, 0x7c, 0xff, 0x80, 0x5e, 0x6b, 0x4a, 0x4d, 0x09, 0xd7, 0x17, 0xb4, 0x27, - 0x1d, 0x4a, 0x33, 0x63, 0x43, 0x6d, 0x01, 0x89, 0xaa, 0x66, 0x47, 0x7f, 0x22, 0x80, 0x1d, 0x2f, - 0x4f, 0x86, 0xd9, 0xd7, 0x40, 0x7a, 0xdd, 0xa6, 0x5c, 0x6f, 0x20, 0x1d, 0xc2, 0xfe, 0x39, 0x0c, - 0xd4, 0x99, 0xd2, 0x2c, 0xe9, 0xf5, 0x17, 0x5d, 0x24, 0x9e, 0x8a, 0x82, 0x4a, 0x7d, 0x17, 0x92, - 0x5d, 0x49, 0x56, 0x6b, 0x42, 0x23, 0x13, 0xce, 0x85, 0xf7, 0xe6, 0x8b, 0xd4, 0x68, 0xc8, 0x2c, - 0x0f, 0xea, 0x9d, 0xf6, 0x53, 0x96, 0x54, 0xb0, 0x5c, 0x42, 0x7b, 0x3a, 0x6d, 0x50, 0x3f, 0x80, - 0x24, 0xb1, 0x9f, 0x89, 0xe4, 0xc2, 0x7b, 0x0b, 0x87, 0x5b, 0x79, 0x8f, 0x71, 0xe6, 0x89, 0x8f, - 0x62, 0xec, 0xf3, 0x21, 0x13, 0xe2, 0x8c, 0x26, 0xd4, 0x06, 0x24, 0x14, 0xa1, 0x29, 0x22, 0x39, - 0x13, 0xd5, 0x3c, 0x71, 0xe4, 0xed, 0xe9, 0xdc, 0x47, 0x9f, 0x32, 0xa1, 0xff, 0x7c, 0xca, 0x84, - 0xd8, 0x36, 0xd0, 0xee, 0x2e, 0x72, 0x48, 0xe9, 0x4a, 0xa2, 0x82, 0xa8, 0x23, 0x00, 0x62, 0x6a, - 0xdc, 0xdb, 0xf5, 0xd1, 0x90, 0x59, 0xd5, 0x7b, 0x3b, 0xae, 0x63, 0xb9, 0x79, 0xf2, 0x72, 0xda, - 0xa0, 0x32, 0x90, 0xec, 0x23, 0x59, 0x11, 0x24, 0x11, 0xf7, 0x79, 0x9e, 0x33, 0x5e, 0xd9, 0x2f, - 0xa3, 0xb0, 0x6a, 0x77, 0x57, 0x95, 0x07, 0x93, 0x11, 0x72, 0x09, 0xe9, 0xae, 0x8c, 0xfa, 0x82, - 0xd4, 0x53, 0x6a, 0x96, 0xbe, 0x61, 0x47, 0xc5, 0xdc, 0x68, 0xc8, 0xd0, 0xa4, 0xa1, 0x1b, 0xc4, - 0x66, 0xc2, 0xdc, 0xaa, 0x51, 0x5e, 0x32, 0xbb, 0x6b, 0xa1, 0x38, 0x3a, 0x39, 0xc5, 0x1c, 0xac, - 0xf1, 0x52, 0x4f, 0x54, 0x91, 0xdc, 0xad, 0xcb, 0xea, 0xa0, 0x66, 0x8c, 0x3c, 0x86, 0x3b, 0xc4, - 0x8c, 0x86, 0xcc, 0x26, 0x21, 0xcb, 0x03, 0xc5, 0x72, 0x69, 0x6b, 0xf1, 0xcf, 0xf5, 0x52, 0x8d, - 0xf6, 0xae, 0x2c, 0x49, 0xef, 0xd6, 0x04, 0x51, 0x50, 0x33, 0xf1, 0x5c, 0x78, 0x6f, 0xd1, 0x4a, - 0xfb, 0xb8, 0x8e, 0xe5, 0xe6, 0xf1, 0x0b, 0xd6, 0xd5, 0x33, 0x58, 0xd4, 0x6b, 0x6e, 0x90, 0xd0, - 0xbc, 0x51, 0x33, 0x09, 0x3c, 0x18, 0xda, 0x32, 0x18, 0x5d, 0xe2, 0xfd, 0x83, 0xfc, 0x4f, 0x30, - 0xa2, 0xb8, 0xa9, 0x0d, 0x65, 0x34, 0x64, 0xd2, 0x56, 0xbb, 0x7a, 0x6b, 0x96, 0x5b, 0xc0, 0xaf, - 0x3a, 0xd2, 0x22, 0xa4, 0xa4, 0x8f, 0x90, 0x8e, 0xe1, 0xb1, 0x2b, 0xb2, 0xa6, 0x8e, 0x2c, 0x8a, - 0x08, 0xdb, 0x15, 0xf1, 0x4f, 0x97, 0x22, 0x4e, 0xf8, 0xd6, 0x64, 0x8a, 0xb0, 0x8b, 0x34, 0x12, - 0x50, 0xa4, 0xcf, 0xe0, 0x91, 0x2d, 0x22, 0x16, 0x13, 0x78, 0xae, 0x14, 0xd9, 0xd1, 0x90, 0xc9, - 0x7a, 0x84, 0xce, 0x6a, 0x6f, 0xdd, 0x5a, 0x33, 0x56, 0xd4, 0x34, 0x34, 0x71, 0x00, 0x7a, 0xa8, - 0x6b, 0xaa, 0x3c, 0x20, 0x92, 0x58, 0x1b, 0x0d, 0x99, 0x94, 0x35, 0x74, 0xaa, 0x3c, 0x60, 0xb9, - 0x39, 0xfc, 0xac, 0xcd, 0xab, 0x87, 0x15, 0xc4, 0xa6, 0x53, 0x10, 0x27, 0x7c, 0xcb, 0x10, 0x04, - 0xfb, 0xb7, 0x08, 0xac, 0xdb, 0x6b, 0x4b, 0x92, 0xf8, 0xae, 0x20, 0x77, 0x66, 0x11, 0x7a, 0x93, - 0xca, 0x3a, 0xdf, 0xc2, 0xc1, 0xf6, 0xa0, 0xb2, 0xce, 0xb7, 0x0c, 0x2a, 0x35, 0x41, 0x3a, 0xa9, - 0x8c, 0x4d, 0x85, 0xca, 0xb8, 0x0f, 0x95, 0x0c, 0x6c, 0x7b, 0x92, 0x65, 0xd2, 0xf9, 0x49, 0x18, - 0xd2, 0x63, 0x44, 0xa9, 0x2d, 0x29, 0x68, 0xf2, 0xa5, 0xe6, 0x6e, 0x64, 0xde, 0xbe, 0xc4, 0x6c, - 0xc3, 0xa6, 0x47, 0xdf, 0xcc, 0xbe, 0xff, 0x3d, 0x02, 0x1b, 0x8e, 0xfa, 0x19, 0x6a, 0xc1, 0x9e, - 0x6a, 0xa3, 0x77, 0x4c, 0xb5, 0xb3, 0x95, 0x43, 0x0e, 0xb2, 0xde, 0x84, 0x99, 0x9c, 0x7e, 0x1c, - 0x81, 0xa5, 0x33, 0xa5, 0xc9, 0x21, 0xbe, 0x7f, 0x59, 0xe7, 0x5b, 0x48, 0xa5, 0xde, 0x86, 0x44, - 0x17, 0x3f, 0x61, 0x26, 0x17, 0x0e, 0x37, 0x3d, 0xd7, 0x38, 0x1d, 0x4c, 0x96, 0x38, 0xd2, 0x80, - 0x7a, 0x07, 0x52, 0x7a, 0x77, 0x79, 0xa9, 0xd3, 0x11, 0xd4, 0x0e, 0x12, 0x55, 0x4c, 0xef, 0x62, - 0x71, 0x73, 0x34, 0x64, 0x1e, 0x59, 0x07, 0x34, 0x46, 0xb0, 0xdc, 0x0a, 0x2e, 0x2a, 0x99, 0x25, - 0x2e, 0xd2, 0xa2, 0x53, 0x21, 0x2d, 0xe6, 0x43, 0xda, 0x6f, 0x70, 0xc2, 0x19, 0x33, 0x62, 0xae, - 0x4d, 0x3f, 0x82, 0x84, 0x8c, 0x94, 0x5e, 0x5b, 0x67, 0x66, 0xf9, 0x70, 0xd7, 0x93, 0x19, 0x03, - 0xce, 0x61, 0x68, 0x75, 0xd0, 0x45, 0x1c, 0x69, 0xf6, 0x34, 0xa6, 0xf9, 0x60, 0xff, 0x1d, 0x01, - 0x38, 0x53, 0x9a, 0x55, 0xa1, 0x83, 0xa4, 0xde, 0xfd, 0xf0, 0xdd, 0x13, 0x65, 0xc4, 0x23, 0xa1, - 0x8f, 0x1a, 0x7e, 0x7c, 0x8f, 0x11, 0x06, 0xdf, 0x57, 0x66, 0xc9, 0x54, 0xf9, 0xfe, 0x29, 0x50, - 0x22, 0xfa, 0x40, 0xad, 0x29, 0xe8, 0xbd, 0x1e, 0x12, 0x79, 0x54, 0x93, 0x11, 0xdf, 0xc7, 0xdc, - 0xc7, 0x8a, 0xdb, 0xa3, 0x21, 0xf3, 0x58, 0xb7, 0xe0, 0xc6, 0xb0, 0x5c, 0x4a, 0x2b, 0xac, 0x90, - 0x32, 0x2d, 0x1e, 0x01, 0x14, 0xff, 0x2b, 0xbc, 0x91, 0x26, 0xdc, 0xde, 0x77, 0xe4, 0x3e, 0xd1, - 0xb7, 0x20, 0xc4, 0xfa, 0x85, 0x88, 0x67, 0xd4, 0x37, 0x21, 0x80, 0xdf, 0x87, 0x05, 0x32, 0xad, - 0xb4, 0x1e, 0x91, 0xe4, 0xb4, 0x31, 0x1a, 0x32, 0x94, 0x6d, 0xce, 0x69, 0x95, 0x2c, 0xa7, 0xa7, - 0x31, 0xbd, 0xef, 0xd3, 0x4c, 0x4f, 0xde, 0x91, 0x8f, 0x7f, 0xdd, 0xc8, 0x27, 0x7c, 0x22, 0x7f, - 0x8d, 0x77, 0x11, 0xf6, 0xd8, 0xdc, 0xb7, 0x00, 0x3e, 0x8b, 0x60, 0x79, 0x9d, 0xf0, 0x2d, 0x51, - 0x7a, 0xbf, 0x8d, 0x1a, 0x4d, 0x84, 0xf3, 0xd5, 0xd7, 0x50, 0xc0, 0x1e, 0xac, 0xd4, 0xed, 0xd6, - 0x74, 0x01, 0x70, 0xce, 0xe2, 0x71, 0x8c, 0xb5, 0x86, 0x0d, 0xbf, 0x18, 0xe3, 0x4a, 0x23, 0xc6, - 0x27, 0xda, 0xcb, 0x03, 0x2f, 0x41, 0x3c, 0x3e, 0x36, 0x3a, 0x18, 0xbb, 0xef, 0xb8, 0xfc, 0x23, - 0x6a, 0xdd, 0x24, 0x5e, 0xe9, 0x67, 0xeb, 0x59, 0xed, 0x6b, 0xfe, 0x08, 0x99, 0xae, 0x2c, 0x75, - 0x25, 0x05, 0x35, 0x6a, 0xe4, 0x58, 0x5f, 0x9b, 0xe4, 0x98, 0xb8, 0x4b, 0xd8, 0x66, 0x4c, 0xb6, - 0x3d, 0x6d, 0xb1, 0xdc, 0x86, 0x51, 0x45, 0x06, 0x48, 0x0c, 0x50, 0xbf, 0x85, 0x65, 0x55, 0x97, - 0x7d, 0xf0, 0x10, 0x6f, 0x13, 0xa7, 0xeb, 0xba, 0x53, 0x7b, 0x7b, 0x96, 0x5b, 0x22, 0x05, 0x24, - 0xcc, 0xa7, 0xb0, 0x6a, 0x20, 0xb4, 0x5f, 0x45, 0xad, 0x77, 0xba, 0x64, 0x26, 0x6f, 0x8d, 0x86, - 0x4c, 0xc6, 0x6e, 0xc4, 0x84, 0xb0, 0x5c, 0x8a, 0x94, 0x55, 0x8d, 0xa2, 0x00, 0x13, 0xd9, 0xb6, - 0x87, 0xb5, 0xc4, 0xd2, 0xdc, 0xb3, 0x7c, 0x95, 0x84, 0x35, 0x17, 0x62, 0xe2, 0xeb, 0x81, 0xbb, - 0x05, 0x5b, 0x75, 0x1c, 0xd8, 0x26, 0x09, 0xf4, 0xb7, 0x08, 0xe7, 0x9b, 0xfe, 0x67, 0x45, 0xc7, - 0x91, 0xce, 0x88, 0xf0, 0x15, 0xd8, 0xce, 0x8f, 0x66, 0xba, 0x24, 0xeb, 0x68, 0x6e, 0x34, 0x64, - 0xb6, 0x3c, 0x8c, 0x1a, 0x30, 0x96, 0xb3, 0x75, 0xda, 0x48, 0xac, 0xaf, 0x55, 0x6e, 0xfc, 0x61, - 0x94, 0x9b, 0x98, 0x85, 0x72, 0x93, 0x77, 0x52, 0xee, 0x0f, 0x61, 0x89, 0xac, 0xa3, 0x84, 0xa2, - 0x39, 0x9c, 0x82, 0x33, 0xa3, 0x21, 0xb3, 0x66, 0x5b, 0x66, 0x8d, 0x51, 0xeb, 0x69, 0xd7, 0x18, - 0x6b, 0x15, 0xd6, 0xc9, 0x4a, 0x4e, 0xd8, 0x21, 0x0e, 0x32, 0xf3, 0xd8, 0x8c, 0x25, 0x86, 0x9e, - 0x30, 0x96, 0x4b, 0xeb, 0xab, 0x3e, 0x91, 0x3b, 0xd9, 0x3d, 0xfe, 0x02, 0x36, 0xec, 0x70, 0x53, - 0x1a, 0x80, 0xcd, 0xee, 0x8c, 0x86, 0xcc, 0xb6, 0x97, 0x59, 0x8b, 0x36, 0xac, 0x76, 0x4d, 0x6d, - 0x38, 0x57, 0x8d, 0x85, 0xa9, 0xac, 0x1a, 0x8b, 0x3e, 0x39, 0x20, 0x0b, 0x5b, 0x5e, 0x33, 0xdc, - 0x4c, 0x01, 0x1f, 0xc6, 0x3c, 0x52, 0xc0, 0x8c, 0xee, 0x83, 0x1e, 0x26, 0x05, 0xb8, 0xd4, 0x17, - 0x9b, 0x48, 0x7d, 0xfe, 0x3a, 0x89, 0xdf, 0xaf, 0x4e, 0x66, 0x7b, 0x75, 0xe4, 0xa5, 0x13, 0xeb, - 0xed, 0xd1, 0xbf, 0x62, 0x90, 0x71, 0x01, 0x66, 0x78, 0x69, 0xf0, 0x3f, 0xa9, 0x95, 0x73, 0x48, - 0xdb, 0x35, 0x80, 0x64, 0x59, 0x92, 0x89, 0x50, 0xb2, 0xd6, 0x8b, 0x73, 0x17, 0x88, 0xe5, 0x56, - 0xad, 0x2a, 0x29, 0x6b, 0x65, 0xaf, 0xd1, 0x5e, 0xe2, 0x7e, 0xb5, 0x97, 0x9c, 0x8a, 0xf6, 0xe6, - 0x7c, 0xb4, 0xc7, 0x42, 0xce, 0x4f, 0x5a, 0xa6, 0xfe, 0xfe, 0x14, 0xf7, 0xd0, 0x9f, 0x91, 0xbb, - 0xff, 0x6f, 0xf5, 0xf7, 0x07, 0x2d, 0xe0, 0xe4, 0xa3, 0x0a, 0x96, 0x45, 0x0d, 0x9f, 0x54, 0xbb, - 0xc6, 0xc6, 0x74, 0xc7, 0xd3, 0x2f, 0x16, 0x0b, 0xa7, 0x03, 0x8b, 0xdf, 0x26, 0xce, 0xb7, 0x1d, - 0xdf, 0x68, 0x6c, 0xe6, 0xb0, 0x2e, 0xf4, 0x0a, 0x6b, 0x63, 0xb7, 0xfe, 0xe3, 0x77, 0xd3, 0xbf, - 0xbd, 0xef, 0x09, 0x6f, 0xfd, 0x3b, 0x7a, 0xa4, 0xeb, 0xdf, 0xd6, 0x9d, 0x6f, 0x9e, 0x4c, 0x1d, - 0xf7, 0x23, 0xec, 0x67, 0x51, 0x78, 0xe4, 0xd6, 0x72, 0x5d, 0xe4, 0x51, 0x7b, 0x16, 0x2a, 0x6d, - 0xc0, 0x92, 0x9d, 0xea, 0x68, 0x50, 0x99, 0x6c, 0x11, 0xa2, 0x48, 0x58, 0x1d, 0xb1, 0x58, 0x44, - 0xd6, 0x30, 0xf8, 0x84, 0x35, 0x76, 0x5f, 0x61, 0x8d, 0x4f, 0x25, 0xac, 0x7e, 0xa7, 0xa4, 0x1d, - 0x60, 0x7c, 0x22, 0x66, 0x44, 0xf5, 0xc9, 0x5f, 0xc3, 0x40, 0xb9, 0x8f, 0xce, 0xd4, 0x31, 0xe4, - 0xb8, 0x72, 0xe5, 0xf2, 0xe2, 0xbc, 0x52, 0xae, 0x71, 0xe5, 0xca, 0xd5, 0xcf, 0xaa, 0xb5, 0xea, - 0x2f, 0x2f, 0xcb, 0xb5, 0xab, 0xf3, 0xca, 0x65, 0xb9, 0x74, 0xfa, 0xce, 0x69, 0xf9, 0xc7, 0xa9, - 0x10, 0xbd, 0xf2, 0xfc, 0x45, 0x6e, 0xc1, 0x52, 0x44, 0xed, 0xc2, 0x63, 0xcf, 0x66, 0xe7, 0x17, - 0x17, 0x97, 0xa9, 0x30, 0x3d, 0xf7, 0xfc, 0x45, 0x2e, 0xa6, 0x3d, 0x53, 0xfb, 0xb0, 0xe5, 0x09, - 0xac, 0x5c, 0x95, 0x4a, 0xe5, 0x4a, 0x25, 0x15, 0xa1, 0x17, 0x9e, 0xbf, 0xc8, 0x25, 0xc9, 0x2b, - 0x1d, 0xfb, 0xe8, 0x2f, 0xd9, 0xd0, 0xe1, 0x87, 0xcb, 0x10, 0x3d, 0x53, 0x9a, 0x54, 0x0b, 0x56, - 0x9c, 0x5f, 0xc1, 0xbd, 0xef, 0x04, 0xdc, 0xdf, 0xa2, 0xe9, 0x42, 0x40, 0xa0, 0x79, 0xfb, 0x70, - 0x03, 0xcb, 0x8e, 0x0f, 0xcc, 0x6f, 0x04, 0x30, 0x51, 0x95, 0x07, 0x74, 0x3e, 0x18, 0xce, 0xc7, - 0x93, 0xb6, 0x51, 0x0d, 0xe2, 0xe9, 0x84, 0x6f, 0x05, 0xf2, 0x64, 0xd9, 0xf1, 0x50, 0x2a, 0x50, - 0x1e, 0xdf, 0xca, 0x9e, 0x04, 0xb0, 0x42, 0xb0, 0xf4, 0x61, 0x70, 0xac, 0xe9, 0x55, 0x84, 0x94, - 0xeb, 0x93, 0xd2, 0xde, 0x2d, 0x76, 0x4c, 0x24, 0xfd, 0x66, 0x50, 0xa4, 0xe9, 0xef, 0x7d, 0x48, - 0x7b, 0x7e, 0x06, 0x0a, 0x62, 0xc8, 0x18, 0xe7, 0x5b, 0x13, 0x80, 0x4d, 0xc7, 0xbf, 0x06, 0xb0, - 0x7c, 0x2b, 0x61, 0xfd, 0x4c, 0x8c, 0x31, 0xf4, 0x93, 0xdb, 0x31, 0xa6, 0xf5, 0x0a, 0x24, 0x8d, - 0xcd, 0x01, 0xe3, 0xd7, 0x8c, 0x00, 0xe8, 0xdd, 0x5b, 0x00, 0x56, 0xed, 0x39, 0x6e, 0xac, 0xdf, - 0xb8, 0xa5, 0x29, 0xc1, 0xf9, 0x6b, 0xcf, 0xe7, 0x96, 0xb5, 0x05, 0x2b, 0xce, 0xab, 0x51, 0xdf, - 0x5e, 0x3a, 0x80, 0xfe, 0x93, 0xd7, 0xef, 0xea, 0x70, 0x2c, 0x74, 0xeb, 0x7d, 0xdf, 0x6d, 0x42, - 0xb7, 0x60, 0x6f, 0x15, 0xba, 0xc7, 0xdd, 0x13, 0xf5, 0x1e, 0xac, 0xba, 0xef, 0x9d, 0xbe, 0x13, - 0xcc, 0x90, 0x96, 0x38, 0x0e, 0x02, 0x43, 0xfd, 0x5d, 0x6a, 0xe9, 0x23, 0xa0, 0x4b, 0x2d, 0x83, - 0x1c, 0x04, 0x86, 0x9a, 0x2e, 0x7f, 0x0f, 0xeb, 0xde, 0x47, 0xa6, 0xfd, 0x60, 0xb6, 0x8c, 0x29, - 0x76, 0x3c, 0x11, 0xdc, 0xdf, 0xbd, 0x31, 0x29, 0x02, 0xba, 0x37, 0xa6, 0xc8, 0xf1, 0x44, 0x70, - 0xd3, 0xfd, 0xef, 0x60, 0xcd, 0x73, 0x27, 0xf4, 0xbd, 0x80, 0xa3, 0xc1, 0x68, 0xfa, 0x68, 0x12, - 0xb4, 0xe1, 0xbb, 0x58, 0xf9, 0xfc, 0x65, 0x36, 0xfc, 0xc5, 0xcb, 0x6c, 0xf8, 0xab, 0x97, 0xd9, - 0xf0, 0xc7, 0xaf, 0xb2, 0xa1, 0x2f, 0x5e, 0x65, 0x43, 0x5f, 0xbe, 0xca, 0x86, 0x9e, 0xbd, 0xdd, - 0x14, 0xd4, 0x9b, 0xde, 0x75, 0x9e, 0x97, 0x3a, 0x05, 0x5e, 0x52, 0x3a, 0x92, 0x52, 0x10, 0xae, - 0xf9, 0xfd, 0xa6, 0x54, 0xe8, 0x1f, 0x15, 0x3a, 0x52, 0xa3, 0xd7, 0x46, 0x8a, 0xfe, 0x37, 0xb3, - 0x37, 0x8f, 0xf6, 0x8d, 0x7f, 0x9a, 0xa9, 0x83, 0x2e, 0x52, 0xae, 0x13, 0xf8, 0x5f, 0x66, 0x6f, - 0xfd, 0x37, 0x00, 0x00, 0xff, 0xff, 0xfd, 0xc0, 0xd1, 0x87, 0x17, 0x27, 0x00, 0x00, + 0xb6, 0x96, 0x62, 0x67, 0x17, 0x41, 0x16, 0x2d, 0x0a, 0x4b, 0x55, 0x50, 0xa3, 0xf5, 0x03, 0x94, + 0xdc, 0xa2, 0xdb, 0xa2, 0xaa, 0x4c, 0x4d, 0x64, 0x42, 0x12, 0xa9, 0x90, 0x94, 0x12, 0x15, 0xe8, + 0xe3, 0x12, 0x20, 0xd8, 0x53, 0xce, 0x01, 0x16, 0x48, 0xd1, 0x53, 0xd1, 0x43, 0xf3, 0x67, 0x04, + 0x3d, 0xe5, 0xd6, 0xb4, 0x07, 0x61, 0xb1, 0x7b, 0xe9, 0x59, 0x7f, 0x41, 0xc1, 0xe1, 0x90, 0xe2, + 0x73, 0x4d, 0x79, 0x2d, 0x79, 0x9b, 0x93, 0xc8, 0x99, 0xdf, 0x7c, 0xdf, 0xcc, 0xef, 0xfb, 0xcd, + 0x37, 0x0f, 0x0a, 0xb6, 0x84, 0x4b, 0xbe, 0xc0, 0x4b, 0x32, 0x2a, 0xf0, 0x57, 0x75, 0x51, 0x44, + 0xed, 0x42, 0xff, 0xa0, 0xa0, 0x7e, 0x9c, 0xef, 0xca, 0x92, 0x2a, 0x51, 0x69, 0xe1, 0x92, 0xcf, + 0x6b, 0xb5, 0x79, 0x52, 0x9b, 0xef, 0x1f, 0xd0, 0x6b, 0x4d, 0xa9, 0x29, 0xe1, 0xfa, 0x82, 0xf6, + 0xa4, 0x43, 0x69, 0x66, 0x6c, 0xa8, 0x2d, 0x20, 0x51, 0xd5, 0xec, 0xe8, 0x4f, 0x04, 0xb0, 0xe3, + 0xe5, 0xc9, 0x30, 0xfb, 0x0a, 0x48, 0xaf, 0xdb, 0x94, 0xeb, 0x0d, 0xa4, 0x43, 0xd8, 0xbf, 0x84, + 0x81, 0x3a, 0x51, 0x9a, 0x25, 0xbd, 0xfe, 0xac, 0x8b, 0xc4, 0x63, 0x51, 0x50, 0xa9, 0xef, 0x43, + 0xb2, 0x2b, 0xc9, 0x6a, 0x4d, 0x68, 0x64, 0xc2, 0xb9, 0xf0, 0xde, 0x7c, 0x91, 0x1a, 0x0d, 0x99, + 0xe5, 0x41, 0xbd, 0xd3, 0x7e, 0xcc, 0x92, 0x0a, 0x96, 0x4b, 0x68, 0x4f, 0xc7, 0x0d, 0xea, 0x87, + 0x90, 0x24, 0xf6, 0x33, 0x91, 0x5c, 0x78, 0x6f, 0xe1, 0x70, 0x2b, 0xef, 0x31, 0xce, 0x3c, 0xf1, + 0x51, 0x8c, 0x7d, 0x35, 0x64, 0x42, 0x9c, 0xd1, 0x84, 0xda, 0x80, 0x84, 0x22, 0x34, 0x45, 0x24, + 0x67, 0xa2, 0x9a, 0x27, 0x8e, 0xbc, 0x3d, 0x9e, 0xfb, 0xf4, 0x0b, 0x26, 0xf4, 0xdf, 0x2f, 0x98, + 0x10, 0xdb, 0x06, 0xda, 0xdd, 0x45, 0x0e, 0x29, 0x5d, 0x49, 0x54, 0x10, 0xf5, 0x10, 0x80, 0x98, + 0x1a, 0xf7, 0x76, 0x7d, 0x34, 0x64, 0x56, 0xf5, 0xde, 0x8e, 0xeb, 0x58, 0x6e, 0x9e, 0xbc, 0x1c, + 0x37, 0xa8, 0x0c, 0x24, 0xfb, 0x48, 0x56, 0x04, 0x49, 0xc4, 0x7d, 0x9e, 0xe7, 0x8c, 0x57, 0xf6, + 0x9b, 0x28, 0xac, 0xda, 0xdd, 0x55, 0xe5, 0xc1, 0x64, 0x84, 0x9c, 0x43, 0xba, 0x2b, 0xa3, 0xbe, + 0x20, 0xf5, 0x94, 0x9a, 0xa5, 0x6f, 0xd8, 0x51, 0x31, 0x37, 0x1a, 0x32, 0x34, 0x69, 0xe8, 0x06, + 0xb1, 0x99, 0x30, 0xb7, 0x6a, 0x94, 0x97, 0xcc, 0xee, 0x5a, 0x28, 0x8e, 0x4e, 0x4e, 0x31, 0x07, + 0x6b, 0xbc, 0xd4, 0x13, 0x55, 0x24, 0x77, 0xeb, 0xb2, 0x3a, 0xa8, 0x19, 0x23, 0x8f, 0xe1, 0x0e, + 0x31, 0xa3, 0x21, 0xb3, 0x49, 0xc8, 0xf2, 0x40, 0xb1, 0x5c, 0xda, 0x5a, 0xfc, 0x0b, 0xbd, 0x54, + 0xa3, 0xbd, 0x2b, 0x4b, 0xd2, 0x07, 0x35, 0x41, 0x14, 0xd4, 0x4c, 0x3c, 0x17, 0xde, 0x5b, 0xb4, + 0xd2, 0x3e, 0xae, 0x63, 0xb9, 0x79, 0xfc, 0x82, 0x75, 0xf5, 0x04, 0x16, 0xf5, 0x9a, 0x2b, 0x24, + 0x34, 0xaf, 0xd4, 0x4c, 0x02, 0x0f, 0x86, 0xb6, 0x0c, 0x46, 0x97, 0x78, 0xff, 0x20, 0xff, 0x53, + 0x8c, 0x28, 0x6e, 0x6a, 0x43, 0x19, 0x0d, 0x99, 0xb4, 0xd5, 0xae, 0xde, 0x9a, 0xe5, 0x16, 0xf0, + 0xab, 0x8e, 0xb4, 0x08, 0x29, 0xe9, 0x23, 0xa4, 0x16, 0xdc, 0x77, 0x45, 0xd6, 0xd4, 0x91, 0x45, + 0x11, 0x61, 0x9b, 0x22, 0x1c, 0x0a, 0x8b, 0x04, 0x53, 0x18, 0xfb, 0x2f, 0x97, 0x8e, 0x8e, 0xf8, + 0xd6, 0x64, 0x3a, 0xba, 0x91, 0x63, 0xea, 0x09, 0xdc, 0xb3, 0xc5, 0xd1, 0x62, 0x02, 0xcf, 0xb0, + 0x22, 0x3b, 0x1a, 0x32, 0x59, 0x8f, 0x80, 0x5b, 0xed, 0xad, 0x5b, 0x6b, 0xc6, 0x3a, 0x9c, 0x86, + 0x92, 0x0e, 0x40, 0x17, 0x48, 0x4d, 0x95, 0x07, 0x44, 0x48, 0x6b, 0xa3, 0x21, 0x93, 0xb2, 0x06, + 0x5c, 0x95, 0x07, 0x2c, 0x37, 0x87, 0x9f, 0xb5, 0xd9, 0x78, 0xb7, 0x32, 0xda, 0x74, 0xca, 0xe8, + 0x88, 0x6f, 0x19, 0x32, 0x62, 0xff, 0x1e, 0x81, 0x75, 0x7b, 0x6d, 0x49, 0x12, 0x3f, 0x10, 0xe4, + 0xce, 0x2c, 0x42, 0x6f, 0x52, 0x59, 0xe7, 0x5b, 0x38, 0xd8, 0x1e, 0x54, 0xd6, 0xf9, 0x96, 0x41, + 0xa5, 0x26, 0x48, 0x27, 0x95, 0xb1, 0xa9, 0x50, 0x19, 0xf7, 0xa1, 0x92, 0x81, 0x6d, 0x4f, 0xb2, + 0x4c, 0x3a, 0x3f, 0x0f, 0x43, 0x7a, 0x8c, 0x28, 0xb5, 0x25, 0x05, 0x4d, 0xbe, 0x40, 0xdd, 0x8c, + 0xcc, 0xeb, 0x17, 0xa6, 0x6d, 0xd8, 0xf4, 0xe8, 0x9b, 0xd9, 0xf7, 0x7f, 0x44, 0x60, 0xc3, 0x51, + 0x3f, 0x43, 0x2d, 0xd8, 0x13, 0x74, 0xf4, 0x86, 0x09, 0x7a, 0xb6, 0x72, 0xc8, 0x41, 0xd6, 0x9b, + 0x30, 0x93, 0xd3, 0xcf, 0x22, 0xb0, 0x74, 0xa2, 0x34, 0x39, 0xc4, 0xf7, 0xcf, 0xeb, 0x7c, 0x0b, + 0xa9, 0xd4, 0x7b, 0x90, 0xe8, 0xe2, 0x27, 0xcc, 0xe4, 0xc2, 0xe1, 0xa6, 0xe7, 0xca, 0xa8, 0x83, + 0xc9, 0xc2, 0x48, 0x1a, 0x50, 0xef, 0x43, 0x4a, 0xef, 0x2e, 0x2f, 0x75, 0x3a, 0x82, 0xda, 0x41, + 0xa2, 0x8a, 0xe9, 0x5d, 0x2c, 0x6e, 0x8e, 0x86, 0xcc, 0x3d, 0xeb, 0x80, 0xc6, 0x08, 0x96, 0x5b, + 0xc1, 0x45, 0x25, 0xb3, 0xc4, 0x45, 0x5a, 0x74, 0x2a, 0xa4, 0xc5, 0x7c, 0x48, 0xfb, 0x2d, 0x4e, + 0x38, 0x63, 0x46, 0xcc, 0x15, 0xed, 0xc7, 0x90, 0x90, 0x91, 0xd2, 0x6b, 0xeb, 0xcc, 0x2c, 0x1f, + 0xee, 0x7a, 0x32, 0x63, 0xc0, 0x39, 0x0c, 0xad, 0x0e, 0xba, 0x88, 0x23, 0xcd, 0x1e, 0xc7, 0x34, + 0x1f, 0xec, 0x7f, 0x22, 0x00, 0x27, 0x4a, 0xb3, 0x2a, 0x74, 0x90, 0xd4, 0xbb, 0x1d, 0xbe, 0x7b, + 0xa2, 0x8c, 0x78, 0x24, 0xf4, 0x51, 0xc3, 0x8f, 0xef, 0x31, 0xc2, 0xe0, 0xfb, 0xc2, 0x2c, 0x99, + 0x2a, 0xdf, 0x3f, 0x03, 0x4a, 0x44, 0x1f, 0xab, 0x35, 0x05, 0x7d, 0xd8, 0x43, 0x22, 0x8f, 0x6a, + 0x32, 0xe2, 0xfb, 0x98, 0xfb, 0x58, 0x71, 0x7b, 0x34, 0x64, 0xee, 0xeb, 0x16, 0xdc, 0x18, 0x96, + 0x4b, 0x69, 0x85, 0x15, 0x52, 0xa6, 0xc5, 0x23, 0x80, 0xe2, 0x7f, 0x8d, 0xb7, 0xdf, 0x84, 0xdb, + 0xdb, 0x8e, 0xdc, 0xe7, 0xfa, 0x16, 0x84, 0x58, 0x3f, 0x13, 0xf1, 0x8c, 0x7a, 0x13, 0x02, 0xf8, + 0x2e, 0x2c, 0x90, 0x69, 0xa5, 0xf5, 0x88, 0x24, 0xa7, 0x8d, 0xd1, 0x90, 0xa1, 0x6c, 0x73, 0x4e, + 0xab, 0x64, 0x39, 0x3d, 0x8d, 0xe9, 0x7d, 0x9f, 0x66, 0x7a, 0xf2, 0x8e, 0x7c, 0xfc, 0x75, 0x23, + 0x9f, 0xf0, 0x89, 0xfc, 0x25, 0xde, 0x45, 0xd8, 0x63, 0x73, 0xdb, 0x02, 0xf8, 0x32, 0x82, 0xe5, + 0x75, 0xc4, 0xb7, 0x44, 0xe9, 0xa3, 0x36, 0x6a, 0x34, 0x11, 0xce, 0x57, 0xaf, 0xa1, 0x80, 0x3d, + 0x58, 0xa9, 0xdb, 0xad, 0xe9, 0x02, 0xe0, 0x9c, 0xc5, 0xe3, 0x18, 0x6b, 0x0d, 0x1b, 0x7e, 0x31, + 0xc6, 0x95, 0x46, 0x8c, 0x8f, 0xb4, 0x97, 0x3b, 0x5e, 0x82, 0x78, 0x7c, 0xd8, 0x74, 0x30, 0x76, + 0xdb, 0x71, 0xf9, 0x67, 0xd4, 0xba, 0x49, 0xbc, 0xd0, 0x4f, 0xe4, 0xb3, 0xda, 0xd7, 0xfc, 0x09, + 0x32, 0x5d, 0x59, 0xea, 0x4a, 0x0a, 0x6a, 0xd4, 0xc8, 0x65, 0x40, 0x6d, 0x92, 0xc3, 0xe5, 0x2e, + 0x61, 0x9b, 0x31, 0xd9, 0xf6, 0xb4, 0xc5, 0x72, 0x1b, 0x46, 0x15, 0x19, 0x20, 0x31, 0x40, 0xfd, + 0x0e, 0x96, 0x55, 0x5d, 0xf6, 0xc1, 0x43, 0xbc, 0x4d, 0x9c, 0xae, 0xeb, 0x4e, 0xed, 0xed, 0x59, + 0x6e, 0x89, 0x14, 0x90, 0x30, 0x1f, 0xc3, 0xaa, 0x81, 0xd0, 0x7e, 0x15, 0xb5, 0xde, 0xe9, 0x92, + 0x99, 0xbc, 0x35, 0x1a, 0x32, 0x19, 0xbb, 0x11, 0x13, 0xc2, 0x72, 0x29, 0x52, 0x56, 0x35, 0x8a, + 0x02, 0x4c, 0x64, 0xdb, 0x1e, 0xd6, 0x12, 0x4b, 0x73, 0xcf, 0xf2, 0x3c, 0x09, 0x6b, 0x2e, 0xc4, + 0xc4, 0x97, 0x0a, 0x37, 0x0b, 0xb6, 0xea, 0x38, 0xb0, 0x4d, 0x12, 0xe8, 0xef, 0x10, 0xce, 0x37, + 0xfd, 0xcf, 0x8a, 0x8e, 0x23, 0x9d, 0x11, 0xe1, 0x0b, 0xb0, 0x9d, 0x1f, 0xcd, 0x74, 0x49, 0xd6, + 0xd1, 0xdc, 0x68, 0xc8, 0x6c, 0x79, 0x18, 0x35, 0x60, 0x2c, 0x67, 0xeb, 0xb4, 0x91, 0x58, 0x5f, + 0xa9, 0xdc, 0xf8, 0xdd, 0x28, 0x37, 0x31, 0x0b, 0xe5, 0x26, 0x6f, 0xa4, 0xdc, 0x1f, 0xc1, 0x12, + 0x59, 0x47, 0x09, 0x45, 0x73, 0x38, 0x05, 0x67, 0x46, 0x43, 0x66, 0xcd, 0xb6, 0xcc, 0x1a, 0xa3, + 0xd6, 0xd3, 0xae, 0x31, 0xd6, 0x2a, 0xac, 0x93, 0x95, 0x9c, 0xb0, 0x43, 0x1c, 0x64, 0xe6, 0xb1, + 0x19, 0x4b, 0x0c, 0x3d, 0x61, 0x2c, 0x97, 0xd6, 0x57, 0x7d, 0x22, 0x77, 0xb2, 0x7b, 0xfc, 0x25, + 0x6c, 0xd8, 0xe1, 0xa6, 0x34, 0x00, 0x9b, 0xdd, 0x19, 0x0d, 0x99, 0x6d, 0x2f, 0xb3, 0x16, 0x6d, + 0x58, 0xed, 0x9a, 0xda, 0x70, 0xae, 0x1a, 0x0b, 0x53, 0x59, 0x35, 0x16, 0x7d, 0x72, 0x40, 0x16, + 0xb6, 0xbc, 0x66, 0xb8, 0x99, 0x02, 0x3e, 0x89, 0x79, 0xa4, 0x80, 0x19, 0xdd, 0x07, 0xdd, 0x4d, + 0x0a, 0x70, 0xa9, 0x2f, 0x36, 0x91, 0xfa, 0xfc, 0x75, 0x12, 0xbf, 0x5d, 0x9d, 0xcc, 0xf6, 0xea, + 0xc8, 0x4b, 0x27, 0xd6, 0xdb, 0xa3, 0x7f, 0xc7, 0x20, 0xe3, 0x02, 0xcc, 0xf0, 0xd2, 0xe0, 0xff, + 0x52, 0x2b, 0xa7, 0x90, 0xb6, 0x6b, 0x00, 0xc9, 0xb2, 0x24, 0x13, 0xa1, 0x64, 0xad, 0xd7, 0xed, + 0x2e, 0x10, 0xcb, 0xad, 0x5a, 0x55, 0x52, 0xd6, 0xca, 0x5e, 0xa1, 0xbd, 0xc4, 0xed, 0x6a, 0x2f, + 0x39, 0x15, 0xed, 0xcd, 0xf9, 0x68, 0x8f, 0x85, 0x9c, 0x9f, 0xb4, 0x4c, 0xfd, 0xfd, 0x39, 0xee, + 0xa1, 0x3f, 0x23, 0x77, 0x7f, 0x6b, 0xf5, 0xf7, 0x47, 0x2d, 0xe0, 0xe4, 0x53, 0x0c, 0x96, 0x45, + 0x0d, 0x9f, 0x54, 0xbb, 0xc6, 0xc6, 0x74, 0xc7, 0xd3, 0x2f, 0x16, 0x0b, 0xa7, 0x03, 0x8b, 0xdf, + 0x25, 0xce, 0xb7, 0x1d, 0x5f, 0x76, 0x6c, 0xe6, 0xb0, 0x2e, 0xf4, 0x0a, 0x6b, 0x63, 0xb7, 0xfe, + 0xe3, 0x37, 0xd3, 0xbf, 0xbd, 0xef, 0x09, 0x6f, 0xfd, 0x3b, 0x7a, 0xa4, 0xeb, 0xdf, 0xd6, 0x9d, + 0x37, 0x4f, 0xa6, 0x8e, 0xfb, 0x11, 0xf6, 0xcb, 0x28, 0xdc, 0x73, 0x6b, 0xb9, 0x2e, 0xf2, 0xa8, + 0x3d, 0x0b, 0x95, 0x36, 0x60, 0xc9, 0x4e, 0x75, 0x34, 0xa8, 0x4c, 0xb6, 0x08, 0x51, 0x24, 0xac, + 0x8e, 0x58, 0x2c, 0x22, 0x6b, 0x18, 0x7c, 0xc2, 0x1a, 0xbb, 0xad, 0xb0, 0xc6, 0xa7, 0x12, 0x56, + 0xbf, 0x53, 0xd2, 0x0e, 0x30, 0x3e, 0x11, 0x33, 0xa2, 0xfa, 0xe0, 0x6f, 0x61, 0xa0, 0xdc, 0x47, + 0x67, 0xea, 0x11, 0xe4, 0xb8, 0x72, 0xe5, 0xfc, 0xec, 0xb4, 0x52, 0xae, 0x71, 0xe5, 0xca, 0xc5, + 0xcf, 0xab, 0xb5, 0xea, 0xaf, 0xce, 0xcb, 0xb5, 0x8b, 0xd3, 0xca, 0x79, 0xb9, 0x74, 0xfc, 0xfe, + 0x71, 0xf9, 0x27, 0xa9, 0x10, 0xbd, 0xf2, 0xf4, 0x59, 0x6e, 0xc1, 0x52, 0x44, 0xed, 0xc2, 0x7d, + 0xcf, 0x66, 0xa7, 0x67, 0x67, 0xe7, 0xa9, 0x30, 0x3d, 0xf7, 0xf4, 0x59, 0x2e, 0xa6, 0x3d, 0x53, + 0xfb, 0xb0, 0xe5, 0x09, 0xac, 0x5c, 0x94, 0x4a, 0xe5, 0x4a, 0x25, 0x15, 0xa1, 0x17, 0x9e, 0x3e, + 0xcb, 0x25, 0xc9, 0x2b, 0x1d, 0xfb, 0xf4, 0xaf, 0xd9, 0xd0, 0xe1, 0x27, 0xcb, 0x10, 0x3d, 0x51, + 0x9a, 0x54, 0x0b, 0x56, 0x9c, 0xdf, 0xce, 0xbd, 0xef, 0x04, 0xdc, 0x5f, 0xb0, 0xe9, 0x42, 0x40, + 0xa0, 0x79, 0xfb, 0x70, 0x05, 0xcb, 0x8e, 0xcf, 0xd2, 0x6f, 0x05, 0x30, 0x51, 0x95, 0x07, 0x74, + 0x3e, 0x18, 0xce, 0xc7, 0x93, 0xb6, 0x51, 0x0d, 0xe2, 0xe9, 0x88, 0x6f, 0x05, 0xf2, 0x64, 0xd9, + 0xf1, 0x50, 0x2a, 0x50, 0x1e, 0xdf, 0xca, 0x1e, 0x04, 0xb0, 0x42, 0xb0, 0xf4, 0x61, 0x70, 0xac, + 0xe9, 0x55, 0x84, 0x94, 0xeb, 0x93, 0xd2, 0xde, 0x35, 0x76, 0x4c, 0x24, 0xfd, 0x76, 0x50, 0xa4, + 0xe9, 0xef, 0x23, 0x48, 0x7b, 0x7e, 0x06, 0x0a, 0x62, 0xc8, 0x18, 0xe7, 0x3b, 0x13, 0x80, 0x4d, + 0xc7, 0xbf, 0x01, 0xb0, 0x7c, 0x2b, 0x61, 0xfd, 0x4c, 0x8c, 0x31, 0xf4, 0x83, 0xeb, 0x31, 0xa6, + 0xf5, 0x0a, 0x24, 0x8d, 0xcd, 0x01, 0xe3, 0xd7, 0x8c, 0x00, 0xe8, 0xdd, 0x6b, 0x00, 0x56, 0xed, + 0x39, 0x6e, 0xac, 0xdf, 0xba, 0xa6, 0x29, 0xc1, 0xf9, 0x6b, 0xcf, 0xe7, 0x96, 0xb5, 0x05, 0x2b, + 0xce, 0xab, 0x51, 0xdf, 0x5e, 0x3a, 0x80, 0xfe, 0x93, 0xd7, 0xef, 0xea, 0x70, 0x2c, 0x74, 0xeb, + 0x7d, 0xdf, 0x75, 0x42, 0xb7, 0x60, 0xaf, 0x15, 0xba, 0xc7, 0xdd, 0x13, 0xf5, 0x21, 0xac, 0xba, + 0xef, 0x9d, 0xbe, 0x17, 0xcc, 0x90, 0x96, 0x38, 0x0e, 0x02, 0x43, 0xfd, 0x5d, 0x6a, 0xe9, 0x23, + 0xa0, 0x4b, 0x2d, 0x83, 0x1c, 0x04, 0x86, 0x9a, 0x2e, 0xff, 0x00, 0xeb, 0xde, 0x47, 0xa6, 0xfd, + 0x60, 0xb6, 0x8c, 0x29, 0xf6, 0x68, 0x22, 0xb8, 0xbf, 0x7b, 0x63, 0x52, 0x04, 0x74, 0x6f, 0x4c, + 0x91, 0x47, 0x13, 0xc1, 0x4d, 0xf7, 0xbf, 0x87, 0x35, 0xcf, 0x9d, 0xd0, 0x0f, 0x02, 0x8e, 0x06, + 0xa3, 0xe9, 0x87, 0x93, 0xa0, 0x0d, 0xdf, 0xc5, 0xca, 0x57, 0x2f, 0xb2, 0xe1, 0xaf, 0x5f, 0x64, + 0xc3, 0xcf, 0x5f, 0x64, 0xc3, 0x9f, 0xbd, 0xcc, 0x86, 0xbe, 0x7e, 0x99, 0x0d, 0x7d, 0xf3, 0x32, + 0x1b, 0x7a, 0xf2, 0x5e, 0x53, 0x50, 0xaf, 0x7a, 0x97, 0x79, 0x5e, 0xea, 0x14, 0x78, 0x49, 0xe9, + 0x48, 0x4a, 0x41, 0xb8, 0xe4, 0xf7, 0x9b, 0x52, 0xa1, 0xff, 0x6e, 0xa1, 0x23, 0x35, 0x7a, 0x6d, + 0xa4, 0xe8, 0x7f, 0x4e, 0x7b, 0xfb, 0xe1, 0xbe, 0xf1, 0xff, 0x34, 0x75, 0xd0, 0x45, 0xca, 0x65, + 0x02, 0xff, 0x37, 0xed, 0x9d, 0xff, 0x05, 0x00, 0x00, 0xff, 0xff, 0x39, 0xbb, 0x93, 0xd7, 0x4d, + 0x27, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -2377,6 +2386,13 @@ func (m *MsgChannelOpenTryResponse) MarshalToSizedBuffer(dAtA []byte) (int, erro _ = i var l int _ = l + if len(m.ChannelId) > 0 { + i -= len(m.ChannelId) + copy(dAtA[i:], m.ChannelId) + i = encodeVarintTx(dAtA, i, uint64(len(m.ChannelId))) + i-- + dAtA[i] = 0x12 + } if len(m.Version) > 0 { i -= len(m.Version) copy(dAtA[i:], m.Version) @@ -3818,6 +3834,10 @@ func (m *MsgChannelOpenTryResponse) Size() (n int) { if l > 0 { n += 1 + l + sovTx(uint64(l)) } + l = len(m.ChannelId) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } return n } @@ -4982,6 +5002,38 @@ func (m *MsgChannelOpenTryResponse) Unmarshal(dAtA []byte) error { } m.Version = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ChannelId", 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.ChannelId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipTx(dAtA[iNdEx:]) diff --git a/modules/core/04-channel/types/upgrade.pb.go b/modules/core/04-channel/types/upgrade.pb.go index 7d4674d801e..9057d650154 100644 --- a/modules/core/04-channel/types/upgrade.pb.go +++ b/modules/core/04-channel/types/upgrade.pb.go @@ -5,9 +5,9 @@ package types import ( fmt "fmt" - types "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - _ "github.com/gogo/protobuf/gogoproto" - proto "github.com/gogo/protobuf/proto" + _ "github.com/cosmos/gogoproto/gogoproto" + proto "github.com/cosmos/gogoproto/proto" + types "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" io "io" math "math" math_bits "math/bits" @@ -145,28 +145,28 @@ func init() { func init() { proto.RegisterFile("ibc/core/channel/v1/upgrade.proto", fileDescriptor_fb1cef68588848b2) } var fileDescriptor_fb1cef68588848b2 = []byte{ - // 334 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x54, 0x91, 0xb1, 0x4e, 0xf3, 0x30, - 0x14, 0x85, 0xe3, 0x5f, 0xfd, 0x11, 0x18, 0xa8, 0x20, 0x14, 0xa9, 0x8a, 0x20, 0x29, 0x99, 0xba, - 0xd4, 0xa6, 0xd0, 0x05, 0x26, 0x54, 0x09, 0x09, 0xd6, 0x50, 0x16, 0x16, 0x48, 0xdc, 0xab, 0xc4, - 0x52, 0x12, 0x87, 0xc4, 0x89, 0xd4, 0xb7, 0xe0, 0x85, 0xd8, 0x3b, 0x76, 0x64, 0xaa, 0x50, 0xfb, - 0x06, 0x7d, 0x02, 0x94, 0xb8, 0x4d, 0xd5, 0xc9, 0xe7, 0x1e, 0x7f, 0x3e, 0xd7, 0xba, 0x17, 0x5f, - 0x71, 0x8f, 0x51, 0x26, 0x52, 0xa0, 0x2c, 0x70, 0xe3, 0x18, 0x42, 0x5a, 0xf4, 0x69, 0x9e, 0xf8, - 0xa9, 0x3b, 0x06, 0x92, 0xa4, 0x42, 0x0a, 0xfd, 0x8c, 0x7b, 0x8c, 0x94, 0x08, 0x59, 0x23, 0xa4, - 0xe8, 0x1b, 0x2d, 0x5f, 0xf8, 0xa2, 0xba, 0xa7, 0xa5, 0x52, 0xa8, 0x61, 0x6d, 0xd3, 0x42, 0x0e, - 0xb1, 0x2c, 0xc3, 0x94, 0x52, 0x80, 0xfd, 0x8d, 0x70, 0xf3, 0x55, 0xa5, 0x8f, 0x78, 0x04, 0x22, - 0x97, 0xfa, 0x07, 0x6e, 0x4a, 0x25, 0xdf, 0x03, 0xe0, 0x7e, 0x20, 0xdb, 0xa8, 0x83, 0xba, 0x87, - 0x37, 0x06, 0xd9, 0xf6, 0x55, 0x11, 0x45, 0x9f, 0x3c, 0x55, 0xc4, 0xf0, 0x72, 0x3a, 0xb7, 0xb4, - 0xd5, 0xdc, 0x3a, 0x9f, 0xb8, 0x51, 0x78, 0x6f, 0xef, 0xbe, 0xb7, 0x9d, 0xe3, 0xb5, 0xa1, 0x68, - 0xfd, 0x19, 0x9f, 0x6e, 0x88, 0xf2, 0xcc, 0xa4, 0x1b, 0x25, 0xed, 0x7f, 0x1d, 0xd4, 0x6d, 0x0c, - 0x2f, 0x56, 0x73, 0xab, 0xbd, 0x1b, 0x52, 0x23, 0xb6, 0x73, 0xb2, 0xf6, 0x46, 0xb5, 0xf5, 0x80, - 0x8f, 0x1e, 0xd3, 0x54, 0xa4, 0x0e, 0x30, 0xe0, 0x89, 0xd4, 0x0d, 0xbc, 0x9f, 0xc1, 0x67, 0x0e, - 0x31, 0x83, 0xea, 0xdb, 0x0d, 0xa7, 0xae, 0xf5, 0x16, 0xfe, 0x0f, 0x25, 0x5b, 0xb5, 0x3a, 0x70, - 0x54, 0x31, 0x7c, 0x99, 0x2e, 0x4c, 0x34, 0x5b, 0x98, 0xe8, 0x77, 0x61, 0xa2, 0xaf, 0xa5, 0xa9, - 0xcd, 0x96, 0xa6, 0xf6, 0xb3, 0x34, 0xb5, 0xb7, 0x3b, 0x9f, 0xcb, 0x20, 0xf7, 0x08, 0x13, 0x11, - 0x65, 0x22, 0x8b, 0x44, 0x46, 0xb9, 0xc7, 0x7a, 0xbe, 0xa0, 0xc5, 0x80, 0x46, 0x62, 0x9c, 0x87, - 0x90, 0xa9, 0xe1, 0x5e, 0x0f, 0x7a, 0x9b, 0x6d, 0xc9, 0x49, 0x02, 0x99, 0xb7, 0x57, 0x4d, 0xf7, - 0xf6, 0x2f, 0x00, 0x00, 0xff, 0xff, 0x94, 0x8d, 0xab, 0x07, 0xce, 0x01, 0x00, 0x00, + // 335 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x54, 0x91, 0xc1, 0x4a, 0xf3, 0x40, + 0x14, 0x85, 0x33, 0x3f, 0xfd, 0x45, 0x47, 0x2d, 0x1a, 0x2b, 0x94, 0xa0, 0x49, 0xcd, 0xaa, 0x9b, + 0xce, 0x58, 0x15, 0x44, 0x57, 0x52, 0x10, 0x74, 0x1b, 0xeb, 0xc6, 0x8d, 0x26, 0xd3, 0x4b, 0x32, + 0x90, 0x64, 0x62, 0x32, 0x09, 0xf4, 0x2d, 0x7c, 0x21, 0xf7, 0x5d, 0x76, 0xe9, 0xaa, 0x48, 0xfb, + 0x06, 0x7d, 0x02, 0x49, 0xa6, 0x4d, 0xe9, 0x6a, 0xce, 0x3d, 0xf3, 0xcd, 0xb9, 0xc3, 0xbd, 0xf8, + 0x82, 0x7b, 0x8c, 0x32, 0x91, 0x02, 0x65, 0x81, 0x1b, 0xc7, 0x10, 0xd2, 0xa2, 0x4f, 0xf3, 0xc4, + 0x4f, 0xdd, 0x11, 0x90, 0x24, 0x15, 0x52, 0xe8, 0x27, 0xdc, 0x63, 0xa4, 0x44, 0xc8, 0x0a, 0x21, + 0x45, 0xdf, 0x68, 0xf9, 0xc2, 0x17, 0xd5, 0x3d, 0x2d, 0x95, 0x42, 0x0d, 0x6b, 0x93, 0x16, 0x72, + 0x88, 0x65, 0x19, 0xa6, 0x94, 0x02, 0xec, 0x6f, 0x84, 0x9b, 0xaf, 0x2a, 0x7d, 0xc8, 0x23, 0x10, + 0xb9, 0xd4, 0x3f, 0x70, 0x53, 0x2a, 0xf9, 0x1e, 0x00, 0xf7, 0x03, 0xd9, 0x46, 0x1d, 0xd4, 0xdd, + 0xbf, 0x32, 0xc8, 0xa6, 0xaf, 0x8a, 0x28, 0xfa, 0xe4, 0xa9, 0x22, 0x06, 0xe7, 0x93, 0x99, 0xa5, + 0x2d, 0x67, 0xd6, 0xe9, 0xd8, 0x8d, 0xc2, 0x7b, 0x7b, 0xfb, 0xbd, 0xed, 0x1c, 0xae, 0x0c, 0x45, + 0xeb, 0xcf, 0xf8, 0x78, 0x4d, 0x94, 0x67, 0x26, 0xdd, 0x28, 0x69, 0xff, 0xeb, 0xa0, 0x6e, 0x63, + 0x70, 0xb6, 0x9c, 0x59, 0xed, 0xed, 0x90, 0x1a, 0xb1, 0x9d, 0xa3, 0x95, 0x37, 0xac, 0xad, 0x07, + 0x7c, 0xf0, 0x98, 0xa6, 0x22, 0x75, 0x80, 0x01, 0x4f, 0xa4, 0x6e, 0xe0, 0xdd, 0x0c, 0x3e, 0x73, + 0x88, 0x19, 0x54, 0xdf, 0x6e, 0x38, 0x75, 0xad, 0xb7, 0xf0, 0x7f, 0x28, 0xd9, 0xaa, 0xd5, 0x9e, + 0xa3, 0x8a, 0xc1, 0xcb, 0x64, 0x6e, 0xa2, 0xe9, 0xdc, 0x44, 0xbf, 0x73, 0x13, 0x7d, 0x2d, 0x4c, + 0x6d, 0xba, 0x30, 0xb5, 0x9f, 0x85, 0xa9, 0xbd, 0xdd, 0xf9, 0x5c, 0x06, 0xb9, 0x47, 0x98, 0x88, + 0x28, 0x13, 0x59, 0x24, 0x32, 0xca, 0x3d, 0xd6, 0xf3, 0x05, 0x2d, 0x6e, 0x69, 0x24, 0x46, 0x79, + 0x08, 0x99, 0x1a, 0xee, 0xe5, 0x4d, 0x6f, 0xbd, 0x2d, 0x39, 0x4e, 0x20, 0xf3, 0x76, 0xaa, 0xe9, + 0x5e, 0xff, 0x05, 0x00, 0x00, 0xff, 0xff, 0x6f, 0x91, 0x30, 0xd4, 0xce, 0x01, 0x00, 0x00, } func (m *UpgradeTimeout) Marshal() (dAtA []byte, err error) { diff --git a/modules/core/05-port/doc.go b/modules/core/05-port/doc.go new file mode 100644 index 00000000000..0ff91c0fe47 --- /dev/null +++ b/modules/core/05-port/doc.go @@ -0,0 +1,7 @@ +/* +Package port implements the ICS 05 - Port Allocation specification +(https://github.com/cosmos/ibc/tree/main/spec/core/ics-005-port-allocation). This +concrete implementation defines types and methods with which modules can bind +to uniquely named ports allocated by the IBC handler. +*/ +package port diff --git a/modules/core/05-port/keeper/keeper.go b/modules/core/05-port/keeper/keeper.go index db65f384455..35b3f273af7 100644 --- a/modules/core/05-port/keeper/keeper.go +++ b/modules/core/05-port/keeper/keeper.go @@ -4,23 +4,23 @@ import ( "fmt" sdk "github.com/cosmos/cosmos-sdk/types" - capabilitykeeper "github.com/cosmos/cosmos-sdk/x/capability/keeper" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" "github.com/tendermint/tendermint/libs/log" - "github.com/cosmos/ibc-go/v4/modules/core/05-port/types" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" + "github.com/cosmos/ibc-go/v7/modules/core/05-port/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + "github.com/cosmos/ibc-go/v7/modules/core/exported" ) // Keeper defines the IBC connection keeper type Keeper struct { Router *types.Router - scopedKeeper capabilitykeeper.ScopedKeeper + scopedKeeper exported.ScopedKeeper } // NewKeeper creates a new IBC connection Keeper instance -func NewKeeper(sck capabilitykeeper.ScopedKeeper) Keeper { +func NewKeeper(sck exported.ScopedKeeper) Keeper { return Keeper{ scopedKeeper: sck, } @@ -28,7 +28,7 @@ func NewKeeper(sck capabilitykeeper.ScopedKeeper) Keeper { // Logger returns a module-specific logger. func (k Keeper) Logger(ctx sdk.Context) log.Logger { - return ctx.Logger().With("module", "x/"+host.ModuleName+"/"+types.SubModuleName) + return ctx.Logger().With("module", "x/"+exported.ModuleName+"/"+types.SubModuleName) } // IsBound checks a given port ID is already bounded. diff --git a/modules/core/05-port/keeper/keeper_test.go b/modules/core/05-port/keeper/keeper_test.go index fe2449b81be..c091a857474 100644 --- a/modules/core/05-port/keeper/keeper_test.go +++ b/modules/core/05-port/keeper/keeper_test.go @@ -8,8 +8,8 @@ import ( "github.com/stretchr/testify/suite" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" - "github.com/cosmos/ibc-go/v4/modules/core/05-port/keeper" - "github.com/cosmos/ibc-go/v4/testing/simapp" + "github.com/cosmos/ibc-go/v7/modules/core/05-port/keeper" + "github.com/cosmos/ibc-go/v7/testing/simapp" ) var ( diff --git a/modules/core/05-port/module.go b/modules/core/05-port/module.go index dd13e8a1b1b..9c07da041c2 100644 --- a/modules/core/05-port/module.go +++ b/modules/core/05-port/module.go @@ -1,11 +1,10 @@ package port import ( - "github.com/gogo/protobuf/grpc" "github.com/spf13/cobra" - "github.com/cosmos/ibc-go/v4/modules/core/05-port/types" - "github.com/cosmos/ibc-go/v4/modules/core/client/cli" + "github.com/cosmos/ibc-go/v7/modules/core/05-port/types" + "github.com/cosmos/ibc-go/v7/modules/core/client/cli" ) // Name returns the IBC port ICS name. @@ -17,8 +16,3 @@ func Name() string { func GetQueryCmd() *cobra.Command { return cli.GetQueryCmd() } - -// RegisterQueryService registers the gRPC query service for IBC ports. -func RegisterQueryService(server grpc.Server, queryServer types.QueryServer) { - types.RegisterQueryServer(server, queryServer) -} diff --git a/modules/core/05-port/types/errors.go b/modules/core/05-port/types/errors.go index 23a2776f59d..1348dfc83b4 100644 --- a/modules/core/05-port/types/errors.go +++ b/modules/core/05-port/types/errors.go @@ -1,13 +1,13 @@ package types import ( - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + errorsmod "cosmossdk.io/errors" ) // IBC port sentinel errors var ( - ErrPortExists = sdkerrors.Register(SubModuleName, 2, "port is already binded") - ErrPortNotFound = sdkerrors.Register(SubModuleName, 3, "port not found") - ErrInvalidPort = sdkerrors.Register(SubModuleName, 4, "invalid port") - ErrInvalidRoute = sdkerrors.Register(SubModuleName, 5, "route not found") + ErrPortExists = errorsmod.Register(SubModuleName, 2, "port is already binded") + ErrPortNotFound = errorsmod.Register(SubModuleName, 3, "port not found") + ErrInvalidPort = errorsmod.Register(SubModuleName, 4, "invalid port") + ErrInvalidRoute = errorsmod.Register(SubModuleName, 5, "route not found") ) diff --git a/modules/core/05-port/types/module.go b/modules/core/05-port/types/module.go index 3f71e1d0339..9073a5778c1 100644 --- a/modules/core/05-port/types/module.go +++ b/modules/core/05-port/types/module.go @@ -4,8 +4,9 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" - "github.com/cosmos/ibc-go/v4/modules/core/exported" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + "github.com/cosmos/ibc-go/v7/modules/core/exported" ) // IBCModule defines an interface that implements all the callbacks @@ -110,8 +111,12 @@ type ICS4Wrapper interface { SendPacket( ctx sdk.Context, chanCap *capabilitytypes.Capability, - packet exported.PacketI, - ) error + sourcePort string, + sourceChannel string, + timeoutHeight clienttypes.Height, + timeoutTimestamp uint64, + data []byte, + ) (sequence uint64, err error) WriteAcknowledgement( ctx sdk.Context, diff --git a/modules/core/05-port/types/query.go b/modules/core/05-port/types/query.go deleted file mode 100644 index 7da3fe678af..00000000000 --- a/modules/core/05-port/types/query.go +++ /dev/null @@ -1,9 +0,0 @@ -package types - -// NewQueryAppVersionResponse creates a new QueryAppVersionResponse instance -func NewQueryAppVersionResponse(portID, version string) *QueryAppVersionResponse { - return &QueryAppVersionResponse{ - PortId: portID, - Version: version, - } -} diff --git a/modules/core/05-port/types/query.pb.go b/modules/core/05-port/types/query.pb.go deleted file mode 100644 index 3d7b414c747..00000000000 --- a/modules/core/05-port/types/query.pb.go +++ /dev/null @@ -1,843 +0,0 @@ -// Code generated by protoc-gen-gogo. DO NOT EDIT. -// source: ibc/core/port/v1/query.proto - -package types - -import ( - context "context" - fmt "fmt" - types "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" - grpc1 "github.com/gogo/protobuf/grpc" - proto "github.com/gogo/protobuf/proto" - grpc "google.golang.org/grpc" - codes "google.golang.org/grpc/codes" - status "google.golang.org/grpc/status" - io "io" - math "math" - math_bits "math/bits" -) - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package - -// QueryAppVersionRequest is the request type for the Query/AppVersion RPC method -type QueryAppVersionRequest struct { - // port unique identifier - PortId string `protobuf:"bytes,1,opt,name=port_id,json=portId,proto3" json:"port_id,omitempty"` - // connection unique identifier - ConnectionId string `protobuf:"bytes,2,opt,name=connection_id,json=connectionId,proto3" json:"connection_id,omitempty"` - // whether the channel is ordered or unordered - Ordering types.Order `protobuf:"varint,3,opt,name=ordering,proto3,enum=ibc.core.channel.v1.Order" json:"ordering,omitempty"` - // counterparty channel end - Counterparty *types.Counterparty `protobuf:"bytes,4,opt,name=counterparty,proto3" json:"counterparty,omitempty"` - // proposed version - ProposedVersion string `protobuf:"bytes,5,opt,name=proposed_version,json=proposedVersion,proto3" json:"proposed_version,omitempty"` -} - -func (m *QueryAppVersionRequest) Reset() { *m = QueryAppVersionRequest{} } -func (m *QueryAppVersionRequest) String() string { return proto.CompactTextString(m) } -func (*QueryAppVersionRequest) ProtoMessage() {} -func (*QueryAppVersionRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_9a256596009a8334, []int{0} -} -func (m *QueryAppVersionRequest) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *QueryAppVersionRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_QueryAppVersionRequest.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 *QueryAppVersionRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_QueryAppVersionRequest.Merge(m, src) -} -func (m *QueryAppVersionRequest) XXX_Size() int { - return m.Size() -} -func (m *QueryAppVersionRequest) XXX_DiscardUnknown() { - xxx_messageInfo_QueryAppVersionRequest.DiscardUnknown(m) -} - -var xxx_messageInfo_QueryAppVersionRequest proto.InternalMessageInfo - -func (m *QueryAppVersionRequest) GetPortId() string { - if m != nil { - return m.PortId - } - return "" -} - -func (m *QueryAppVersionRequest) GetConnectionId() string { - if m != nil { - return m.ConnectionId - } - return "" -} - -func (m *QueryAppVersionRequest) GetOrdering() types.Order { - if m != nil { - return m.Ordering - } - return types.NONE -} - -func (m *QueryAppVersionRequest) GetCounterparty() *types.Counterparty { - if m != nil { - return m.Counterparty - } - return nil -} - -func (m *QueryAppVersionRequest) GetProposedVersion() string { - if m != nil { - return m.ProposedVersion - } - return "" -} - -// QueryAppVersionResponse is the response type for the Query/AppVersion RPC method. -type QueryAppVersionResponse struct { - // port id associated with the request identifiers - PortId string `protobuf:"bytes,1,opt,name=port_id,json=portId,proto3" json:"port_id,omitempty"` - // supported app version - Version string `protobuf:"bytes,2,opt,name=version,proto3" json:"version,omitempty"` -} - -func (m *QueryAppVersionResponse) Reset() { *m = QueryAppVersionResponse{} } -func (m *QueryAppVersionResponse) String() string { return proto.CompactTextString(m) } -func (*QueryAppVersionResponse) ProtoMessage() {} -func (*QueryAppVersionResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_9a256596009a8334, []int{1} -} -func (m *QueryAppVersionResponse) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *QueryAppVersionResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_QueryAppVersionResponse.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 *QueryAppVersionResponse) XXX_Merge(src proto.Message) { - xxx_messageInfo_QueryAppVersionResponse.Merge(m, src) -} -func (m *QueryAppVersionResponse) XXX_Size() int { - return m.Size() -} -func (m *QueryAppVersionResponse) XXX_DiscardUnknown() { - xxx_messageInfo_QueryAppVersionResponse.DiscardUnknown(m) -} - -var xxx_messageInfo_QueryAppVersionResponse proto.InternalMessageInfo - -func (m *QueryAppVersionResponse) GetPortId() string { - if m != nil { - return m.PortId - } - return "" -} - -func (m *QueryAppVersionResponse) GetVersion() string { - if m != nil { - return m.Version - } - return "" -} - -func init() { - proto.RegisterType((*QueryAppVersionRequest)(nil), "ibc.core.port.v1.QueryAppVersionRequest") - proto.RegisterType((*QueryAppVersionResponse)(nil), "ibc.core.port.v1.QueryAppVersionResponse") -} - -func init() { proto.RegisterFile("ibc/core/port/v1/query.proto", fileDescriptor_9a256596009a8334) } - -var fileDescriptor_9a256596009a8334 = []byte{ - // 371 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x52, 0xcd, 0xee, 0x12, 0x31, - 0x10, 0xdf, 0x45, 0x01, 0xad, 0xa8, 0xa4, 0x07, 0xd9, 0x10, 0xb3, 0x01, 0xbc, 0x2c, 0x07, 0x5a, - 0x81, 0xc8, 0x5d, 0x8d, 0x07, 0x12, 0x13, 0x75, 0x0f, 0x1e, 0xbc, 0x10, 0xb6, 0xdb, 0x40, 0x13, - 0xe8, 0x94, 0xb6, 0xbb, 0x09, 0x37, 0x1f, 0xc1, 0xc7, 0xf2, 0xc8, 0xd1, 0xa3, 0x81, 0x17, 0x31, - 0xdd, 0x65, 0xc1, 0x0f, 0x4c, 0xfe, 0xb7, 0xce, 0xfc, 0x3e, 0xf2, 0x9b, 0xe9, 0xa0, 0xe7, 0x22, - 0x61, 0x94, 0x81, 0xe6, 0x54, 0x81, 0xb6, 0x34, 0x1f, 0xd3, 0x5d, 0xc6, 0xf5, 0x9e, 0x28, 0x0d, - 0x16, 0x70, 0x5b, 0x24, 0x8c, 0x38, 0x94, 0x38, 0x94, 0xe4, 0xe3, 0x6e, 0xff, 0xc2, 0x67, 0xeb, - 0xa5, 0x94, 0x7c, 0xe3, 0x24, 0xe7, 0x67, 0x29, 0x1a, 0x7c, 0xad, 0xa1, 0x67, 0x9f, 0x9c, 0xc9, - 0x6b, 0xa5, 0x3e, 0x73, 0x6d, 0x04, 0xc8, 0x98, 0xef, 0x32, 0x6e, 0x2c, 0xee, 0xa0, 0xa6, 0x33, - 0x5a, 0x88, 0x34, 0xf0, 0x7b, 0x7e, 0xf4, 0x30, 0x6e, 0xb8, 0x72, 0x9e, 0xe2, 0x17, 0xe8, 0x31, - 0x03, 0x29, 0x39, 0xb3, 0x02, 0xa4, 0x83, 0x6b, 0x05, 0xdc, 0xba, 0x36, 0xe7, 0x29, 0x9e, 0xa1, - 0x07, 0xa0, 0x53, 0xae, 0x85, 0x5c, 0x05, 0xf7, 0x7a, 0x7e, 0xf4, 0x64, 0xd2, 0x25, 0x97, 0x80, - 0x55, 0x86, 0x7c, 0x4c, 0x3e, 0x38, 0x52, 0x7c, 0xe1, 0xe2, 0x77, 0xa8, 0xc5, 0x20, 0x93, 0x96, - 0x6b, 0xb5, 0xd4, 0x76, 0x1f, 0xdc, 0xef, 0xf9, 0xd1, 0xa3, 0x49, 0xff, 0xa6, 0xf6, 0xed, 0x6f, - 0xc4, 0xf8, 0x0f, 0x19, 0x1e, 0xa2, 0xb6, 0xd2, 0xa0, 0xc0, 0xf0, 0x74, 0x91, 0x97, 0x73, 0x05, - 0xf5, 0x22, 0xe6, 0xd3, 0xaa, 0x7f, 0x1e, 0x77, 0xf0, 0x1e, 0x75, 0xfe, 0xd9, 0x80, 0x51, 0x20, - 0x0d, 0xff, 0xff, 0x0a, 0x02, 0xd4, 0xac, 0x5c, 0xcb, 0xe1, 0xab, 0x72, 0xb2, 0x41, 0xf5, 0xc2, - 0x0d, 0x33, 0x84, 0xae, 0x8e, 0x38, 0x22, 0x7f, 0xff, 0x0e, 0xb9, 0xbd, 0xf6, 0xee, 0xf0, 0x0e, - 0xcc, 0x32, 0xde, 0xc0, 0x7b, 0xf3, 0xf1, 0xfb, 0x31, 0xf4, 0x0f, 0xc7, 0xd0, 0xff, 0x79, 0x0c, - 0xfd, 0x6f, 0xa7, 0xd0, 0x3b, 0x9c, 0x42, 0xef, 0xc7, 0x29, 0xf4, 0xbe, 0xcc, 0x56, 0xc2, 0xae, - 0xb3, 0x84, 0x30, 0xd8, 0x52, 0x06, 0x66, 0x0b, 0x86, 0x8a, 0x84, 0x8d, 0x56, 0x40, 0xf3, 0x29, - 0xdd, 0x42, 0x9a, 0x6d, 0xb8, 0x29, 0x6f, 0xe3, 0xe5, 0xab, 0x51, 0x71, 0x4e, 0x76, 0xaf, 0xb8, - 0x49, 0x1a, 0xc5, 0x5d, 0x4c, 0x7f, 0x05, 0x00, 0x00, 0xff, 0xff, 0x19, 0x37, 0x7f, 0x1d, 0x6c, - 0x02, 0x00, 0x00, -} - -// Reference imports to suppress errors if they are not otherwise used. -var _ context.Context -var _ grpc.ClientConn - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the grpc package it is being compiled against. -const _ = grpc.SupportPackageIsVersion4 - -// QueryClient is the client API for Query service. -// -// 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 { - // AppVersion queries an IBC Port and determines the appropriate application version to be used - AppVersion(ctx context.Context, in *QueryAppVersionRequest, opts ...grpc.CallOption) (*QueryAppVersionResponse, error) -} - -type queryClient struct { - cc grpc1.ClientConn -} - -func NewQueryClient(cc grpc1.ClientConn) QueryClient { - return &queryClient{cc} -} - -func (c *queryClient) AppVersion(ctx context.Context, in *QueryAppVersionRequest, opts ...grpc.CallOption) (*QueryAppVersionResponse, error) { - out := new(QueryAppVersionResponse) - err := c.cc.Invoke(ctx, "/ibc.core.port.v1.Query/AppVersion", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -// QueryServer is the server API for Query service. -type QueryServer interface { - // AppVersion queries an IBC Port and determines the appropriate application version to be used - AppVersion(context.Context, *QueryAppVersionRequest) (*QueryAppVersionResponse, error) -} - -// UnimplementedQueryServer can be embedded to have forward compatible implementations. -type UnimplementedQueryServer struct { -} - -func (*UnimplementedQueryServer) AppVersion(ctx context.Context, req *QueryAppVersionRequest) (*QueryAppVersionResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method AppVersion not implemented") -} - -func RegisterQueryServer(s grpc1.Server, srv QueryServer) { - s.RegisterService(&_Query_serviceDesc, srv) -} - -func _Query_AppVersion_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(QueryAppVersionRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(QueryServer).AppVersion(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/ibc.core.port.v1.Query/AppVersion", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(QueryServer).AppVersion(ctx, req.(*QueryAppVersionRequest)) - } - return interceptor(ctx, in, info, handler) -} - -var _Query_serviceDesc = grpc.ServiceDesc{ - ServiceName: "ibc.core.port.v1.Query", - HandlerType: (*QueryServer)(nil), - Methods: []grpc.MethodDesc{ - { - MethodName: "AppVersion", - Handler: _Query_AppVersion_Handler, - }, - }, - Streams: []grpc.StreamDesc{}, - Metadata: "ibc/core/port/v1/query.proto", -} - -func (m *QueryAppVersionRequest) 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 *QueryAppVersionRequest) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *QueryAppVersionRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if len(m.ProposedVersion) > 0 { - i -= len(m.ProposedVersion) - copy(dAtA[i:], m.ProposedVersion) - i = encodeVarintQuery(dAtA, i, uint64(len(m.ProposedVersion))) - i-- - dAtA[i] = 0x2a - } - if m.Counterparty != nil { - { - size, err := m.Counterparty.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintQuery(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x22 - } - if m.Ordering != 0 { - i = encodeVarintQuery(dAtA, i, uint64(m.Ordering)) - i-- - dAtA[i] = 0x18 - } - 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.PortId) > 0 { - i -= len(m.PortId) - copy(dAtA[i:], m.PortId) - i = encodeVarintQuery(dAtA, i, uint64(len(m.PortId))) - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - -func (m *QueryAppVersionResponse) 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 *QueryAppVersionResponse) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *QueryAppVersionResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if len(m.Version) > 0 { - i -= len(m.Version) - copy(dAtA[i:], m.Version) - i = encodeVarintQuery(dAtA, i, uint64(len(m.Version))) - i-- - dAtA[i] = 0x12 - } - if len(m.PortId) > 0 { - i -= len(m.PortId) - copy(dAtA[i:], m.PortId) - i = encodeVarintQuery(dAtA, i, uint64(len(m.PortId))) - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - -func encodeVarintQuery(dAtA []byte, offset int, v uint64) int { - offset -= sovQuery(v) - base := offset - for v >= 1<<7 { - dAtA[offset] = uint8(v&0x7f | 0x80) - v >>= 7 - offset++ - } - dAtA[offset] = uint8(v) - return base -} -func (m *QueryAppVersionRequest) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.PortId) - if l > 0 { - n += 1 + l + sovQuery(uint64(l)) - } - l = len(m.ConnectionId) - if l > 0 { - n += 1 + l + sovQuery(uint64(l)) - } - if m.Ordering != 0 { - n += 1 + sovQuery(uint64(m.Ordering)) - } - if m.Counterparty != nil { - l = m.Counterparty.Size() - n += 1 + l + sovQuery(uint64(l)) - } - l = len(m.ProposedVersion) - if l > 0 { - n += 1 + l + sovQuery(uint64(l)) - } - return n -} - -func (m *QueryAppVersionResponse) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.PortId) - if l > 0 { - n += 1 + l + sovQuery(uint64(l)) - } - l = len(m.Version) - if l > 0 { - n += 1 + l + sovQuery(uint64(l)) - } - return n -} - -func sovQuery(x uint64) (n int) { - return (math_bits.Len64(x|1) + 6) / 7 -} -func sozQuery(x uint64) (n int) { - return sovQuery(uint64((x << 1) ^ uint64((int64(x) >> 63)))) -} -func (m *QueryAppVersionRequest) 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: QueryAppVersionRequest: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: QueryAppVersionRequest: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field PortId", 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.PortId = 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 - case 3: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Ordering", wireType) - } - m.Ordering = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowQuery - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Ordering |= types.Order(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 4: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Counterparty", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowQuery - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthQuery - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthQuery - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.Counterparty == nil { - m.Counterparty = &types.Counterparty{} - } - if err := m.Counterparty.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 5: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ProposedVersion", 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.ProposedVersion = 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 *QueryAppVersionResponse) 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: QueryAppVersionResponse: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: QueryAppVersionResponse: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field PortId", 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.PortId = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Version", 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.Version = 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 skipQuery(dAtA []byte) (n int, err error) { - l := len(dAtA) - iNdEx := 0 - depth := 0 - for iNdEx < l { - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowQuery - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - wireType := int(wire & 0x7) - switch wireType { - case 0: - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowQuery - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - iNdEx++ - if dAtA[iNdEx-1] < 0x80 { - break - } - } - case 1: - iNdEx += 8 - case 2: - var length int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowQuery - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - length |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - if length < 0 { - return 0, ErrInvalidLengthQuery - } - iNdEx += length - case 3: - depth++ - case 4: - if depth == 0 { - return 0, ErrUnexpectedEndOfGroupQuery - } - depth-- - case 5: - iNdEx += 4 - default: - return 0, fmt.Errorf("proto: illegal wireType %d", wireType) - } - if iNdEx < 0 { - return 0, ErrInvalidLengthQuery - } - if depth == 0 { - return iNdEx, nil - } - } - return 0, io.ErrUnexpectedEOF -} - -var ( - ErrInvalidLengthQuery = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowQuery = fmt.Errorf("proto: integer overflow") - ErrUnexpectedEndOfGroupQuery = fmt.Errorf("proto: unexpected end of group") -) diff --git a/modules/core/23-commitment/types/codec.go b/modules/core/23-commitment/types/codec.go index 5c1334cec6d..3ccf9164d60 100644 --- a/modules/core/23-commitment/types/codec.go +++ b/modules/core/23-commitment/types/codec.go @@ -3,7 +3,7 @@ package types import ( codectypes "github.com/cosmos/cosmos-sdk/codec/types" - "github.com/cosmos/ibc-go/v4/modules/core/exported" + "github.com/cosmos/ibc-go/v7/modules/core/exported" ) // RegisterInterfaces registers the commitment interfaces to protobuf Any. diff --git a/modules/core/23-commitment/types/commitment.pb.go b/modules/core/23-commitment/types/commitment.pb.go index 8b77f6aa693..190710ffbe1 100644 --- a/modules/core/23-commitment/types/commitment.pb.go +++ b/modules/core/23-commitment/types/commitment.pb.go @@ -5,9 +5,9 @@ package types import ( fmt "fmt" - _go "github.com/confio/ics23/go" - _ "github.com/gogo/protobuf/gogoproto" - proto "github.com/gogo/protobuf/proto" + _ "github.com/cosmos/gogoproto/gogoproto" + proto "github.com/cosmos/gogoproto/proto" + _go "github.com/cosmos/ics23/go" io "io" math "math" math_bits "math/bits" @@ -217,28 +217,29 @@ func init() { } var fileDescriptor_7921d88972a41469 = []byte{ - // 333 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x4c, 0x91, 0xbd, 0x4e, 0xc3, 0x30, - 0x10, 0xc7, 0x13, 0x51, 0x15, 0xea, 0x56, 0x42, 0x04, 0xa8, 0x50, 0x87, 0x14, 0x65, 0x80, 0x2e, - 0xb5, 0xd5, 0x8f, 0xa9, 0x82, 0x25, 0xb0, 0x22, 0x55, 0x19, 0x18, 0x58, 0x50, 0x62, 0xdc, 0xc4, - 0x6a, 0xc3, 0x45, 0xb1, 0x5b, 0x91, 0x37, 0x60, 0x64, 0x64, 0xe4, 0x71, 0x18, 0x3b, 0x32, 0x55, - 0xa8, 0x7d, 0x83, 0x3e, 0x01, 0xb2, 0x4d, 0x21, 0xdb, 0xfd, 0x75, 0xbf, 0xfb, 0xfa, 0x1f, 0xba, - 0xe4, 0x11, 0x25, 0x14, 0x72, 0x46, 0x28, 0xa4, 0x29, 0x97, 0x29, 0x7b, 0x96, 0x64, 0xd1, 0x2b, - 0x29, 0x9c, 0xe5, 0x20, 0xc1, 0x69, 0xf2, 0x88, 0x62, 0x05, 0xe2, 0x52, 0x6a, 0xd1, 0x6b, 0x9d, - 0xc4, 0x10, 0x83, 0x46, 0x88, 0x8a, 0x0c, 0xdd, 0x6a, 0x64, 0x39, 0xc0, 0x44, 0x18, 0xe5, 0x5d, - 0x20, 0x74, 0xc7, 0xf2, 0xe9, 0x8c, 0x05, 0x00, 0xd2, 0x71, 0x50, 0x25, 0x09, 0x45, 0x72, 0x66, - 0x9f, 0xdb, 0x9d, 0x46, 0xa0, 0xe3, 0x51, 0xe5, 0xf5, 0xa3, 0x6d, 0x79, 0xb7, 0xa8, 0x61, 0xb8, - 0x71, 0xce, 0x26, 0xfc, 0xc5, 0x19, 0x22, 0x34, 0x65, 0xc5, 0x63, 0xa6, 0x95, 0xe1, 0xfd, 0xd3, - 0xed, 0xaa, 0x7d, 0x54, 0x84, 0xe9, 0x6c, 0xe4, 0xfd, 0xe7, 0xbc, 0xa0, 0x36, 0x65, 0x85, 0xa9, - 0xf2, 0xfc, 0xdd, 0xb4, 0x71, 0x28, 0x13, 0x07, 0xa3, 0x03, 0xcd, 0x85, 0x52, 0x4d, 0xdc, 0xeb, - 0xd4, 0xfc, 0xe3, 0xed, 0xaa, 0x7d, 0x58, 0xea, 0x10, 0xca, 0xc4, 0x0b, 0xf6, 0x55, 0x7d, 0x28, - 0x93, 0x51, 0xe5, 0x5d, 0x6d, 0x72, 0x8d, 0xea, 0xbb, 0x4d, 0x00, 0x26, 0x0e, 0x46, 0x55, 0x73, - 0x90, 0x6e, 0x51, 0xef, 0x37, 0x31, 0xa7, 0xa2, 0x3f, 0xc0, 0x37, 0x7f, 0x56, 0x68, 0x2e, 0xf8, - 0xa5, 0xfc, 0xfb, 0xcf, 0xb5, 0x6b, 0x2f, 0xd7, 0xae, 0xfd, 0xbd, 0x76, 0xed, 0xb7, 0x8d, 0x6b, - 0x2d, 0x37, 0xae, 0xf5, 0xb5, 0x71, 0xad, 0x87, 0xab, 0x98, 0xcb, 0x64, 0x1e, 0x29, 0x13, 0x09, - 0x05, 0x91, 0x82, 0x20, 0x3c, 0xa2, 0xdd, 0x18, 0xc8, 0x62, 0x48, 0x52, 0x78, 0x9a, 0xcf, 0x98, - 0x30, 0xff, 0xe8, 0x0f, 0xba, 0xa5, 0x97, 0xc8, 0x22, 0x63, 0x22, 0xaa, 0x6a, 0x3f, 0x07, 0x3f, - 0x01, 0x00, 0x00, 0xff, 0xff, 0xe4, 0x08, 0x76, 0x1b, 0xb6, 0x01, 0x00, 0x00, + // 346 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x4c, 0x91, 0xcd, 0x4a, 0xc3, 0x40, + 0x10, 0xc7, 0x13, 0x2c, 0xd5, 0x6e, 0x0b, 0x62, 0xfc, 0x40, 0x8a, 0xa4, 0x25, 0x07, 0xed, 0xa5, + 0xbb, 0xb4, 0x15, 0x94, 0xe2, 0x29, 0x0a, 0x9e, 0x84, 0x92, 0x83, 0x07, 0x2f, 0x92, 0xac, 0xdb, + 0x24, 0xb4, 0x71, 0x42, 0x76, 0x1b, 0xcc, 0x1b, 0x78, 0xf4, 0xe8, 0xd1, 0xc7, 0xf1, 0xd8, 0xa3, + 0xa7, 0x22, 0xed, 0x1b, 0xf4, 0x09, 0x64, 0x77, 0xad, 0xe6, 0x36, 0xc3, 0xfc, 0xe6, 0xe3, 0xff, + 0x1f, 0x74, 0x16, 0x07, 0x94, 0x50, 0xc8, 0x18, 0xa1, 0x90, 0x24, 0xb1, 0x48, 0xd8, 0xb3, 0x20, + 0x79, 0xaf, 0x94, 0xe1, 0x34, 0x03, 0x01, 0xd6, 0x51, 0x1c, 0x50, 0x2c, 0x41, 0x5c, 0x2a, 0xe5, + 0xbd, 0xe6, 0x41, 0x08, 0x21, 0x28, 0x84, 0xc8, 0x48, 0xd3, 0xcd, 0x13, 0x0a, 0x3c, 0x01, 0x4e, + 0x62, 0xca, 0xfb, 0x03, 0x39, 0x2f, 0xcd, 0x00, 0xc6, 0x5c, 0x57, 0x9d, 0x53, 0x84, 0xee, 0x58, + 0x36, 0x99, 0x32, 0x0f, 0x40, 0x58, 0x16, 0xaa, 0x44, 0x3e, 0x8f, 0x8e, 0xcd, 0xb6, 0xd9, 0x69, + 0x78, 0x2a, 0x1e, 0x56, 0x5e, 0x3f, 0x5a, 0x86, 0x73, 0x83, 0x1a, 0x9a, 0x1b, 0x65, 0x6c, 0x1c, + 0xbf, 0x58, 0xe7, 0x08, 0x4d, 0x58, 0xf1, 0x98, 0xaa, 0x4c, 0xf3, 0xee, 0xe1, 0x7a, 0xd1, 0xda, + 0x2b, 0xfc, 0x64, 0x3a, 0x74, 0xfe, 0x6b, 0x8e, 0x57, 0x9b, 0xb0, 0x42, 0x77, 0x39, 0xee, 0x66, + 0xdb, 0xc8, 0x17, 0x91, 0x85, 0xd1, 0x8e, 0xe2, 0x7c, 0x21, 0x37, 0x6e, 0x75, 0x6a, 0xee, 0xfe, + 0x7a, 0xd1, 0xda, 0x2d, 0x4d, 0xf0, 0x45, 0xe4, 0x78, 0xdb, 0xb2, 0xdf, 0x17, 0xd1, 0xb0, 0xf2, + 0x2e, 0x2f, 0xb9, 0x45, 0xf5, 0xcd, 0x25, 0x00, 0x63, 0xeb, 0x12, 0x55, 0xb5, 0x20, 0x35, 0xa2, + 0xde, 0x6f, 0x63, 0xad, 0x17, 0x2b, 0xbd, 0x38, 0xef, 0xe1, 0xeb, 0x3f, 0x93, 0x54, 0x87, 0xf7, + 0xcb, 0xbb, 0xf7, 0x9f, 0x4b, 0xdb, 0x9c, 0x2f, 0x6d, 0xf3, 0x7b, 0x69, 0x9b, 0x6f, 0x2b, 0xdb, + 0x98, 0xaf, 0x6c, 0xe3, 0x6b, 0x65, 0x1b, 0x0f, 0x57, 0x61, 0x2c, 0xa2, 0x59, 0x20, 0xed, 0x25, + 0x1b, 0xf7, 0x02, 0xda, 0x0d, 0x81, 0xe4, 0x17, 0x24, 0x81, 0xa7, 0xd9, 0x94, 0x71, 0xfd, 0xa9, + 0xfe, 0xa0, 0x5b, 0x7a, 0x96, 0x28, 0x52, 0xc6, 0x83, 0xaa, 0x72, 0x76, 0xf0, 0x13, 0x00, 0x00, + 0xff, 0xff, 0x90, 0xe5, 0x86, 0x50, 0xd0, 0x01, 0x00, 0x00, } func (m *MerkleRoot) Marshal() (dAtA []byte, err error) { diff --git a/modules/core/23-commitment/types/commitment_test.go b/modules/core/23-commitment/types/commitment_test.go index d1a32b9cae8..ccc794579f8 100644 --- a/modules/core/23-commitment/types/commitment_test.go +++ b/modules/core/23-commitment/types/commitment_test.go @@ -7,6 +7,7 @@ import ( "github.com/cosmos/cosmos-sdk/store/rootmulti" storetypes "github.com/cosmos/cosmos-sdk/store/types" "github.com/stretchr/testify/suite" + "github.com/tendermint/tendermint/libs/log" dbm "github.com/tendermint/tm-db" ) @@ -20,12 +21,14 @@ type MerkleTestSuite struct { func (suite *MerkleTestSuite) SetupTest() { db := dbm.NewMemDB() - suite.store = rootmulti.NewStore(db) + dblog := log.TestingLogger() + suite.store = rootmulti.NewStore(db, dblog) suite.storeKey = storetypes.NewKVStoreKey("iavlStoreKey") suite.store.MountStoreWithDB(suite.storeKey, storetypes.StoreTypeIAVL, nil) - suite.store.LoadVersion(0) + err := suite.store.LoadVersion(0) + suite.Require().NoError(err) suite.iavlStore = suite.store.GetCommitStore(suite.storeKey).(*iavl.Store) } diff --git a/modules/core/23-commitment/types/errors.go b/modules/core/23-commitment/types/errors.go index 7191baef1cc..140ec912b00 100644 --- a/modules/core/23-commitment/types/errors.go +++ b/modules/core/23-commitment/types/errors.go @@ -1,7 +1,7 @@ package types import ( - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + errorsmod "cosmossdk.io/errors" ) // SubModuleName is the error codespace @@ -9,7 +9,7 @@ const SubModuleName string = "commitment" // IBC connection sentinel errors var ( - ErrInvalidProof = sdkerrors.Register(SubModuleName, 2, "invalid proof") - ErrInvalidPrefix = sdkerrors.Register(SubModuleName, 3, "invalid prefix") - ErrInvalidMerkleProof = sdkerrors.Register(SubModuleName, 4, "invalid merkle proof") + ErrInvalidProof = errorsmod.Register(SubModuleName, 2, "invalid proof") + ErrInvalidPrefix = errorsmod.Register(SubModuleName, 3, "invalid prefix") + ErrInvalidMerkleProof = errorsmod.Register(SubModuleName, 4, "invalid merkle proof") ) diff --git a/modules/core/23-commitment/types/merkle.go b/modules/core/23-commitment/types/merkle.go index b661855935f..7d15b00a318 100644 --- a/modules/core/23-commitment/types/merkle.go +++ b/modules/core/23-commitment/types/merkle.go @@ -5,12 +5,12 @@ import ( "fmt" "net/url" - ics23 "github.com/confio/ics23/go" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/gogo/protobuf/proto" + errorsmod "cosmossdk.io/errors" + "github.com/cosmos/gogoproto/proto" + ics23 "github.com/cosmos/ics23/go" tmcrypto "github.com/tendermint/tendermint/proto/tendermint/crypto" - "github.com/cosmos/ibc-go/v4/modules/core/exported" + "github.com/cosmos/ibc-go/v7/modules/core/exported" ) // var representing the proofspecs for a SDK chain @@ -121,7 +121,7 @@ func (mp MerklePath) Empty() bool { // with the given path. func ApplyPrefix(prefix exported.Prefix, path MerklePath) (MerklePath, error) { if prefix == nil || prefix.Empty() { - return MerklePath{}, sdkerrors.Wrap(ErrInvalidPrefix, "prefix can't be empty") + return MerklePath{}, errorsmod.Wrap(ErrInvalidPrefix, "prefix can't be empty") } return NewMerklePath(append([]string{string(prefix.Bytes())}, path.KeyPath...)...), nil } @@ -129,6 +129,7 @@ func ApplyPrefix(prefix exported.Prefix, path MerklePath) (MerklePath, error) { var _ exported.Proof = (*MerkleProof)(nil) // VerifyMembership verifies the membership of a merkle proof against the given root, path, and value. +// Note that the path is expected as []string{, }. func (proof MerkleProof) VerifyMembership(specs []*ics23.ProofSpec, root exported.Root, path exported.Path, value []byte) error { if err := proof.validateVerificationArgs(specs, root); err != nil { return err @@ -137,14 +138,14 @@ func (proof MerkleProof) VerifyMembership(specs []*ics23.ProofSpec, root exporte // VerifyMembership specific argument validation mpath, ok := path.(MerklePath) if !ok { - return sdkerrors.Wrapf(ErrInvalidProof, "path %v is not of type MerklePath", path) + return errorsmod.Wrapf(ErrInvalidProof, "path %v is not of type MerklePath", path) } if len(mpath.KeyPath) != len(specs) { - return sdkerrors.Wrapf(ErrInvalidProof, "path length %d not same as proof %d", + return errorsmod.Wrapf(ErrInvalidProof, "path length %d not same as proof %d", len(mpath.KeyPath), len(specs)) } if len(value) == 0 { - return sdkerrors.Wrap(ErrInvalidProof, "empty value in membership proof") + return errorsmod.Wrap(ErrInvalidProof, "empty value in membership proof") } // Since every proof in chain is a membership proof we can use verifyChainedMembershipProof from index 0 @@ -166,10 +167,10 @@ func (proof MerkleProof) VerifyNonMembership(specs []*ics23.ProofSpec, root expo // VerifyNonMembership specific argument validation mpath, ok := path.(MerklePath) if !ok { - return sdkerrors.Wrapf(ErrInvalidProof, "path %v is not of type MerkleProof", path) + return errorsmod.Wrapf(ErrInvalidProof, "path %v is not of type MerkleProof", path) } if len(mpath.KeyPath) != len(specs) { - return sdkerrors.Wrapf(ErrInvalidProof, "path length %d not same as proof %d", + return errorsmod.Wrapf(ErrInvalidProof, "path length %d not same as proof %d", len(mpath.KeyPath), len(specs)) } @@ -179,14 +180,14 @@ func (proof MerkleProof) VerifyNonMembership(specs []*ics23.ProofSpec, root expo // of all subroots up to final root subroot, err := proof.Proofs[0].Calculate() if err != nil { - return sdkerrors.Wrapf(ErrInvalidProof, "could not calculate root for proof index 0, merkle tree is likely empty. %v", err) + return errorsmod.Wrapf(ErrInvalidProof, "could not calculate root for proof index 0, merkle tree is likely empty. %v", err) } key, err := mpath.GetKey(uint64(len(mpath.KeyPath) - 1)) if err != nil { - return sdkerrors.Wrapf(ErrInvalidProof, "could not retrieve key bytes for key: %s", mpath.KeyPath[len(mpath.KeyPath)-1]) + return errorsmod.Wrapf(ErrInvalidProof, "could not retrieve key bytes for key: %s", mpath.KeyPath[len(mpath.KeyPath)-1]) } if ok := ics23.VerifyNonMembership(specs[0], subroot, proof.Proofs[0], key); !ok { - return sdkerrors.Wrapf(ErrInvalidProof, "could not verify absence of key %s. Please ensure that the path is correct.", string(key)) + return errorsmod.Wrapf(ErrInvalidProof, "could not verify absence of key %s. Please ensure that the path is correct.", string(key)) } // Verify chained membership proof starting from index 1 with value = subroot @@ -194,10 +195,10 @@ func (proof MerkleProof) VerifyNonMembership(specs []*ics23.ProofSpec, root expo return err } case *ics23.CommitmentProof_Exist: - return sdkerrors.Wrapf(ErrInvalidProof, + return errorsmod.Wrapf(ErrInvalidProof, "got ExistenceProof in VerifyNonMembership. If this is unexpected, please ensure that proof was queried with the correct key.") default: - return sdkerrors.Wrapf(ErrInvalidProof, + return errorsmod.Wrapf(ErrInvalidProof, "expected proof type: %T, got: %T", &ics23.CommitmentProof_Exist{}, proof.Proofs[0].Proof) } return nil @@ -206,13 +207,13 @@ func (proof MerkleProof) VerifyNonMembership(specs []*ics23.ProofSpec, root expo // BatchVerifyMembership verifies a group of key value pairs against the given root // NOTE: Currently left unimplemented as it is unused func (proof MerkleProof) BatchVerifyMembership(specs []*ics23.ProofSpec, root exported.Root, path exported.Path, items map[string][]byte) error { - return sdkerrors.Wrap(ErrInvalidProof, "batch proofs are currently unsupported") + return errorsmod.Wrap(ErrInvalidProof, "batch proofs are currently unsupported") } // BatchVerifyNonMembership verifies absence of a group of keys against the given root // NOTE: Currently left unimplemented as it is unused func (proof MerkleProof) BatchVerifyNonMembership(specs []*ics23.ProofSpec, root exported.Root, path exported.Path, items [][]byte) error { - return sdkerrors.Wrap(ErrInvalidProof, "batch proofs are currently unsupported") + return errorsmod.Wrap(ErrInvalidProof, "batch proofs are currently unsupported") } // verifyChainedMembershipProof takes a list of proofs and specs and verifies each proof sequentially ensuring that the value is committed to @@ -234,35 +235,35 @@ func verifyChainedMembershipProof(root []byte, specs []*ics23.ProofSpec, proofs case *ics23.CommitmentProof_Exist: subroot, err = proofs[i].Calculate() if err != nil { - return sdkerrors.Wrapf(ErrInvalidProof, "could not calculate proof root at index %d, merkle tree may be empty. %v", i, err) + return errorsmod.Wrapf(ErrInvalidProof, "could not calculate proof root at index %d, merkle tree may be empty. %v", i, err) } // Since keys are passed in from highest to lowest, we must grab their indices in reverse order // from the proofs and specs which are lowest to highest key, err := keys.GetKey(uint64(len(keys.KeyPath) - 1 - i)) if err != nil { - return sdkerrors.Wrapf(ErrInvalidProof, "could not retrieve key bytes for key %s: %v", keys.KeyPath[len(keys.KeyPath)-1-i], err) + return errorsmod.Wrapf(ErrInvalidProof, "could not retrieve key bytes for key %s: %v", keys.KeyPath[len(keys.KeyPath)-1-i], err) } // verify membership of the proof at this index with appropriate key and value if ok := ics23.VerifyMembership(specs[i], subroot, proofs[i], key, value); !ok { - return sdkerrors.Wrapf(ErrInvalidProof, + return errorsmod.Wrapf(ErrInvalidProof, "chained membership proof failed to verify membership of value: %X in subroot %X at index %d. Please ensure the path and value are both correct.", value, subroot, i) } // Set value to subroot so that we verify next proof in chain commits to this subroot value = subroot case *ics23.CommitmentProof_Nonexist: - return sdkerrors.Wrapf(ErrInvalidProof, + return errorsmod.Wrapf(ErrInvalidProof, "chained membership proof contains nonexistence proof at index %d. If this is unexpected, please ensure that proof was queried from a height that contained the value in store and was queried with the correct key. The key used: %s", i, keys) default: - return sdkerrors.Wrapf(ErrInvalidProof, + return errorsmod.Wrapf(ErrInvalidProof, "expected proof type: %T, got: %T", &ics23.CommitmentProof_Exist{}, proofs[i].Proof) } } // Check that chained proof root equals passed-in root if !bytes.Equal(root, subroot) { - return sdkerrors.Wrapf(ErrInvalidProof, + return errorsmod.Wrapf(ErrInvalidProof, "proof did not commit to expected root: %X, got: %X. Please ensure proof was submitted with correct proofHeight and to the correct chain.", root, subroot) } @@ -271,8 +272,10 @@ func verifyChainedMembershipProof(root []byte, specs []*ics23.ProofSpec, proofs // blankMerkleProof and blankProofOps will be used to compare against their zero values, // and are declared as globals to avoid having to unnecessarily re-allocate on every comparison. -var blankMerkleProof = &MerkleProof{} -var blankProofOps = &tmcrypto.ProofOps{} +var ( + blankMerkleProof = &MerkleProof{} + blankProofOps = &tmcrypto.ProofOps{} +) // Empty returns true if the root is empty func (proof *MerkleProof) Empty() bool { @@ -290,22 +293,22 @@ func (proof MerkleProof) ValidateBasic() error { // validateVerificationArgs verifies the proof arguments are valid func (proof MerkleProof) validateVerificationArgs(specs []*ics23.ProofSpec, root exported.Root) error { if proof.Empty() { - return sdkerrors.Wrap(ErrInvalidMerkleProof, "proof cannot be empty") + return errorsmod.Wrap(ErrInvalidMerkleProof, "proof cannot be empty") } if root == nil || root.Empty() { - return sdkerrors.Wrap(ErrInvalidMerkleProof, "root cannot be empty") + return errorsmod.Wrap(ErrInvalidMerkleProof, "root cannot be empty") } if len(specs) != len(proof.Proofs) { - return sdkerrors.Wrapf(ErrInvalidMerkleProof, + return errorsmod.Wrapf(ErrInvalidMerkleProof, "length of specs: %d not equal to length of proof: %d", len(specs), len(proof.Proofs)) } for i, spec := range specs { if spec == nil { - return sdkerrors.Wrapf(ErrInvalidProof, "spec at position %d is nil", i) + return errorsmod.Wrapf(ErrInvalidProof, "spec at position %d is nil", i) } } return nil diff --git a/modules/core/23-commitment/types/merkle_test.go b/modules/core/23-commitment/types/merkle_test.go index cf13348faf3..8086afd8c76 100644 --- a/modules/core/23-commitment/types/merkle_test.go +++ b/modules/core/23-commitment/types/merkle_test.go @@ -7,7 +7,7 @@ import ( "github.com/stretchr/testify/require" abci "github.com/tendermint/tendermint/abci/types" - "github.com/cosmos/ibc-go/v4/modules/core/23-commitment/types" + "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/types" ) func (suite *MerkleTestSuite) TestVerifyMembership() { @@ -64,10 +64,10 @@ func (suite *MerkleTestSuite) TestVerifyMembership() { err := proof.VerifyMembership(types.GetSDKSpecs(), &root, path, tc.value) if tc.shouldPass { - // nolint: scopelint + //nolint: scopelint suite.Require().NoError(err, "test case %d should have passed", i) } else { - // nolint: scopelint + //nolint: scopelint suite.Require().Error(err, "test case %d should have failed", i) } }) @@ -127,10 +127,10 @@ func (suite *MerkleTestSuite) TestVerifyNonMembership() { err := proof.VerifyNonMembership(types.GetSDKSpecs(), &root, path) if tc.shouldPass { - // nolint: scopelint + //nolint: scopelint suite.Require().NoError(err, "test case %d should have passed", i) } else { - // nolint: scopelint + //nolint: scopelint suite.Require().Error(err, "test case %d should have failed", i) } }) diff --git a/modules/core/23-commitment/types/utils.go b/modules/core/23-commitment/types/utils.go index 7d2937f0fb5..2ad3bcf57dc 100644 --- a/modules/core/23-commitment/types/utils.go +++ b/modules/core/23-commitment/types/utils.go @@ -1,15 +1,15 @@ package types import ( - ics23 "github.com/confio/ics23/go" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + errorsmod "cosmossdk.io/errors" + ics23 "github.com/cosmos/ics23/go" crypto "github.com/tendermint/tendermint/proto/tendermint/crypto" ) // ConvertProofs converts crypto.ProofOps into MerkleProof func ConvertProofs(tmProof *crypto.ProofOps) (MerkleProof, error) { if tmProof == nil { - return MerkleProof{}, sdkerrors.Wrapf(ErrInvalidMerkleProof, "tendermint proof is nil") + return MerkleProof{}, errorsmod.Wrapf(ErrInvalidMerkleProof, "tendermint proof is nil") } // Unmarshal all proof ops to CommitmentProof proofs := make([]*ics23.CommitmentProof, len(tmProof.Ops)) @@ -17,7 +17,7 @@ func ConvertProofs(tmProof *crypto.ProofOps) (MerkleProof, error) { var p ics23.CommitmentProof err := p.Unmarshal(op.Data) if err != nil || p.Proof == nil { - return MerkleProof{}, sdkerrors.Wrapf(ErrInvalidMerkleProof, "could not unmarshal proof op into CommitmentProof at index %d: %v", i, err) + return MerkleProof{}, errorsmod.Wrapf(ErrInvalidMerkleProof, "could not unmarshal proof op into CommitmentProof at index %d: %v", i, err) } proofs[i] = &p } diff --git a/modules/core/23-commitment/types/utils_test.go b/modules/core/23-commitment/types/utils_test.go index 270d13cae5e..4a1c5d3e185 100644 --- a/modules/core/23-commitment/types/utils_test.go +++ b/modules/core/23-commitment/types/utils_test.go @@ -7,7 +7,7 @@ import ( abci "github.com/tendermint/tendermint/abci/types" crypto "github.com/tendermint/tendermint/proto/tendermint/crypto" - "github.com/cosmos/ibc-go/v4/modules/core/23-commitment/types" + "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/types" ) func (suite *MerkleTestSuite) TestConvertProofs() { diff --git a/modules/core/24-host/client_keys.go b/modules/core/24-host/client_keys.go index 3372da97689..575c363628f 100644 --- a/modules/core/24-host/client_keys.go +++ b/modules/core/24-host/client_keys.go @@ -3,7 +3,7 @@ package host import ( "fmt" - "github.com/cosmos/ibc-go/v4/modules/core/exported" + "github.com/cosmos/ibc-go/v7/modules/core/exported" ) // KeyClientStorePrefix defines the KVStore key prefix for IBC clients @@ -26,6 +26,20 @@ func FullClientKey(clientID string, path []byte) []byte { return []byte(FullClientPath(clientID, string(path))) } +// PrefixedClientStorePath returns a key path which can be used for prefixed +// key store iteration. The prefix may be a clientType, clientID, or any +// valid key prefix which may be concatenated with the client store constant. +func PrefixedClientStorePath(prefix []byte) string { + return fmt.Sprintf("%s/%s", KeyClientStorePrefix, prefix) +} + +// PrefixedClientStoreKey returns a key which can be used for prefixed +// key store iteration. The prefix may be a clientType, clientID, or any +// valid key prefix which may be concatenated with the client store constant. +func PrefixedClientStoreKey(prefix []byte) []byte { + return []byte(PrefixedClientStorePath(prefix)) +} + // ICS02 // The following paths are the keys to the store as defined in https://github.com/cosmos/ibc/tree/master/spec/core/ics-002-client-semantics#path-space diff --git a/modules/core/24-host/doc.go b/modules/core/24-host/doc.go index 0d73c4e7efe..c7bf710c597 100644 --- a/modules/core/24-host/doc.go +++ b/modules/core/24-host/doc.go @@ -1,9 +1,8 @@ /* -24-host is an implementation of ICS24. +24-host is an implementation of ICS 24. -The storage path supported are defined in [ICS24](https://github.com/cosmos/ibc/blob/master/spec/core/ics-024-host-requirements#path-space). - -Hostname validation is implemented as defined in [ICS 24](https://github.com/cosmos/ibc/tree/master/spec/core/ics-024-host-requirements). +The storage path supported are defined in ICS 24 (https://github.com/cosmos/ibc/blob/master/spec/core/ics-024-host-requirements#path-space). +Hostname validation is implemented as defined in ICS 24 (https://github.com/cosmos/ibc/tree/master/spec/core/ics-024-host-requirements). */ package host diff --git a/modules/core/24-host/errors.go b/modules/core/24-host/errors.go index fe8129bde86..742f1779311 100644 --- a/modules/core/24-host/errors.go +++ b/modules/core/24-host/errors.go @@ -1,7 +1,7 @@ package host import ( - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + errorsmod "cosmossdk.io/errors" ) // SubModuleName defines the ICS 24 host @@ -9,7 +9,7 @@ const SubModuleName = "host" // IBC client sentinel errors var ( - ErrInvalidID = sdkerrors.Register(SubModuleName, 2, "invalid identifier") - ErrInvalidPath = sdkerrors.Register(SubModuleName, 3, "invalid path") - ErrInvalidPacket = sdkerrors.Register(SubModuleName, 4, "invalid packet") + ErrInvalidID = errorsmod.Register(SubModuleName, 2, "invalid identifier") + ErrInvalidPath = errorsmod.Register(SubModuleName, 3, "invalid path") + ErrInvalidPacket = errorsmod.Register(SubModuleName, 4, "invalid packet") ) diff --git a/modules/core/24-host/parse.go b/modules/core/24-host/parse.go index 8c3459500d9..81074bbfcca 100644 --- a/modules/core/24-host/parse.go +++ b/modules/core/24-host/parse.go @@ -4,7 +4,7 @@ import ( "strconv" "strings" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + errorsmod "cosmossdk.io/errors" ) // ParseIdentifier parses the sequence from the identifier using the provided prefix. This function @@ -12,32 +12,66 @@ import ( // are required to use this format. func ParseIdentifier(identifier, prefix string) (uint64, error) { if !strings.HasPrefix(identifier, prefix) { - return 0, sdkerrors.Wrapf(ErrInvalidID, "identifier doesn't contain prefix `%s`", prefix) + return 0, errorsmod.Wrapf(ErrInvalidID, "identifier doesn't contain prefix `%s`", prefix) } splitStr := strings.Split(identifier, prefix) if len(splitStr) != 2 { - return 0, sdkerrors.Wrapf(ErrInvalidID, "identifier must be in format: `%s{N}`", prefix) + return 0, errorsmod.Wrapf(ErrInvalidID, "identifier must be in format: `%s{N}`", prefix) } // sanity check if splitStr[0] != "" { - return 0, sdkerrors.Wrapf(ErrInvalidID, "identifier must begin with prefix %s", prefix) + return 0, errorsmod.Wrapf(ErrInvalidID, "identifier must begin with prefix %s", prefix) } sequence, err := strconv.ParseUint(splitStr[1], 10, 64) if err != nil { - return 0, sdkerrors.Wrap(err, "failed to parse identifier sequence") + return 0, errorsmod.Wrap(err, "failed to parse identifier sequence") } return sequence, nil } +// MustParseClientStatePath returns the client ID from a client state path. It panics +// if the provided path is invalid or if the clientID is empty. +func MustParseClientStatePath(path string) string { + clientID, err := parseClientStatePath(path) + if err != nil { + panic(err.Error()) + } + + return clientID +} + +// parseClientStatePath returns the client ID from a client state path. It returns +// an error if the provided path is invalid. +func parseClientStatePath(path string) (string, error) { + split := strings.Split(path, "/") + if len(split) != 3 { + return "", errorsmod.Wrapf(ErrInvalidPath, "cannot parse client state path %s", path) + } + + if split[0] != string(KeyClientStorePrefix) { + return "", errorsmod.Wrapf(ErrInvalidPath, "path does not begin with client store prefix: expected %s, got %s", KeyClientStorePrefix, split[0]) + } + + if split[2] != KeyClientState { + return "", errorsmod.Wrapf(ErrInvalidPath, "path does not end with client state key: expected %s, got %s", KeyClientState, split[2]) + } + + if strings.TrimSpace(split[1]) == "" { + return "", errorsmod.Wrap(ErrInvalidPath, "clientID is empty") + } + + return split[1], nil +} + // ParseConnectionPath returns the connection ID from a full path. It returns // an error if the provided path is invalid. func ParseConnectionPath(path string) (string, error) { split := strings.Split(path, "/") if len(split) != 2 { - return "", sdkerrors.Wrapf(ErrInvalidPath, "cannot parse connection path %s", path) + return "", errorsmod.Wrapf(ErrInvalidPath, "cannot parse connection path %s", path) } return split[1], nil @@ -48,11 +82,11 @@ func ParseConnectionPath(path string) (string, error) { func ParseChannelPath(path string) (string, string, error) { split := strings.Split(path, "/") if len(split) < 5 { - return "", "", sdkerrors.Wrapf(ErrInvalidPath, "cannot parse channel path %s", path) + return "", "", errorsmod.Wrapf(ErrInvalidPath, "cannot parse channel path %s", path) } if split[1] != KeyPortPrefix || split[3] != KeyChannelPrefix { - return "", "", sdkerrors.Wrapf(ErrInvalidPath, "cannot parse channel path %s", path) + return "", "", errorsmod.Wrapf(ErrInvalidPath, "cannot parse channel path %s", path) } return split[2], split[4], nil diff --git a/modules/core/24-host/parse_test.go b/modules/core/24-host/parse_test.go index 18b9848e950..18c3ab2e832 100644 --- a/modules/core/24-host/parse_test.go +++ b/modules/core/24-host/parse_test.go @@ -1,13 +1,15 @@ package host_test import ( + "fmt" "math" "testing" "github.com/stretchr/testify/require" - connectiontypes "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" + connectiontypes "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + ibctesting "github.com/cosmos/ibc-go/v7/testing" ) func TestParseIdentifier(t *testing.T) { @@ -46,3 +48,32 @@ func TestParseIdentifier(t *testing.T) { } } } + +func TestMustParseClientStatePath(t *testing.T) { + testCases := []struct { + name string + path string + expPass bool + }{ + {"valid", host.FullClientStatePath(ibctesting.FirstClientID), true}, + {"path too large", fmt.Sprintf("clients/clients/%s/clientState", ibctesting.FirstClientID), false}, + {"path too small", fmt.Sprintf("clients/%s", ibctesting.FirstClientID), false}, + {"path does not begin with client store", fmt.Sprintf("cli/%s/%s", ibctesting.FirstClientID, host.KeyClientState), false}, + {"path does not end with client state key", fmt.Sprintf("%s/%s/consensus", string(host.KeyClientStorePrefix), ibctesting.FirstClientID), false}, + {"client ID is empty", host.FullClientStatePath(""), false}, + {"client ID is only spaces", host.FullClientStatePath(" "), false}, + } + + for _, tc := range testCases { + if tc.expPass { + require.NotPanics(t, func() { + clientID := host.MustParseClientStatePath(tc.path) + require.Equal(t, ibctesting.FirstClientID, clientID) + }) + } else { + require.Panics(t, func() { + host.MustParseClientStatePath(tc.path) + }) + } + } +} diff --git a/modules/core/24-host/validate.go b/modules/core/24-host/validate.go index 7102e66b455..e6219d99b40 100644 --- a/modules/core/24-host/validate.go +++ b/modules/core/24-host/validate.go @@ -4,7 +4,7 @@ import ( "regexp" "strings" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + errorsmod "cosmossdk.io/errors" ) // DefaultMaxCharacterLength defines the default maximum character length used @@ -36,21 +36,21 @@ var IsValidID = regexp.MustCompile(`^[a-zA-Z0-9\.\_\+\-\#\[\]\<\>]+$`).MatchStri // ValidateFn function type to validate path and identifier bytestrings type ValidateFn func(string) error -func defaultIdentifierValidator(id string, min, max int) error { //nolint:unparam +func defaultIdentifierValidator(id string, min, max int) error { if strings.TrimSpace(id) == "" { - return sdkerrors.Wrap(ErrInvalidID, "identifier cannot be blank") + return errorsmod.Wrap(ErrInvalidID, "identifier cannot be blank") } // valid id MUST NOT contain "/" separator if strings.Contains(id, "/") { - return sdkerrors.Wrapf(ErrInvalidID, "identifier %s cannot contain separator '/'", id) + return errorsmod.Wrapf(ErrInvalidID, "identifier %s cannot contain separator '/'", id) } // valid id must fit the length requirements if len(id) < min || len(id) > max { - return sdkerrors.Wrapf(ErrInvalidID, "identifier %s has invalid length: %d, must be between %d-%d characters", id, len(id), min, max) + return errorsmod.Wrapf(ErrInvalidID, "identifier %s has invalid length: %d, must be between %d-%d characters", id, len(id), min, max) } // valid id must contain only lower alphabetic characters if !IsValidID(id) { - return sdkerrors.Wrapf( + return errorsmod.Wrapf( ErrInvalidID, "identifier %s must contain only alphanumeric or the following characters: '.', '_', '+', '-', '#', '[', ']', '<', '>'", id, @@ -95,13 +95,13 @@ func NewPathValidator(idValidator ValidateFn) ValidateFn { return func(path string) error { pathArr := strings.Split(path, "/") if len(pathArr) > 0 && pathArr[0] == path { - return sdkerrors.Wrapf(ErrInvalidPath, "path %s doesn't contain any separator '/'", path) + return errorsmod.Wrapf(ErrInvalidPath, "path %s doesn't contain any separator '/'", path) } for _, p := range pathArr { // a path beginning or ending in a separator returns empty string elements. if p == "" { - return sdkerrors.Wrapf(ErrInvalidPath, "path %s cannot begin or end with '/'", path) + return errorsmod.Wrapf(ErrInvalidPath, "path %s cannot begin or end with '/'", path) } if err := idValidator(p); err != nil { @@ -109,7 +109,7 @@ func NewPathValidator(idValidator ValidateFn) ValidateFn { } // Each path element must either be a valid identifier or constant number if err := defaultIdentifierValidator(p, 1, DefaultMaxCharacterLength); err != nil { - return sdkerrors.Wrapf(err, "path %s contains an invalid identifier: '%s'", path, p) + return errorsmod.Wrapf(err, "path %s contains an invalid identifier: '%s'", path, p) } } diff --git a/modules/core/24-host/validate_test.go b/modules/core/24-host/validate_test.go index fe5c290ba40..e75ce9334bd 100644 --- a/modules/core/24-host/validate_test.go +++ b/modules/core/24-host/validate_test.go @@ -9,7 +9,7 @@ import ( ) // 195 characters -var longId = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis eros neque, ultricies vel ligula ac, convallis porttitor elit. Maecenas tincidunt turpis elit, vel faucibus nisl pellentesque sodales" +var longID = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis eros neque, ultricies vel ligula ac, convallis porttitor elit. Maecenas tincidunt turpis elit, vel faucibus nisl pellentesque sodales" type testCase struct { msg string @@ -63,7 +63,7 @@ func TestPortIdentifierValidator(t *testing.T) { {"numeric id", "1234567890", true}, {"blank id", " ", false}, {"id length out of range", "1", false}, - {"id is too long", longId, false}, + {"id is too long", longID, false}, {"path-like id", "lower/case/id", false}, {"invalid id", "(clientid)", false}, {"empty string", "", false}, diff --git a/modules/core/ante/ante.go b/modules/core/ante/ante.go index 1ad8d776b70..7b8cdb326de 100644 --- a/modules/core/ante/ante.go +++ b/modules/core/ante/ante.go @@ -3,25 +3,25 @@ package ante import ( sdk "github.com/cosmos/cosmos-sdk/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" - "github.com/cosmos/ibc-go/v4/modules/core/keeper" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + "github.com/cosmos/ibc-go/v7/modules/core/keeper" ) -type AnteDecorator struct { +type RedundantRelayDecorator struct { k *keeper.Keeper } -func NewAnteDecorator(k *keeper.Keeper) AnteDecorator { - return AnteDecorator{k: k} +func NewRedundantRelayDecorator(k *keeper.Keeper) RedundantRelayDecorator { + return RedundantRelayDecorator{k: k} } -// AnteDecorator returns an error if a multiMsg tx only contains packet messages (Recv, Ack, Timeout) and additional update messages +// RedundantRelayDecorator returns an error if a multiMsg tx only contains packet messages (Recv, Ack, Timeout) and additional update messages // and all packet messages are redundant. If the transaction is just a single UpdateClient message, or the multimsg transaction // contains some other message type, then the antedecorator returns no error and continues processing to ensure these transactions // are included. This will ensure that relayers do not waste fees on multiMsg transactions when another relayer has already submitted // all packets, by rejecting the tx at the mempool layer. -func (ad AnteDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) { +func (rrd RedundantRelayDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) { // do not run redundancy check on DeliverTx or simulate if (ctx.IsCheckTx() || ctx.IsReCheckTx()) && !simulate { // keep track of total packet messages and number of redundancies across `RecvPacket`, `AcknowledgePacket`, and `TimeoutPacket/OnClose` @@ -30,47 +30,47 @@ func (ad AnteDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, ne for _, m := range tx.GetMsgs() { switch msg := m.(type) { case *channeltypes.MsgRecvPacket: - response, err := ad.k.RecvPacket(sdk.WrapSDKContext(ctx), msg) + response, err := rrd.k.RecvPacket(sdk.WrapSDKContext(ctx), msg) if err != nil { return ctx, err } if response.Result == channeltypes.NOOP { - redundancies += 1 + redundancies++ } - packetMsgs += 1 + packetMsgs++ case *channeltypes.MsgAcknowledgement: - response, err := ad.k.Acknowledgement(sdk.WrapSDKContext(ctx), msg) + response, err := rrd.k.Acknowledgement(sdk.WrapSDKContext(ctx), msg) if err != nil { return ctx, err } if response.Result == channeltypes.NOOP { - redundancies += 1 + redundancies++ } - packetMsgs += 1 + packetMsgs++ case *channeltypes.MsgTimeout: - response, err := ad.k.Timeout(sdk.WrapSDKContext(ctx), msg) + response, err := rrd.k.Timeout(sdk.WrapSDKContext(ctx), msg) if err != nil { return ctx, err } if response.Result == channeltypes.NOOP { - redundancies += 1 + redundancies++ } - packetMsgs += 1 + packetMsgs++ case *channeltypes.MsgTimeoutOnClose: - response, err := ad.k.TimeoutOnClose(sdk.WrapSDKContext(ctx), msg) + response, err := rrd.k.TimeoutOnClose(sdk.WrapSDKContext(ctx), msg) if err != nil { return ctx, err } if response.Result == channeltypes.NOOP { - redundancies += 1 + redundancies++ } - packetMsgs += 1 + packetMsgs++ case *clienttypes.MsgUpdateClient: - _, err := ad.k.UpdateClient(sdk.WrapSDKContext(ctx), msg) + _, err := rrd.k.UpdateClient(sdk.WrapSDKContext(ctx), msg) if err != nil { return ctx, err } diff --git a/modules/core/ante/ante_test.go b/modules/core/ante/ante_test.go index fb71db32b0f..20ed350a053 100644 --- a/modules/core/ante/ante_test.go +++ b/modules/core/ante/ante_test.go @@ -7,12 +7,12 @@ import ( "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" - "github.com/cosmos/ibc-go/v4/modules/core/ante" - "github.com/cosmos/ibc-go/v4/modules/core/exported" - ibctesting "github.com/cosmos/ibc-go/v4/testing" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + "github.com/cosmos/ibc-go/v7/modules/core/ante" + "github.com/cosmos/ibc-go/v7/modules/core/exported" + ibctesting "github.com/cosmos/ibc-go/v7/testing" ) type AnteTestSuite struct { @@ -45,14 +45,14 @@ func TestAnteTestSuite(t *testing.T) { } // createRecvPacketMessage creates a RecvPacket message for a packet sent from chain A to chain B. -func (suite *AnteTestSuite) createRecvPacketMessage(sequenceNumber uint64, isRedundant bool) sdk.Msg { - packet := channeltypes.NewPacket(ibctesting.MockPacketData, sequenceNumber, +func (suite *AnteTestSuite) createRecvPacketMessage(isRedundant bool) sdk.Msg { + sequence, err := suite.path.EndpointA.SendPacket(clienttypes.NewHeight(2, 0), 0, ibctesting.MockPacketData) + suite.Require().NoError(err) + + packet := channeltypes.NewPacket(ibctesting.MockPacketData, sequence, suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, suite.path.EndpointB.ChannelConfig.PortID, suite.path.EndpointB.ChannelID, - clienttypes.NewHeight(1, 0), 0) - - err := suite.path.EndpointA.SendPacket(packet) - suite.Require().NoError(err) + clienttypes.NewHeight(2, 0), 0) if isRedundant { err = suite.path.EndpointB.RecvPacket(packet) @@ -69,14 +69,14 @@ func (suite *AnteTestSuite) createRecvPacketMessage(sequenceNumber uint64, isRed } // createAcknowledgementMessage creates an Acknowledgement message for a packet sent from chain B to chain A. -func (suite *AnteTestSuite) createAcknowledgementMessage(sequenceNumber uint64, isRedundant bool) sdk.Msg { - packet := channeltypes.NewPacket(ibctesting.MockPacketData, sequenceNumber, +func (suite *AnteTestSuite) createAcknowledgementMessage(isRedundant bool) sdk.Msg { + sequence, err := suite.path.EndpointB.SendPacket(clienttypes.NewHeight(2, 0), 0, ibctesting.MockPacketData) + suite.Require().NoError(err) + + packet := channeltypes.NewPacket(ibctesting.MockPacketData, sequence, suite.path.EndpointB.ChannelConfig.PortID, suite.path.EndpointB.ChannelID, suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, - clienttypes.NewHeight(1, 0), 0) - - err := suite.path.EndpointB.SendPacket(packet) - suite.Require().NoError(err) + clienttypes.NewHeight(2, 0), 0) err = suite.path.EndpointA.RecvPacket(packet) suite.Require().NoError(err) @@ -92,15 +92,11 @@ func (suite *AnteTestSuite) createAcknowledgementMessage(sequenceNumber uint64, } // createTimeoutMessage creates an Timeout message for a packet sent from chain B to chain A. -func (suite *AnteTestSuite) createTimeoutMessage(sequenceNumber uint64, isRedundant bool) sdk.Msg { +func (suite *AnteTestSuite) createTimeoutMessage(isRedundant bool) sdk.Msg { height := suite.chainA.LastHeader.GetHeight() timeoutHeight := clienttypes.NewHeight(height.GetRevisionNumber(), height.GetRevisionHeight()+1) - packet := channeltypes.NewPacket(ibctesting.MockPacketData, sequenceNumber, - suite.path.EndpointB.ChannelConfig.PortID, suite.path.EndpointB.ChannelID, - suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, - timeoutHeight, 0) - err := suite.path.EndpointB.SendPacket(packet) + sequence, err := suite.path.EndpointB.SendPacket(timeoutHeight, 0, ibctesting.MockPacketData) suite.Require().NoError(err) suite.coordinator.CommitNBlocks(suite.chainA, 3) @@ -108,6 +104,11 @@ func (suite *AnteTestSuite) createTimeoutMessage(sequenceNumber uint64, isRedund err = suite.path.EndpointB.UpdateClient() suite.Require().NoError(err) + packet := channeltypes.NewPacket(ibctesting.MockPacketData, sequence, + suite.path.EndpointB.ChannelConfig.PortID, suite.path.EndpointB.ChannelID, + suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, + timeoutHeight, 0) + if isRedundant { err = suite.path.EndpointB.TimeoutPacket(packet) suite.Require().NoError(err) @@ -116,23 +117,24 @@ func (suite *AnteTestSuite) createTimeoutMessage(sequenceNumber uint64, isRedund packetKey := host.PacketReceiptKey(packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) proof, proofHeight := suite.chainA.QueryProof(packetKey) - return channeltypes.NewMsgTimeout(packet, sequenceNumber, proof, proofHeight, suite.path.EndpointA.Chain.SenderAccount.GetAddress().String()) + return channeltypes.NewMsgTimeout(packet, sequence, proof, proofHeight, suite.path.EndpointA.Chain.SenderAccount.GetAddress().String()) } // createTimeoutOnCloseMessage creates an TimeoutOnClose message for a packet sent from chain B to chain A. -func (suite *AnteTestSuite) createTimeoutOnCloseMessage(sequenceNumber uint64, isRedundant bool) sdk.Msg { +func (suite *AnteTestSuite) createTimeoutOnCloseMessage(isRedundant bool) sdk.Msg { height := suite.chainA.LastHeader.GetHeight() timeoutHeight := clienttypes.NewHeight(height.GetRevisionNumber(), height.GetRevisionHeight()+1) - packet := channeltypes.NewPacket(ibctesting.MockPacketData, sequenceNumber, - suite.path.EndpointB.ChannelConfig.PortID, suite.path.EndpointB.ChannelID, - suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, - timeoutHeight, 0) - err := suite.path.EndpointB.SendPacket(packet) + sequence, err := suite.path.EndpointB.SendPacket(timeoutHeight, 0, ibctesting.MockPacketData) suite.Require().NoError(err) - err = suite.path.EndpointA.SetChannelClosed() + err = suite.path.EndpointA.SetChannelState(channeltypes.CLOSED) suite.Require().NoError(err) + packet := channeltypes.NewPacket(ibctesting.MockPacketData, sequence, + suite.path.EndpointB.ChannelConfig.PortID, suite.path.EndpointB.ChannelID, + suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, + timeoutHeight, 0) + if isRedundant { err = suite.path.EndpointB.TimeoutOnClose(packet) suite.Require().NoError(err) @@ -153,7 +155,7 @@ func (suite *AnteTestSuite) createUpdateClientMessage() sdk.Msg { // ensure counterparty has committed state endpoint.Chain.Coordinator.CommitBlock(endpoint.Counterparty.Chain) - var header exported.Header + var header exported.ClientMessage switch endpoint.ClientConfig.GetClientType() { case exported.Tendermint: @@ -166,7 +168,7 @@ func (suite *AnteTestSuite) createUpdateClientMessage() sdk.Msg { endpoint.ClientID, header, endpoint.Chain.SenderAccount.GetAddress().String(), ) - require.NoError(endpoint.Chain.T, err) + require.NoError(endpoint.Chain.TB, err) return msg } @@ -181,7 +183,7 @@ func (suite *AnteTestSuite) TestAnteDecorator() { "success on one new RecvPacket message", func(suite *AnteTestSuite) []sdk.Msg { // the RecvPacket message has not been submitted to the chain yet, so it will succeed - return []sdk.Msg{suite.createRecvPacketMessage(1, false)} + return []sdk.Msg{suite.createRecvPacketMessage(false)} }, true, }, @@ -189,7 +191,7 @@ func (suite *AnteTestSuite) TestAnteDecorator() { "success on one new Acknowledgement message", func(suite *AnteTestSuite) []sdk.Msg { // the Acknowledgement message has not been submitted to the chain yet, so it will succeed - return []sdk.Msg{suite.createAcknowledgementMessage(1, false)} + return []sdk.Msg{suite.createAcknowledgementMessage(false)} }, true, }, @@ -197,7 +199,7 @@ func (suite *AnteTestSuite) TestAnteDecorator() { "success on one new Timeout message", func(suite *AnteTestSuite) []sdk.Msg { // the Timeout message has not been submitted to the chain yet, so it will succeed - return []sdk.Msg{suite.createTimeoutMessage(1, false)} + return []sdk.Msg{suite.createTimeoutMessage(false)} }, true, }, @@ -205,7 +207,7 @@ func (suite *AnteTestSuite) TestAnteDecorator() { "success on one new TimeoutOnClose message", func(suite *AnteTestSuite) []sdk.Msg { // the TimeoutOnClose message has not been submitted to the chain yet, so it will succeed - return []sdk.Msg{suite.createTimeoutOnCloseMessage(uint64(1), false)} + return []sdk.Msg{suite.createTimeoutOnCloseMessage(false)} }, true, }, @@ -220,18 +222,18 @@ func (suite *AnteTestSuite) TestAnteDecorator() { // from A to B for i := 1; i <= 3; i++ { - msgs = append(msgs, suite.createRecvPacketMessage(uint64(i), false)) + msgs = append(msgs, suite.createRecvPacketMessage(false)) } // from B to A for i := 1; i <= 9; i++ { switch { case i >= 1 && i <= 3: - msgs = append(msgs, suite.createAcknowledgementMessage(uint64(i), false)) + msgs = append(msgs, suite.createAcknowledgementMessage(false)) case i >= 4 && i <= 6: - msgs = append(msgs, suite.createTimeoutMessage(uint64(i), false)) + msgs = append(msgs, suite.createTimeoutMessage(false)) case i >= 7 && i <= 9: - msgs = append(msgs, suite.createTimeoutOnCloseMessage(uint64(i), false)) + msgs = append(msgs, suite.createTimeoutOnCloseMessage(false)) } } return msgs @@ -250,18 +252,18 @@ func (suite *AnteTestSuite) TestAnteDecorator() { // from A to B for i := 1; i <= 3; i++ { - msgs = append(msgs, suite.createRecvPacketMessage(uint64(i), true)) + msgs = append(msgs, suite.createRecvPacketMessage(true)) } // from B to A for i := 1; i <= 7; i++ { switch { case i >= 1 && i <= 3: - msgs = append(msgs, suite.createAcknowledgementMessage(uint64(i), true)) + msgs = append(msgs, suite.createAcknowledgementMessage(true)) case i == 4: - msgs = append(msgs, suite.createTimeoutMessage(uint64(i), false)) + msgs = append(msgs, suite.createTimeoutMessage(false)) case i >= 5 && i <= 7: - msgs = append(msgs, suite.createTimeoutOnCloseMessage(uint64(i), true)) + msgs = append(msgs, suite.createTimeoutOnCloseMessage(true)) } } return msgs @@ -280,18 +282,18 @@ func (suite *AnteTestSuite) TestAnteDecorator() { // from A to B for i := 1; i <= 3; i++ { - msgs = append(msgs, suite.createRecvPacketMessage(uint64(i), i != 2)) + msgs = append(msgs, suite.createRecvPacketMessage(i != 2)) } // from B to A for i := 1; i <= 9; i++ { switch { case i >= 1 && i <= 3: - msgs = append(msgs, suite.createAcknowledgementMessage(uint64(i), i != 2)) + msgs = append(msgs, suite.createAcknowledgementMessage(i != 2)) case i >= 4 && i <= 6: - msgs = append(msgs, suite.createTimeoutMessage(uint64(i), i != 5)) + msgs = append(msgs, suite.createTimeoutMessage(i != 5)) case i >= 7 && i <= 9: - msgs = append(msgs, suite.createTimeoutOnCloseMessage(uint64(i), i != 8)) + msgs = append(msgs, suite.createTimeoutOnCloseMessage(i != 8)) } } return msgs @@ -319,7 +321,7 @@ func (suite *AnteTestSuite) TestAnteDecorator() { suite.createUpdateClientMessage(), suite.createUpdateClientMessage(), suite.createUpdateClientMessage(), - suite.createRecvPacketMessage(uint64(1), false), + suite.createRecvPacketMessage(false), } }, true, @@ -330,7 +332,7 @@ func (suite *AnteTestSuite) TestAnteDecorator() { msgs := []sdk.Msg{suite.createUpdateClientMessage()} for i := 1; i <= 3; i++ { - msgs = append(msgs, suite.createRecvPacketMessage(uint64(i), true)) + msgs = append(msgs, suite.createRecvPacketMessage(true)) } // append non packet and update message to msgs to ensure multimsg tx should pass @@ -342,7 +344,7 @@ func (suite *AnteTestSuite) TestAnteDecorator() { { "no success on one redundant RecvPacket message", func(suite *AnteTestSuite) []sdk.Msg { - return []sdk.Msg{suite.createRecvPacketMessage(uint64(1), true)} + return []sdk.Msg{suite.createRecvPacketMessage(true)} }, false, }, @@ -353,18 +355,18 @@ func (suite *AnteTestSuite) TestAnteDecorator() { // from A to B for i := 1; i <= 3; i++ { - msgs = append(msgs, suite.createRecvPacketMessage(uint64(i), true)) + msgs = append(msgs, suite.createRecvPacketMessage(true)) } // from B to A for i := 1; i <= 9; i++ { switch { case i >= 1 && i <= 3: - msgs = append(msgs, suite.createAcknowledgementMessage(uint64(i), true)) + msgs = append(msgs, suite.createAcknowledgementMessage(true)) case i >= 4 && i <= 6: - msgs = append(msgs, suite.createTimeoutMessage(uint64(i), true)) + msgs = append(msgs, suite.createTimeoutMessage(true)) case i >= 7 && i <= 9: - msgs = append(msgs, suite.createTimeoutOnCloseMessage(uint64(i), true)) + msgs = append(msgs, suite.createTimeoutOnCloseMessage(true)) } } return msgs @@ -377,7 +379,7 @@ func (suite *AnteTestSuite) TestAnteDecorator() { msgs := []sdk.Msg{&clienttypes.MsgUpdateClient{}} for i := 1; i <= 3; i++ { - msgs = append(msgs, suite.createRecvPacketMessage(uint64(i), true)) + msgs = append(msgs, suite.createRecvPacketMessage(true)) } return msgs @@ -391,18 +393,18 @@ func (suite *AnteTestSuite) TestAnteDecorator() { // from A to B for i := 1; i <= 3; i++ { - msgs = append(msgs, suite.createRecvPacketMessage(uint64(i), true)) + msgs = append(msgs, suite.createRecvPacketMessage(true)) } // from B to A for i := 1; i <= 9; i++ { switch { case i >= 1 && i <= 3: - msgs = append(msgs, suite.createAcknowledgementMessage(uint64(i), true)) + msgs = append(msgs, suite.createAcknowledgementMessage(true)) case i >= 4 && i <= 6: - msgs = append(msgs, suite.createTimeoutMessage(uint64(i), true)) + msgs = append(msgs, suite.createTimeoutMessage(true)) case i >= 7 && i <= 9: - msgs = append(msgs, suite.createTimeoutOnCloseMessage(uint64(i), true)) + msgs = append(msgs, suite.createTimeoutOnCloseMessage(true)) } } return msgs @@ -415,11 +417,11 @@ func (suite *AnteTestSuite) TestAnteDecorator() { packet := channeltypes.NewPacket(ibctesting.MockPacketData, 2, suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, suite.path.EndpointB.ChannelConfig.PortID, suite.path.EndpointB.ChannelID, - clienttypes.NewHeight(1, 0), 0) + clienttypes.NewHeight(2, 0), 0) return []sdk.Msg{ - suite.createRecvPacketMessage(uint64(1), false), - channeltypes.NewMsgRecvPacket(packet, []byte("proof"), clienttypes.NewHeight(0, 1), "signer"), + suite.createRecvPacketMessage(false), + channeltypes.NewMsgRecvPacket(packet, []byte("proof"), clienttypes.NewHeight(1, 1), "signer"), } }, false, @@ -427,13 +429,13 @@ func (suite *AnteTestSuite) TestAnteDecorator() { { "no success on one new message and one redundant message in the same block", func(suite *AnteTestSuite) []sdk.Msg { - msg := suite.createRecvPacketMessage(uint64(1), false) + msg := suite.createRecvPacketMessage(false) // We want to be able to run check tx with the non-redundant message without - // commiting it to a block, so that the when check tx runs with the redundant + // committing it to a block, so that the when check tx runs with the redundant // message they are both in the same block k := suite.chainB.App.GetIBCKeeper() - decorator := ante.NewAnteDecorator(k) + decorator := ante.NewRedundantRelayDecorator(k) checkCtx := suite.chainB.GetContext().WithIsCheckTx(true) next := func(ctx sdk.Context, tx sdk.Tx, simulate bool) (newCtx sdk.Context, err error) { return ctx, nil } txBuilder := suite.chainB.TxConfig.NewTxBuilder() @@ -458,7 +460,7 @@ func (suite *AnteTestSuite) TestAnteDecorator() { suite.SetupTest() k := suite.chainB.App.GetIBCKeeper() - decorator := ante.NewAnteDecorator(k) + decorator := ante.NewRedundantRelayDecorator(k) msgs := tc.malleate(suite) diff --git a/modules/core/client/cli/cli.go b/modules/core/client/cli/cli.go index a16b1d17751..6d5da971264 100644 --- a/modules/core/client/cli/cli.go +++ b/modules/core/client/cli/cli.go @@ -1,20 +1,19 @@ package cli import ( - "github.com/spf13/cobra" - "github.com/cosmos/cosmos-sdk/client" + "github.com/spf13/cobra" - ibcclient "github.com/cosmos/ibc-go/v4/modules/core/02-client" - connection "github.com/cosmos/ibc-go/v4/modules/core/03-connection" - channel "github.com/cosmos/ibc-go/v4/modules/core/04-channel" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" + ibcclient "github.com/cosmos/ibc-go/v7/modules/core/02-client" + connection "github.com/cosmos/ibc-go/v7/modules/core/03-connection" + channel "github.com/cosmos/ibc-go/v7/modules/core/04-channel" + ibcexported "github.com/cosmos/ibc-go/v7/modules/core/exported" ) // GetTxCmd returns the transaction commands for this module func GetTxCmd() *cobra.Command { ibcTxCmd := &cobra.Command{ - Use: host.ModuleName, + Use: ibcexported.ModuleName, Short: "IBC transaction subcommands", DisableFlagParsing: true, SuggestionsMinimumDistance: 2, @@ -33,7 +32,7 @@ func GetTxCmd() *cobra.Command { func GetQueryCmd() *cobra.Command { // Group ibc queries under a subcommand ibcQueryCmd := &cobra.Command{ - Use: host.ModuleName, + Use: ibcexported.ModuleName, Short: "Querying commands for the IBC module", DisableFlagParsing: true, SuggestionsMinimumDistance: 2, diff --git a/modules/core/client/query.go b/modules/core/client/query.go index 3dd56aa92cc..48a87b3fa17 100644 --- a/modules/core/client/query.go +++ b/modules/core/client/query.go @@ -7,9 +7,9 @@ import ( "github.com/cosmos/cosmos-sdk/codec" abci "github.com/tendermint/tendermint/abci/types" - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - commitmenttypes "github.com/cosmos/ibc-go/v4/modules/core/23-commitment/types" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + commitmenttypes "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/types" + ibcexported "github.com/cosmos/ibc-go/v7/modules/core/exported" ) // QueryTendermintProof performs an ABCI query with the given key and returns @@ -39,7 +39,7 @@ func QueryTendermintProof(clientCtx client.Context, key []byte) ([]byte, []byte, } req := abci.RequestQuery{ - Path: fmt.Sprintf("store/%s/key", host.StoreKey), + Path: fmt.Sprintf("store/%s/key", ibcexported.StoreKey), Height: height, Data: key, Prove: true, diff --git a/modules/core/exported/client.go b/modules/core/exported/client.go index 4dce203bea4..88273ecc860 100644 --- a/modules/core/exported/client.go +++ b/modules/core/exported/client.go @@ -3,7 +3,7 @@ package exported import ( "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" - proto "github.com/gogo/protobuf/proto" + proto "github.com/cosmos/gogoproto/proto" ) // Status represents the status of a client @@ -19,10 +19,6 @@ const ( // Tendermint is used to indicate that the client uses the Tendermint Consensus Algorithm. Tendermint string = "07-tendermint" - // Localhost is the client type for a localhost client. It is also used as the clientID - // for the localhost client. - Localhost string = "09-localhost" - // Active is a status type of a client. An active client is allowed to be used. Active Status = "Active" @@ -44,137 +40,92 @@ type ClientState interface { GetLatestHeight() Height Validate() error - // Initialization function - // Clients must validate the initial consensus state, and may store any client-specific metadata - // necessary for correct light client operation - Initialize(sdk.Context, codec.BinaryCodec, sdk.KVStore, ConsensusState) error - - // Status function - // Clients must return their status. Only Active clients are allowed to process packets. + // Status must return the status of the client. Only Active clients are allowed to process packets. Status(ctx sdk.Context, clientStore sdk.KVStore, cdc codec.BinaryCodec) Status - // Genesis function - ExportMetadata(sdk.KVStore) []GenesisMetadata - - // Update and Misbehaviour functions + // ExportMetadata must export metadata stored within the clientStore for genesis export + ExportMetadata(clientStore sdk.KVStore) []GenesisMetadata - CheckHeaderAndUpdateState(sdk.Context, codec.BinaryCodec, sdk.KVStore, Header) (ClientState, ConsensusState, error) - CheckMisbehaviourAndUpdateState(sdk.Context, codec.BinaryCodec, sdk.KVStore, Misbehaviour) (ClientState, error) - CheckSubstituteAndUpdateState(ctx sdk.Context, cdc codec.BinaryCodec, subjectClientStore, substituteClientStore sdk.KVStore, substituteClient ClientState) (ClientState, error) - - // Upgrade functions - // NOTE: proof heights are not included as upgrade to a new revision is expected to pass only on the last - // height committed by the current revision. Clients are responsible for ensuring that the planned last - // height of the current revision is somehow encoded in the proof verification process. - // This is to ensure that no premature upgrades occur, since upgrade plans committed to by the counterparty - // may be cancelled or modified before the last planned height. - VerifyUpgradeAndUpdateState( - ctx sdk.Context, - cdc codec.BinaryCodec, - store sdk.KVStore, - newClient ClientState, - newConsState ConsensusState, - proofUpgradeClient, - proofUpgradeConsState []byte, - ) (ClientState, ConsensusState, error) - // Utility function that zeroes out any client customizable fields in client state + // ZeroCustomFields zeroes out any client customizable fields in client state // Ledger enforced fields are maintained while all custom fields are zero values // Used to verify upgrades ZeroCustomFields() ClientState - // State verification functions - - VerifyClientState( - store sdk.KVStore, - cdc codec.BinaryCodec, - height Height, - prefix Prefix, - counterpartyClientIdentifier string, - proof []byte, - clientState ClientState, - ) error - VerifyClientConsensusState( - store sdk.KVStore, - cdc codec.BinaryCodec, - height Height, - counterpartyClientIdentifier string, - consensusHeight Height, - prefix Prefix, - proof []byte, - consensusState ConsensusState, - ) error - VerifyConnectionState( - store sdk.KVStore, - cdc codec.BinaryCodec, - height Height, - prefix Prefix, - proof []byte, - connectionID string, - connectionEnd ConnectionI, - ) error - VerifyChannelState( - store sdk.KVStore, - cdc codec.BinaryCodec, - height Height, - prefix Prefix, - proof []byte, - portID, - channelID string, - channel ChannelI, - ) error - VerifyPacketCommitment( + // GetTimestampAtHeight must return the timestamp for the consensus state associated with the provided height. + GetTimestampAtHeight( ctx sdk.Context, - store sdk.KVStore, + clientStore sdk.KVStore, cdc codec.BinaryCodec, height Height, - delayTimePeriod uint64, - delayBlockPeriod uint64, - prefix Prefix, - proof []byte, - portID, - channelID string, - sequence uint64, - commitmentBytes []byte, - ) error - VerifyPacketAcknowledgement( + ) (uint64, error) + + // Initialize is called upon client creation, it allows the client to perform validation on the initial consensus state and set the + // client state, consensus state and any client-specific metadata necessary for correct light client operation in the provided client store. + Initialize(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, consensusState ConsensusState) error + + // VerifyMembership is a generic proof verification method which verifies a proof of the existence of a value at a given CommitmentPath at the specified height. + // The caller is expected to construct the full CommitmentPath from a CommitmentPrefix and a standardized path (as defined in ICS 24). + VerifyMembership( ctx sdk.Context, - store sdk.KVStore, + clientStore sdk.KVStore, cdc codec.BinaryCodec, height Height, delayTimePeriod uint64, delayBlockPeriod uint64, - prefix Prefix, proof []byte, - portID, - channelID string, - sequence uint64, - acknowledgement []byte, + path Path, + value []byte, ) error - VerifyPacketReceiptAbsence( + + // VerifyNonMembership is a generic proof verification method which verifies the absence of a given CommitmentPath at a specified height. + // The caller is expected to construct the full CommitmentPath from a CommitmentPrefix and a standardized path (as defined in ICS 24). + VerifyNonMembership( ctx sdk.Context, - store sdk.KVStore, + clientStore sdk.KVStore, cdc codec.BinaryCodec, height Height, delayTimePeriod uint64, delayBlockPeriod uint64, - prefix Prefix, proof []byte, - portID, - channelID string, - sequence uint64, + path Path, ) error - VerifyNextSequenceRecv( + + // VerifyClientMessage must verify a ClientMessage. A ClientMessage could be a Header, Misbehaviour, or batch update. + // It must handle each type of ClientMessage appropriately. Calls to CheckForMisbehaviour, UpdateState, and UpdateStateOnMisbehaviour + // will assume that the content of the ClientMessage has been verified and can be trusted. An error should be returned + // if the ClientMessage fails to verify. + VerifyClientMessage(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, clientMsg ClientMessage) error + + // Checks for evidence of a misbehaviour in Header or Misbehaviour type. It assumes the ClientMessage + // has already been verified. + CheckForMisbehaviour(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, clientMsg ClientMessage) bool + + // UpdateStateOnMisbehaviour should perform appropriate state changes on a client state given that misbehaviour has been detected and verified + UpdateStateOnMisbehaviour(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, clientMsg ClientMessage) + + // UpdateState updates and stores as necessary any associated information for an IBC client, such as the ClientState and corresponding ConsensusState. + // Upon successful update, a list of consensus heights is returned. It assumes the ClientMessage has already been verified. + UpdateState(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, clientMsg ClientMessage) []Height + + // CheckSubstituteAndUpdateState must verify that the provided substitute may be used to update the subject client. + // The light client must set the updated client and consensus states within the clientStore for the subject client. + CheckSubstituteAndUpdateState(ctx sdk.Context, cdc codec.BinaryCodec, subjectClientStore, substituteClientStore sdk.KVStore, substituteClient ClientState) error + + // Upgrade functions + // NOTE: proof heights are not included as upgrade to a new revision is expected to pass only on the last + // height committed by the current revision. Clients are responsible for ensuring that the planned last + // height of the current revision is somehow encoded in the proof verification process. + // This is to ensure that no premature upgrades occur, since upgrade plans committed to by the counterparty + // may be cancelled or modified before the last planned height. + // If the upgrade is verified, the upgraded client and consensus states must be set in the client store. + VerifyUpgradeAndUpdateState( ctx sdk.Context, - store sdk.KVStore, cdc codec.BinaryCodec, - height Height, - delayTimePeriod uint64, - delayBlockPeriod uint64, - prefix Prefix, - proof []byte, - portID, - channelID string, - nextSequenceRecv uint64, + store sdk.KVStore, + newClient ClientState, + newConsState ConsensusState, + proofUpgradeClient, + proofUpgradeConsState []byte, ) error } @@ -184,31 +135,19 @@ type ConsensusState interface { ClientType() string // Consensus kind - // GetRoot returns the commitment root of the consensus state, - // which is used for key-value pair verification. - GetRoot() Root - // GetTimestamp returns the timestamp (in nanoseconds) of the consensus state GetTimestamp() uint64 ValidateBasic() error } -// Misbehaviour defines counterparty misbehaviour for a specific consensus type -type Misbehaviour interface { - proto.Message - - ClientType() string - GetClientID() string - ValidateBasic() error -} - -// Header is the consensus state update information -type Header interface { +// ClientMessage is an interface used to update an IBC client. +// The update may be done by a single header, a batch of headers, misbehaviour, or any type which when verified produces +// a change to state of the IBC client +type ClientMessage interface { proto.Message ClientType() string - GetHeight() Height ValidateBasic() error } diff --git a/modules/core/exported/commitment.go b/modules/core/exported/commitment.go index 99f304e2792..904910e44e9 100644 --- a/modules/core/exported/commitment.go +++ b/modules/core/exported/commitment.go @@ -1,6 +1,6 @@ package exported -import ics23 "github.com/confio/ics23/go" +import ics23 "github.com/cosmos/ics23/go" // ICS 023 Types Implementation // @@ -35,7 +35,7 @@ type Path interface { // Proof implements spec:CommitmentProof. // Proof can prove whether the key-value pair is a part of the Root or not. // Each proof has designated key-value pair it is able to prove. -// Proofs includes key but value is provided dynamically at the verification time. +// Proofs include key but value is provided dynamically at the verification time. type Proof interface { VerifyMembership([]*ics23.ProofSpec, Root, Path, []byte) error VerifyNonMembership([]*ics23.ProofSpec, Root, Path) error diff --git a/modules/core/exported/connection.go b/modules/core/exported/connection.go index 8f705daff1a..93129050db2 100644 --- a/modules/core/exported/connection.go +++ b/modules/core/exported/connection.go @@ -5,7 +5,6 @@ type ConnectionI interface { GetClientID() string GetState() int32 GetCounterparty() CounterpartyConnectionI - GetVersions() []Version GetDelayPeriod() uint64 ValidateBasic() error } diff --git a/modules/core/exported/expected_keepers.go b/modules/core/exported/expected_keepers.go new file mode 100644 index 00000000000..32bac1a00fb --- /dev/null +++ b/modules/core/exported/expected_keepers.go @@ -0,0 +1,15 @@ +package exported + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" +) + +// ScopedKeeper defines the expected x/capability scoped keeper interface +type ScopedKeeper interface { + NewCapability(ctx sdk.Context, name string) (*capabilitytypes.Capability, error) + GetCapability(ctx sdk.Context, name string) (*capabilitytypes.Capability, bool) + AuthenticateCapability(ctx sdk.Context, cap *capabilitytypes.Capability, name string) bool + LookupModules(ctx sdk.Context, name string) ([]string, *capabilitytypes.Capability, error) + ClaimCapability(ctx sdk.Context, cap *capabilitytypes.Capability, name string) error +} diff --git a/modules/core/24-host/keys.go b/modules/core/exported/module.go similarity index 68% rename from modules/core/24-host/keys.go rename to modules/core/exported/module.go index 090030b6479..5af7d78b7a9 100644 --- a/modules/core/24-host/keys.go +++ b/modules/core/exported/module.go @@ -1,15 +1,12 @@ -package host +package exported const ( // ModuleName is the name of the IBC module ModuleName = "ibc" - // StoreKey is the string store representation - StoreKey string = ModuleName - + StoreKey = ModuleName // QuerierRoute is the querier route for the IBC module - QuerierRoute string = ModuleName - + QuerierRoute = ModuleName // RouterKey is the msg router key for the IBC module - RouterKey string = ModuleName + RouterKey = ModuleName ) diff --git a/modules/core/genesis.go b/modules/core/genesis.go index 7f9f65fa46f..42661193665 100644 --- a/modules/core/genesis.go +++ b/modules/core/genesis.go @@ -3,16 +3,16 @@ package ibc import ( sdk "github.com/cosmos/cosmos-sdk/types" - client "github.com/cosmos/ibc-go/v4/modules/core/02-client" - connection "github.com/cosmos/ibc-go/v4/modules/core/03-connection" - channel "github.com/cosmos/ibc-go/v4/modules/core/04-channel" - "github.com/cosmos/ibc-go/v4/modules/core/keeper" - "github.com/cosmos/ibc-go/v4/modules/core/types" + client "github.com/cosmos/ibc-go/v7/modules/core/02-client" + connection "github.com/cosmos/ibc-go/v7/modules/core/03-connection" + channel "github.com/cosmos/ibc-go/v7/modules/core/04-channel" + "github.com/cosmos/ibc-go/v7/modules/core/keeper" + "github.com/cosmos/ibc-go/v7/modules/core/types" ) // InitGenesis initializes the ibc state from a provided genesis // state. -func InitGenesis(ctx sdk.Context, k keeper.Keeper, createLocalhost bool, gs *types.GenesisState) { +func InitGenesis(ctx sdk.Context, k keeper.Keeper, gs *types.GenesisState) { client.InitGenesis(ctx, k.ClientKeeper, gs.ClientGenesis) connection.InitGenesis(ctx, k.ConnectionKeeper, gs.ConnectionGenesis) channel.InitGenesis(ctx, k.ChannelKeeper, gs.ChannelGenesis) diff --git a/modules/core/genesis_test.go b/modules/core/genesis_test.go index cf0cf19efb0..215383fe09d 100644 --- a/modules/core/genesis_test.go +++ b/modules/core/genesis_test.go @@ -8,25 +8,23 @@ import ( "github.com/stretchr/testify/suite" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" - ibc "github.com/cosmos/ibc-go/v4/modules/core" - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - connectiontypes "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types" - channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" - commitmenttypes "github.com/cosmos/ibc-go/v4/modules/core/23-commitment/types" - "github.com/cosmos/ibc-go/v4/modules/core/exported" - "github.com/cosmos/ibc-go/v4/modules/core/types" - ibctmtypes "github.com/cosmos/ibc-go/v4/modules/light-clients/07-tendermint/types" - localhosttypes "github.com/cosmos/ibc-go/v4/modules/light-clients/09-localhost/types" - ibctesting "github.com/cosmos/ibc-go/v4/testing" - "github.com/cosmos/ibc-go/v4/testing/simapp" + ibc "github.com/cosmos/ibc-go/v7/modules/core" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + connectiontypes "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + commitmenttypes "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/types" + "github.com/cosmos/ibc-go/v7/modules/core/exported" + "github.com/cosmos/ibc-go/v7/modules/core/types" + ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" + ibctesting "github.com/cosmos/ibc-go/v7/testing" + "github.com/cosmos/ibc-go/v7/testing/simapp" ) const ( connectionID = "connection-0" clientID = "07-tendermint-0" connectionID2 = "connection-1" - clientID2 = "07-tendermin-1" - localhostID = exported.Localhost + "-1" + clientID2 = "07-tendermint-1" port1 = "firstport" port2 = "secondport" @@ -35,7 +33,7 @@ const ( channel2 = "channel-1" ) -var clientHeight = clienttypes.NewHeight(0, 10) +var clientHeight = clienttypes.NewHeight(1, 10) type IBCTestSuite struct { suite.Suite @@ -77,10 +75,7 @@ func (suite *IBCTestSuite) TestValidateGenesis() { ClientGenesis: clienttypes.NewGenesisState( []clienttypes.IdentifiedClientState{ clienttypes.NewIdentifiedClientState( - clientID, ibctmtypes.NewClientState(suite.chainA.ChainID, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), - ), - clienttypes.NewIdentifiedClientState( - localhostID, localhosttypes.NewClientState("chaindID", clientHeight), + clientID, ibctm.NewClientState(suite.chainA.ChainID, ibctm.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath), ), }, []clienttypes.ClientConsensusStates{ @@ -89,7 +84,7 @@ func (suite *IBCTestSuite) TestValidateGenesis() { []clienttypes.ConsensusStateWithHeight{ clienttypes.NewConsensusStateWithHeight( header.GetHeight().(clienttypes.Height), - ibctmtypes.NewConsensusState( + ibctm.NewConsensusState( header.GetTime(), commitmenttypes.NewMerkleRoot(header.Header.AppHash), header.Header.NextValidatorsHash, ), ), @@ -105,8 +100,8 @@ func (suite *IBCTestSuite) TestValidateGenesis() { }, ), }, - clienttypes.NewParams(exported.Tendermint, exported.Localhost), - true, + clienttypes.NewParams(exported.Tendermint), + false, 2, ), ConnectionGenesis: connectiontypes.NewGenesisState( @@ -157,10 +152,7 @@ func (suite *IBCTestSuite) TestValidateGenesis() { ClientGenesis: clienttypes.NewGenesisState( []clienttypes.IdentifiedClientState{ clienttypes.NewIdentifiedClientState( - clientID, ibctmtypes.NewClientState(suite.chainA.ChainID, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), - ), - clienttypes.NewIdentifiedClientState( - localhostID, localhosttypes.NewClientState("(chaindID)", clienttypes.ZeroHeight()), + clientID, ibctm.NewClientState(suite.chainA.ChainID, ibctm.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath), ), }, nil, @@ -241,10 +233,7 @@ func (suite *IBCTestSuite) TestInitGenesis() { ClientGenesis: clienttypes.NewGenesisState( []clienttypes.IdentifiedClientState{ clienttypes.NewIdentifiedClientState( - clientID, ibctmtypes.NewClientState(suite.chainA.ChainID, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), - ), - clienttypes.NewIdentifiedClientState( - exported.Localhost, localhosttypes.NewClientState("chaindID", clientHeight), + clientID, ibctm.NewClientState(suite.chainA.ChainID, ibctm.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath), ), }, []clienttypes.ClientConsensusStates{ @@ -253,7 +242,7 @@ func (suite *IBCTestSuite) TestInitGenesis() { []clienttypes.ConsensusStateWithHeight{ clienttypes.NewConsensusStateWithHeight( header.GetHeight().(clienttypes.Height), - ibctmtypes.NewConsensusState( + ibctm.NewConsensusState( header.GetTime(), commitmenttypes.NewMerkleRoot(header.Header.AppHash), header.Header.NextValidatorsHash, ), ), @@ -269,8 +258,8 @@ func (suite *IBCTestSuite) TestInitGenesis() { }, ), }, - clienttypes.NewParams(exported.Tendermint, exported.Localhost), - true, + clienttypes.NewParams(exported.Tendermint), + false, 0, ), ConnectionGenesis: connectiontypes.NewGenesisState( @@ -320,7 +309,7 @@ func (suite *IBCTestSuite) TestInitGenesis() { app := simapp.Setup(false) suite.NotPanics(func() { - ibc.InitGenesis(app.BaseApp.NewContext(false, tmproto.Header{Height: 1}), *app.IBCKeeper, true, tc.genState) + ibc.InitGenesis(app.BaseApp.NewContext(false, tmproto.Header{Height: 1}), *app.IBCKeeper, tc.genState) }) } } @@ -355,7 +344,7 @@ func (suite *IBCTestSuite) TestExportGenesis() { // init genesis based on export suite.NotPanics(func() { - ibc.InitGenesis(suite.chainA.GetContext(), *suite.chainA.App.GetIBCKeeper(), true, gs) + ibc.InitGenesis(suite.chainA.GetContext(), *suite.chainA.App.GetIBCKeeper(), gs) }) suite.NotPanics(func() { @@ -366,7 +355,7 @@ func (suite *IBCTestSuite) TestExportGenesis() { // init genesis based on marshal and unmarshal suite.NotPanics(func() { - ibc.InitGenesis(suite.chainA.GetContext(), *suite.chainA.App.GetIBCKeeper(), true, gs) + ibc.InitGenesis(suite.chainA.GetContext(), *suite.chainA.App.GetIBCKeeper(), gs) }) }) } diff --git a/modules/core/keeper/grpc_query.go b/modules/core/keeper/grpc_query.go index c42d7857616..a0d57ad36ac 100644 --- a/modules/core/keeper/grpc_query.go +++ b/modules/core/keeper/grpc_query.go @@ -3,9 +3,9 @@ package keeper import ( "context" - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - connectiontypes "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types" - channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + connectiontypes "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" ) // ClientState implements the IBC QueryServer interface @@ -48,6 +48,11 @@ func (q Keeper) UpgradedClientState(c context.Context, req *clienttypes.QueryUpg return q.ClientKeeper.UpgradedClientState(c, req) } +// UpgradedConsensusState implements the IBC QueryServer interface +func (q Keeper) UpgradedConsensusState(c context.Context, req *clienttypes.QueryUpgradedConsensusStateRequest) (*clienttypes.QueryUpgradedConsensusStateResponse, error) { + return q.ClientKeeper.UpgradedConsensusState(c, req) +} + // Connection implements the IBC QueryServer interface func (q Keeper) Connection(c context.Context, req *connectiontypes.QueryConnectionRequest) (*connectiontypes.QueryConnectionResponse, error) { return q.ConnectionKeeper.Connection(c, req) @@ -73,6 +78,11 @@ func (q Keeper) ConnectionConsensusState(c context.Context, req *connectiontypes return q.ConnectionKeeper.ConnectionConsensusState(c, req) } +// ConnectionParams implements the IBC QueryServer interface +func (q Keeper) ConnectionParams(c context.Context, req *connectiontypes.QueryConnectionParamsRequest) (*connectiontypes.QueryConnectionParamsResponse, error) { + return q.ConnectionKeeper.ConnectionParams(c, req) +} + // Channel implements the IBC QueryServer interface func (q Keeper) Channel(c context.Context, req *channeltypes.QueryChannelRequest) (*channeltypes.QueryChannelResponse, error) { return q.ChannelKeeper.Channel(c, req) diff --git a/modules/core/keeper/keeper.go b/modules/core/keeper/keeper.go index 5ae7f4e40e7..a887b845bf4 100644 --- a/modules/core/keeper/keeper.go +++ b/modules/core/keeper/keeper.go @@ -5,18 +5,18 @@ import ( "reflect" "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" + storetypes "github.com/cosmos/cosmos-sdk/store/types" capabilitykeeper "github.com/cosmos/cosmos-sdk/x/capability/keeper" paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" - clientkeeper "github.com/cosmos/ibc-go/v4/modules/core/02-client/keeper" - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - connectionkeeper "github.com/cosmos/ibc-go/v4/modules/core/03-connection/keeper" - connectiontypes "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types" - channelkeeper "github.com/cosmos/ibc-go/v4/modules/core/04-channel/keeper" - portkeeper "github.com/cosmos/ibc-go/v4/modules/core/05-port/keeper" - porttypes "github.com/cosmos/ibc-go/v4/modules/core/05-port/types" - "github.com/cosmos/ibc-go/v4/modules/core/types" + clientkeeper "github.com/cosmos/ibc-go/v7/modules/core/02-client/keeper" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + connectionkeeper "github.com/cosmos/ibc-go/v7/modules/core/03-connection/keeper" + connectiontypes "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" + channelkeeper "github.com/cosmos/ibc-go/v7/modules/core/04-channel/keeper" + portkeeper "github.com/cosmos/ibc-go/v7/modules/core/05-port/keeper" + porttypes "github.com/cosmos/ibc-go/v7/modules/core/05-port/types" + "github.com/cosmos/ibc-go/v7/modules/core/types" ) var _ types.QueryServer = (*Keeper)(nil) @@ -37,7 +37,7 @@ type Keeper struct { // NewKeeper creates a new ibc Keeper func NewKeeper( - cdc codec.BinaryCodec, key sdk.StoreKey, paramSpace paramtypes.Subspace, + cdc codec.BinaryCodec, key storetypes.StoreKey, paramSpace paramtypes.Subspace, stakingKeeper clienttypes.StakingKeeper, upgradeKeeper clienttypes.UpgradeKeeper, scopedKeeper capabilitykeeper.ScopedKeeper, ) *Keeper { @@ -50,11 +50,10 @@ func NewKeeper( } // panic if any of the keepers passed in is empty - if reflect.ValueOf(stakingKeeper).IsZero() { + if isEmpty(stakingKeeper) { panic(fmt.Errorf("cannot initialize IBC keeper: empty staking keeper")) } - - if reflect.ValueOf(upgradeKeeper).IsZero() { + if isEmpty(upgradeKeeper) { panic(fmt.Errorf("cannot initialize IBC keeper: empty upgrade keeper")) } @@ -92,3 +91,19 @@ func (k *Keeper) SetRouter(rtr *porttypes.Router) { k.Router = rtr k.Router.Seal() } + +// isEmpty checks if the interface is an empty struct or a pointer pointing +// to an empty struct +func isEmpty(keeper interface{}) bool { + switch reflect.TypeOf(keeper).Kind() { + case reflect.Ptr: + if reflect.ValueOf(keeper).Elem().IsZero() { + return true + } + default: + if reflect.ValueOf(keeper).IsZero() { + return true + } + } + return false +} diff --git a/modules/core/keeper/keeper_test.go b/modules/core/keeper/keeper_test.go index ec808bdc0e0..60dd960ccba 100644 --- a/modules/core/keeper/keeper_test.go +++ b/modules/core/keeper/keeper_test.go @@ -6,16 +6,15 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" capabilitykeeper "github.com/cosmos/cosmos-sdk/x/capability/keeper" - "github.com/stretchr/testify/suite" - stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" upgradekeeper "github.com/cosmos/cosmos-sdk/x/upgrade/keeper" + "github.com/stretchr/testify/suite" - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - ibchost "github.com/cosmos/ibc-go/v4/modules/core/24-host" - ibckeeper "github.com/cosmos/ibc-go/v4/modules/core/keeper" - ibctesting "github.com/cosmos/ibc-go/v4/testing" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + ibcexported "github.com/cosmos/ibc-go/v7/modules/core/exported" + ibckeeper "github.com/cosmos/ibc-go/v7/modules/core/keeper" + ibctesting "github.com/cosmos/ibc-go/v7/testing" ) type KeeperTestSuite struct { @@ -60,14 +59,14 @@ func (d MockStakingKeeper) UnbondingTime(ctx sdk.Context) time.Duration { // It verifies if ibckeeper.NewKeeper panic when any of the keepers passed in is empty. func (suite *KeeperTestSuite) TestNewKeeper() { var ( - stakingKeeper clienttypes.StakingKeeper - upgradeKeeper clienttypes.UpgradeKeeper - scopedKeeper capabilitykeeper.ScopedKeeper - newIBCKeeper = func() { + stakingKeeper clienttypes.StakingKeeper + upgradeKeeper clienttypes.UpgradeKeeper + scopedKeeper capabilitykeeper.ScopedKeeper + newIBCKeeperFn = func() { ibckeeper.NewKeeper( suite.chainA.GetSimApp().AppCodec(), - suite.chainA.GetSimApp().GetKey(ibchost.StoreKey), - suite.chainA.GetSimApp().GetSubspace(ibchost.ModuleName), + suite.chainA.GetSimApp().GetKey(ibcexported.StoreKey), + suite.chainA.GetSimApp().GetSubspace(ibcexported.ModuleName), stakingKeeper, upgradeKeeper, scopedKeeper, @@ -80,10 +79,15 @@ func (suite *KeeperTestSuite) TestNewKeeper() { malleate func() expPass bool }{ - {"failure: empty staking keeper", func() { - emptyStakingKeeper := stakingkeeper.Keeper{} + {"failure: empty staking keeper value", func() { + emptyStakingKeeperValue := stakingkeeper.Keeper{} - stakingKeeper = emptyStakingKeeper + stakingKeeper = emptyStakingKeeperValue + }, false}, + {"failure: empty staking keeper pointer", func() { + emptyStakingKeeperPointer := &stakingkeeper.Keeper{} + + stakingKeeper = emptyStakingKeeperPointer }, false}, {"failure: empty mock staking keeper", func() { // use a different implementation of clienttypes.StakingKeeper @@ -91,10 +95,15 @@ func (suite *KeeperTestSuite) TestNewKeeper() { stakingKeeper = emptyMockStakingKeeper }, false}, - {"failure: empty upgrade keeper", func() { - emptyUpgradeKeeper := upgradekeeper.Keeper{} + {"failure: empty upgrade keeper value", func() { + emptyUpgradeKeeperValue := upgradekeeper.Keeper{} + + upgradeKeeper = emptyUpgradeKeeperValue + }, false}, + {"failure: empty upgrade keeper pointer", func() { + emptyUpgradeKeeperPointer := &upgradekeeper.Keeper{} - upgradeKeeper = emptyUpgradeKeeper + upgradeKeeper = emptyUpgradeKeeperPointer }, false}, {"failure: empty scoped keeper", func() { emptyScopedKeeper := capabilitykeeper.ScopedKeeper{} @@ -122,11 +131,11 @@ func (suite *KeeperTestSuite) TestNewKeeper() { if tc.expPass { suite.Require().NotPanics( - newIBCKeeper, + newIBCKeeperFn, ) } else { suite.Require().Panics( - newIBCKeeper, + newIBCKeeperFn, ) } }) diff --git a/modules/core/keeper/migrations.go b/modules/core/keeper/migrations.go index 5a79c8399ec..423429092c7 100644 --- a/modules/core/keeper/migrations.go +++ b/modules/core/keeper/migrations.go @@ -3,7 +3,7 @@ package keeper import ( sdk "github.com/cosmos/cosmos-sdk/types" - clientkeeper "github.com/cosmos/ibc-go/v4/modules/core/02-client/keeper" + clientkeeper "github.com/cosmos/ibc-go/v7/modules/core/02-client/keeper" ) // Migrator is a struct for handling in-place store migrations. @@ -16,15 +16,10 @@ func NewMigrator(keeper Keeper) Migrator { return Migrator{keeper: keeper} } -// Migrate1to2 migrates from version 1 to 2. -// This migration prunes: -// - migrates solo machine client state from protobuf definition v1 to v2 -// - prunes solo machine consensus states -// - prunes expired tendermint consensus states -// - adds ProcessedHeight and Iteration keys for unexpired tendermint consensus states -func (m Migrator) Migrate1to2(ctx sdk.Context) error { +// Migrate2to3 migrates from version 2 to 3. See 02-client keeper function Migrate2to3. +func (m Migrator) Migrate2to3(ctx sdk.Context) error { clientMigrator := clientkeeper.NewMigrator(m.keeper.ClientKeeper) - if err := clientMigrator.Migrate1to2(ctx); err != nil { + if err := clientMigrator.Migrate2to3(ctx); err != nil { return err } diff --git a/modules/core/keeper/msg_server.go b/modules/core/keeper/msg_server.go index b08edaf41a2..01d93e17f03 100644 --- a/modules/core/keeper/msg_server.go +++ b/modules/core/keeper/msg_server.go @@ -3,17 +3,16 @@ package keeper import ( "context" + errorsmod "cosmossdk.io/errors" metrics "github.com/armon/go-metrics" - "github.com/cosmos/cosmos-sdk/telemetry" sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - connectiontypes "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types" - channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" - porttypes "github.com/cosmos/ibc-go/v4/modules/core/05-port/types" - coretypes "github.com/cosmos/ibc-go/v4/modules/core/types" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + connectiontypes "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + porttypes "github.com/cosmos/ibc-go/v7/modules/core/05-port/types" + coretypes "github.com/cosmos/ibc-go/v7/modules/core/types" ) var ( @@ -47,12 +46,12 @@ func (k Keeper) CreateClient(goCtx context.Context, msg *clienttypes.MsgCreateCl func (k Keeper) UpdateClient(goCtx context.Context, msg *clienttypes.MsgUpdateClient) (*clienttypes.MsgUpdateClientResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) - header, err := clienttypes.UnpackHeader(msg.Header) + clientMsg, err := clienttypes.UnpackClientMessage(msg.ClientMessage) if err != nil { return nil, err } - if err = k.ClientKeeper.UpdateClient(ctx, msg.ClientId, header); err != nil { + if err = k.ClientKeeper.UpdateClient(ctx, msg.ClientId, clientMsg); err != nil { return nil, err } @@ -81,16 +80,18 @@ func (k Keeper) UpgradeClient(goCtx context.Context, msg *clienttypes.MsgUpgrade } // SubmitMisbehaviour defines a rpc handler method for MsgSubmitMisbehaviour. +// Warning: DEPRECATED +// This handler is redudant as `MsgUpdateClient` is now capable of handling both a Header and a Misbehaviour func (k Keeper) SubmitMisbehaviour(goCtx context.Context, msg *clienttypes.MsgSubmitMisbehaviour) (*clienttypes.MsgSubmitMisbehaviourResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) - misbehaviour, err := clienttypes.UnpackMisbehaviour(msg.Misbehaviour) + misbehaviour, err := clienttypes.UnpackClientMessage(msg.Misbehaviour) if err != nil { return nil, err } - if err := k.ClientKeeper.CheckMisbehaviourAndUpdateState(ctx, misbehaviour); err != nil { - return nil, sdkerrors.Wrap(err, "failed to process misbehaviour for IBC client") + if err = k.ClientKeeper.UpdateClient(ctx, msg.ClientId, misbehaviour); err != nil { + return nil, err } return &clienttypes.MsgSubmitMisbehaviourResponse{}, nil @@ -101,7 +102,7 @@ func (k Keeper) ConnectionOpenInit(goCtx context.Context, msg *connectiontypes.M ctx := sdk.UnwrapSDKContext(goCtx) if _, err := k.ConnectionKeeper.ConnOpenInit(ctx, msg.ClientId, msg.Counterparty, msg.Version, msg.DelayPeriod); err != nil { - return nil, sdkerrors.Wrap(err, "connection handshake open init failed") + return nil, errorsmod.Wrap(err, "connection handshake open init failed") } return &connectiontypes.MsgConnectionOpenInitResponse{}, nil @@ -121,7 +122,7 @@ func (k Keeper) ConnectionOpenTry(goCtx context.Context, msg *connectiontypes.Ms connectiontypes.ProtoVersionsToExported(msg.CounterpartyVersions), msg.ProofInit, msg.ProofClient, msg.ProofConsensus, msg.ProofHeight, msg.ConsensusHeight, ); err != nil { - return nil, sdkerrors.Wrap(err, "connection handshake open try failed") + return nil, errorsmod.Wrap(err, "connection handshake open try failed") } return &connectiontypes.MsgConnectionOpenTryResponse{}, nil @@ -140,7 +141,7 @@ func (k Keeper) ConnectionOpenAck(goCtx context.Context, msg *connectiontypes.Ms msg.ProofTry, msg.ProofClient, msg.ProofConsensus, msg.ProofHeight, msg.ConsensusHeight, ); err != nil { - return nil, sdkerrors.Wrap(err, "connection handshake open ack failed") + return nil, errorsmod.Wrap(err, "connection handshake open ack failed") } return &connectiontypes.MsgConnectionOpenAckResponse{}, nil @@ -153,7 +154,7 @@ func (k Keeper) ConnectionOpenConfirm(goCtx context.Context, msg *connectiontype if err := k.ConnectionKeeper.ConnOpenConfirm( ctx, msg.ConnectionId, msg.ProofAck, msg.ProofHeight, ); err != nil { - return nil, sdkerrors.Wrap(err, "connection handshake open confirm failed") + return nil, errorsmod.Wrap(err, "connection handshake open confirm failed") } return &connectiontypes.MsgConnectionOpenConfirmResponse{}, nil @@ -168,13 +169,15 @@ func (k Keeper) ChannelOpenInit(goCtx context.Context, msg *channeltypes.MsgChan // Lookup module by port capability module, portCap, err := k.PortKeeper.LookupModuleByPort(ctx, msg.PortId) if err != nil { - return nil, sdkerrors.Wrap(err, "could not retrieve module from port-id") + ctx.Logger().Error("channel open init callback failed", "port-id", msg.PortId, "error", errorsmod.Wrap(err, "could not retrieve module from port-id")) + return nil, errorsmod.Wrap(err, "could not retrieve module from port-id") } // Retrieve application callbacks from router cbs, ok := k.Router.GetRoute(module) if !ok { - return nil, sdkerrors.Wrapf(porttypes.ErrInvalidRoute, "route not found to module: %s", module) + ctx.Logger().Error("channel open init callback failed", "port-id", msg.PortId, "error", errorsmod.Wrapf(porttypes.ErrInvalidRoute, "route not found to module: %s", module)) + return nil, errorsmod.Wrapf(porttypes.ErrInvalidRoute, "route not found to module: %s", module) } // Perform 04-channel verification @@ -183,18 +186,22 @@ func (k Keeper) ChannelOpenInit(goCtx context.Context, msg *channeltypes.MsgChan portCap, msg.Channel.Counterparty, msg.Channel.Version, ) if err != nil { - return nil, sdkerrors.Wrap(err, "channel handshake open init failed") + ctx.Logger().Error("channel open init callback failed", "error", errorsmod.Wrap(err, "channel handshake open init failed")) + return nil, errorsmod.Wrap(err, "channel handshake open init failed") } // Perform application logic callback version, err := cbs.OnChanOpenInit(ctx, msg.Channel.Ordering, msg.Channel.ConnectionHops, msg.PortId, channelID, cap, msg.Channel.Counterparty, msg.Channel.Version) if err != nil { - return nil, sdkerrors.Wrap(err, "channel open init callback failed") + ctx.Logger().Error("channel open init callback failed", "port-id", msg.PortId, "channel-id", channelID, "error", err.Error()) + return nil, errorsmod.Wrapf(err, "channel open init callback failed for port ID: %s, channel ID: %s", msg.PortId, channelID) } // Write channel into state k.ChannelKeeper.WriteOpenInitChannel(ctx, msg.PortId, channelID, msg.Channel.Ordering, msg.Channel.ConnectionHops, msg.Channel.Counterparty, version) + ctx.Logger().Info("channel open init callback succeeded", "channel-id", channelID, "version", version) + return &channeltypes.MsgChannelOpenInitResponse{ ChannelId: channelID, Version: version, @@ -210,13 +217,15 @@ func (k Keeper) ChannelOpenTry(goCtx context.Context, msg *channeltypes.MsgChann // Lookup module by port capability module, portCap, err := k.PortKeeper.LookupModuleByPort(ctx, msg.PortId) if err != nil { - return nil, sdkerrors.Wrap(err, "could not retrieve module from port-id") + ctx.Logger().Error("channel open try callback failed", "port-id", msg.PortId, "error", errorsmod.Wrap(err, "could not retrieve module from port-id")) + return nil, errorsmod.Wrap(err, "could not retrieve module from port-id") } // Retrieve application callbacks from router cbs, ok := k.Router.GetRoute(module) if !ok { - return nil, sdkerrors.Wrapf(porttypes.ErrInvalidRoute, "route not found to module: %s", module) + ctx.Logger().Error("channel open try callback failed", "port-id", msg.PortId, "error", errorsmod.Wrapf(porttypes.ErrInvalidRoute, "route not found to module: %s", module)) + return nil, errorsmod.Wrapf(porttypes.ErrInvalidRoute, "route not found to module: %s", module) } // Perform 04-channel verification @@ -224,20 +233,25 @@ func (k Keeper) ChannelOpenTry(goCtx context.Context, msg *channeltypes.MsgChann portCap, msg.Channel.Counterparty, msg.CounterpartyVersion, msg.ProofInit, msg.ProofHeight, ) if err != nil { - return nil, sdkerrors.Wrap(err, "channel handshake open try failed") + ctx.Logger().Error("channel open try callback failed", "error", errorsmod.Wrap(err, "channel handshake open try failed")) + return nil, errorsmod.Wrap(err, "channel handshake open try failed") } // Perform application logic callback version, err := cbs.OnChanOpenTry(ctx, msg.Channel.Ordering, msg.Channel.ConnectionHops, msg.PortId, channelID, cap, msg.Channel.Counterparty, msg.CounterpartyVersion) if err != nil { - return nil, sdkerrors.Wrap(err, "channel open try callback failed") + ctx.Logger().Error("channel open try callback failed", "port-id", msg.PortId, "channel-id", channelID, "error", err.Error()) + return nil, errorsmod.Wrapf(err, "channel open try callback failed for port ID: %s, channel ID: %s", msg.PortId, channelID) } // Write channel into state k.ChannelKeeper.WriteOpenTryChannel(ctx, msg.PortId, channelID, msg.Channel.Ordering, msg.Channel.ConnectionHops, msg.Channel.Counterparty, version) + ctx.Logger().Info("channel open try callback succeeded", "channel-id", channelID, "port-id", msg.PortId, "version", version) + return &channeltypes.MsgChannelOpenTryResponse{ - Version: version, + ChannelId: channelID, + Version: version, }, nil } @@ -250,29 +264,35 @@ func (k Keeper) ChannelOpenAck(goCtx context.Context, msg *channeltypes.MsgChann // Lookup module by channel capability module, cap, err := k.ChannelKeeper.LookupModuleByChannel(ctx, msg.PortId, msg.ChannelId) if err != nil { - return nil, sdkerrors.Wrap(err, "could not retrieve module from port-id") + ctx.Logger().Error("channel open ack callback failed", "port-id", msg.PortId, "error", errorsmod.Wrap(err, "could not retrieve module from port-id")) + return nil, errorsmod.Wrap(err, "could not retrieve module from port-id") } // Retrieve application callbacks from router cbs, ok := k.Router.GetRoute(module) if !ok { - return nil, sdkerrors.Wrapf(porttypes.ErrInvalidRoute, "route not found to module: %s", module) + ctx.Logger().Error("channel open ack callback failed", "port-id", msg.PortId, "error", errorsmod.Wrapf(porttypes.ErrInvalidRoute, "route not found to module: %s", module)) + return nil, errorsmod.Wrapf(porttypes.ErrInvalidRoute, "route not found to module: %s", module) } // Perform 04-channel verification if err = k.ChannelKeeper.ChanOpenAck( ctx, msg.PortId, msg.ChannelId, cap, msg.CounterpartyVersion, msg.CounterpartyChannelId, msg.ProofTry, msg.ProofHeight, ); err != nil { - return nil, sdkerrors.Wrap(err, "channel handshake open ack failed") + ctx.Logger().Error("channel open ack callback failed", "error", err.Error()) + return nil, errorsmod.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") + ctx.Logger().Error("channel handshake open ack callback failed", "port-id", msg.PortId, "channel-id", msg.ChannelId, "error", err.Error()) + return nil, errorsmod.Wrapf(err, "channel open ack callback failed for port ID: %s, channel ID: %s", msg.PortId, msg.ChannelId) } - // Write channel into state - k.ChannelKeeper.WriteOpenAckChannel(ctx, msg.PortId, msg.ChannelId, msg.CounterpartyVersion, msg.CounterpartyChannelId) + ctx.Logger().Info("channel open ack callback succeeded", "channel-id", msg.ChannelId, "port-id", msg.PortId) return &channeltypes.MsgChannelOpenAckResponse{}, nil } @@ -286,27 +306,33 @@ func (k Keeper) ChannelOpenConfirm(goCtx context.Context, msg *channeltypes.MsgC // Lookup module by channel capability module, cap, err := k.ChannelKeeper.LookupModuleByChannel(ctx, msg.PortId, msg.ChannelId) if err != nil { - return nil, sdkerrors.Wrap(err, "could not retrieve module from port-id") + ctx.Logger().Error("channel open confirm callback failed", "port-id", msg.PortId, "error", errorsmod.Wrap(err, "could not retrieve module from port-id")) + return nil, errorsmod.Wrap(err, "could not retrieve module from port-id") } // Retrieve application callbacks from router cbs, ok := k.Router.GetRoute(module) if !ok { - return nil, sdkerrors.Wrapf(porttypes.ErrInvalidRoute, "route not found to module: %s", module) + ctx.Logger().Error("channel open confirm callback failed", "port-id", msg.PortId, "error", errorsmod.Wrapf(porttypes.ErrInvalidRoute, "route not found to module: %s", module)) + return nil, errorsmod.Wrapf(porttypes.ErrInvalidRoute, "route not found to module: %s", module) } // Perform 04-channel verification if err = k.ChannelKeeper.ChanOpenConfirm(ctx, msg.PortId, msg.ChannelId, cap, msg.ProofAck, msg.ProofHeight); err != nil { - return nil, sdkerrors.Wrap(err, "channel handshake open confirm failed") + ctx.Logger().Error("channel open confirm callback failed", "error", errorsmod.Wrap(err, "channel handshake open confirm failed")) + return nil, errorsmod.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") + ctx.Logger().Error("channel handshake open confirm callback failed", "port-id", msg.PortId, "channel-id", msg.ChannelId, "error", err.Error()) + return nil, errorsmod.Wrapf(err, "channel open confirm callback failed for port ID: %s, channel ID: %s", msg.PortId, msg.ChannelId) } - // Write channel into state - k.ChannelKeeper.WriteOpenConfirmChannel(ctx, msg.PortId, msg.ChannelId) + ctx.Logger().Info("channel open confirm callback succeeded", "channel-id", msg.ChannelId, "port-id", msg.PortId) return &channeltypes.MsgChannelOpenConfirmResponse{}, nil } @@ -317,24 +343,30 @@ func (k Keeper) ChannelCloseInit(goCtx context.Context, msg *channeltypes.MsgCha // Lookup module by channel capability module, cap, err := k.ChannelKeeper.LookupModuleByChannel(ctx, msg.PortId, msg.ChannelId) if err != nil { - return nil, sdkerrors.Wrap(err, "could not retrieve module from port-id") + ctx.Logger().Error("channel close init callback failed", "port-id", msg.PortId, "error", errorsmod.Wrap(err, "could not retrieve module from port-id")) + return nil, errorsmod.Wrap(err, "could not retrieve module from port-id") } // Retrieve callbacks from router cbs, ok := k.Router.GetRoute(module) if !ok { - return nil, sdkerrors.Wrapf(porttypes.ErrInvalidRoute, "route not found to module: %s", module) + ctx.Logger().Error("channel close init callback failed", "port-id", msg.PortId, "error", errorsmod.Wrapf(porttypes.ErrInvalidRoute, "route not found to module: %s", module)) + return nil, errorsmod.Wrapf(porttypes.ErrInvalidRoute, "route not found to module: %s", module) } if err = cbs.OnChanCloseInit(ctx, msg.PortId, msg.ChannelId); err != nil { - return nil, sdkerrors.Wrap(err, "channel close init callback failed") + ctx.Logger().Error("channel close init callback failed", "port-id", msg.PortId, "channel-id", msg.ChannelId, "error", err.Error()) + return nil, errorsmod.Wrapf(err, "channel close init callback failed for port ID: %s, channel ID: %s", msg.PortId, msg.ChannelId) } err = k.ChannelKeeper.ChanCloseInit(ctx, msg.PortId, msg.ChannelId, cap) if err != nil { - return nil, sdkerrors.Wrap(err, "channel handshake close init failed") + ctx.Logger().Error("channel handshake close init callback failed", "port-id", msg.PortId, "channel-id", msg.ChannelId, "error", err.Error()) + return nil, errorsmod.Wrap(err, "channel handshake close init failed") } + ctx.Logger().Info("channel close init callback succeeded", "channel-id", msg.ChannelId, "port-id", msg.PortId) + return &channeltypes.MsgChannelCloseInitResponse{}, nil } @@ -345,24 +377,30 @@ func (k Keeper) ChannelCloseConfirm(goCtx context.Context, msg *channeltypes.Msg // Lookup module by channel capability module, cap, err := k.ChannelKeeper.LookupModuleByChannel(ctx, msg.PortId, msg.ChannelId) if err != nil { - return nil, sdkerrors.Wrap(err, "could not retrieve module from port-id") + ctx.Logger().Error("channel close confirm callback failed", "port-id", msg.PortId, "error", errorsmod.Wrap(err, "could not retrieve module from port-id")) + return nil, errorsmod.Wrap(err, "could not retrieve module from port-id") } // Retrieve callbacks from router cbs, ok := k.Router.GetRoute(module) if !ok { - return nil, sdkerrors.Wrapf(porttypes.ErrInvalidRoute, "route not found to module: %s", module) + ctx.Logger().Error("channel close confirm callback failed", "port-id", msg.PortId, "error", errorsmod.Wrapf(porttypes.ErrInvalidRoute, "route not found to module: %s", module)) + return nil, errorsmod.Wrapf(porttypes.ErrInvalidRoute, "route not found to module: %s", module) } if err = cbs.OnChanCloseConfirm(ctx, msg.PortId, msg.ChannelId); err != nil { - return nil, sdkerrors.Wrap(err, "channel close confirm callback failed") + ctx.Logger().Error("channel close confirm callback failed", "port-id", msg.PortId, "channel-id", msg.ChannelId, "error", err.Error()) + return nil, errorsmod.Wrapf(err, "channel close confirm callback failed for port ID: %s, channel ID: %s", msg.PortId, msg.ChannelId) } err = k.ChannelKeeper.ChanCloseConfirm(ctx, msg.PortId, msg.ChannelId, cap, msg.ProofInit, msg.ProofHeight) if err != nil { - return nil, sdkerrors.Wrap(err, "channel handshake close confirm failed") + ctx.Logger().Error("channel handshake close confirm callback failed", "port-id", msg.PortId, "channel-id", msg.ChannelId, "error", err.Error()) + return nil, errorsmod.Wrap(err, "channel handshake close confirm failed") } + ctx.Logger().Info("channel close confirm callback succeeded", "channel-id", msg.ChannelId, "port-id", msg.PortId) + return &channeltypes.MsgChannelCloseConfirmResponse{}, nil } @@ -372,19 +410,22 @@ func (k Keeper) RecvPacket(goCtx context.Context, msg *channeltypes.MsgRecvPacke relayer, err := sdk.AccAddressFromBech32(msg.Signer) if err != nil { - return nil, sdkerrors.Wrap(err, "Invalid address for msg Signer") + ctx.Logger().Error("receive packet failed", "error", errorsmod.Wrap(err, "Invalid address for msg Signer")) + return nil, errorsmod.Wrap(err, "Invalid address for msg Signer") } // Lookup module by channel capability module, cap, err := k.ChannelKeeper.LookupModuleByChannel(ctx, msg.Packet.DestinationPort, msg.Packet.DestinationChannel) if err != nil { - return nil, sdkerrors.Wrap(err, "could not retrieve module from port-id") + ctx.Logger().Error("receive packet failed", "port-id", msg.Packet.SourcePort, "channel-id", msg.Packet.SourceChannel, "error", errorsmod.Wrap(err, "could not retrieve module from port-id")) + return nil, errorsmod.Wrap(err, "could not retrieve module from port-id") } // Retrieve callbacks from router cbs, ok := k.Router.GetRoute(module) if !ok { - return nil, sdkerrors.Wrapf(porttypes.ErrInvalidRoute, "route not found to module: %s", module) + ctx.Logger().Error("receive packet failed", "port-id", msg.Packet.SourcePort, "error", errorsmod.Wrapf(porttypes.ErrInvalidRoute, "route not found to module: %s", module)) + return nil, errorsmod.Wrapf(porttypes.ErrInvalidRoute, "route not found to module: %s", module) } // Perform TAO verification @@ -394,16 +435,15 @@ func (k Keeper) RecvPacket(goCtx context.Context, msg *channeltypes.MsgRecvPacke cacheCtx, writeFn := ctx.CacheContext() err = k.ChannelKeeper.RecvPacket(cacheCtx, cap, msg.Packet, msg.ProofCommitment, msg.ProofHeight) - // 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()) - switch err { case nil: writeFn() case channeltypes.ErrNoOpMsg: + // no-ops do not need event emission as they will be ignored return &channeltypes.MsgRecvPacketResponse{Result: channeltypes.NOOP}, nil default: - return nil, sdkerrors.Wrap(err, "receive packet verification failed") + ctx.Logger().Error("receive packet failed", "port-id", msg.Packet.SourcePort, "channel-id", msg.Packet.SourceChannel, "error", errorsmod.Wrap(err, "receive packet verification failed")) + return nil, errorsmod.Wrap(err, "receive packet verification failed") } // Perform application logic callback @@ -411,12 +451,13 @@ 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() + } else { + // NOTE: The context returned by CacheContext() refers to a new EventManager, so it needs to explicitly set events to the original context. + // Events should still be emitted from failed acks and asynchronous acks + ctx.EventManager().EmitEvents(cacheCtx.EventManager().Events()) } // Set packet acknowledgement only if the acknowledgement is not nil. @@ -441,6 +482,8 @@ func (k Keeper) RecvPacket(goCtx context.Context, msg *channeltypes.MsgRecvPacke ) }() + ctx.Logger().Info("receive packet callback succeeded", "port-id", msg.Packet.SourcePort, "channel-id", msg.Packet.SourceChannel, "result", channeltypes.SUCCESS.String()) + return &channeltypes.MsgRecvPacketResponse{Result: channeltypes.SUCCESS}, nil } @@ -450,19 +493,22 @@ func (k Keeper) Timeout(goCtx context.Context, msg *channeltypes.MsgTimeout) (*c relayer, err := sdk.AccAddressFromBech32(msg.Signer) if err != nil { - return nil, sdkerrors.Wrap(err, "Invalid address for msg Signer") + ctx.Logger().Error("timeout failed", "error", errorsmod.Wrap(err, "Invalid address for msg Signer")) + return nil, errorsmod.Wrap(err, "Invalid address for msg Signer") } // Lookup module by channel capability module, cap, err := k.ChannelKeeper.LookupModuleByChannel(ctx, msg.Packet.SourcePort, msg.Packet.SourceChannel) if err != nil { - return nil, sdkerrors.Wrap(err, "could not retrieve module from port-id") + ctx.Logger().Error("timeout failed", "port-id", msg.Packet.SourcePort, "channel-id", msg.Packet.SourceChannel, "error", errorsmod.Wrap(err, "could not retrieve module from port-id")) + return nil, errorsmod.Wrap(err, "could not retrieve module from port-id") } // Retrieve callbacks from router cbs, ok := k.Router.GetRoute(module) if !ok { - return nil, sdkerrors.Wrapf(porttypes.ErrInvalidRoute, "route not found to module: %s", module) + ctx.Logger().Error("timeout failed", "port-id", msg.Packet.SourcePort, "error", errorsmod.Wrapf(porttypes.ErrInvalidRoute, "route not found to module: %s", module)) + return nil, errorsmod.Wrapf(porttypes.ErrInvalidRoute, "route not found to module: %s", module) } // Perform TAO verification @@ -472,22 +518,22 @@ func (k Keeper) Timeout(goCtx context.Context, msg *channeltypes.MsgTimeout) (*c cacheCtx, writeFn := ctx.CacheContext() err = k.ChannelKeeper.TimeoutPacket(cacheCtx, msg.Packet, msg.ProofUnreceived, msg.ProofHeight, msg.NextSequenceRecv) - // 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()) - switch err { case nil: writeFn() case channeltypes.ErrNoOpMsg: + // no-ops do not need event emission as they will be ignored return &channeltypes.MsgTimeoutResponse{Result: channeltypes.NOOP}, nil default: - return nil, sdkerrors.Wrap(err, "timeout packet verification failed") + ctx.Logger().Error("timeout failed", "port-id", msg.Packet.SourcePort, "channel-id", msg.Packet.SourceChannel, "error", errorsmod.Wrap(err, "timeout packet verification failed")) + return nil, errorsmod.Wrap(err, "timeout packet verification failed") } // Perform application logic callback err = cbs.OnTimeoutPacket(ctx, msg.Packet, relayer) if err != nil { - return nil, sdkerrors.Wrap(err, "timeout packet callback failed") + ctx.Logger().Error("timeout failed", "port-id", msg.Packet.SourcePort, "channel-id", msg.Packet.SourceChannel, "error", errorsmod.Wrap(err, "timeout packet callback failed")) + return nil, errorsmod.Wrap(err, "timeout packet callback failed") } // Delete packet commitment @@ -509,6 +555,8 @@ func (k Keeper) Timeout(goCtx context.Context, msg *channeltypes.MsgTimeout) (*c ) }() + ctx.Logger().Info("timeout packet callback succeeded", "port-id", msg.Packet.SourcePort, "channel-id", msg.Packet.SourceChannel, "result", channeltypes.SUCCESS.String()) + return &channeltypes.MsgTimeoutResponse{Result: channeltypes.SUCCESS}, nil } @@ -518,19 +566,22 @@ func (k Keeper) TimeoutOnClose(goCtx context.Context, msg *channeltypes.MsgTimeo relayer, err := sdk.AccAddressFromBech32(msg.Signer) if err != nil { - return nil, sdkerrors.Wrap(err, "Invalid address for msg Signer") + ctx.Logger().Error("timeout on close failed", "error", errorsmod.Wrap(err, "Invalid address for msg Signer")) + return nil, errorsmod.Wrap(err, "Invalid address for msg Signer") } // Lookup module by channel capability module, cap, err := k.ChannelKeeper.LookupModuleByChannel(ctx, msg.Packet.SourcePort, msg.Packet.SourceChannel) if err != nil { - return nil, sdkerrors.Wrap(err, "could not retrieve module from port-id") + ctx.Logger().Error("timeout on close failed", "port-id", msg.Packet.SourcePort, "channel-id", msg.Packet.SourceChannel, "error", errorsmod.Wrap(err, "could not retrieve module from port-id")) + return nil, errorsmod.Wrap(err, "could not retrieve module from port-id") } // Retrieve callbacks from router cbs, ok := k.Router.GetRoute(module) if !ok { - return nil, sdkerrors.Wrapf(porttypes.ErrInvalidRoute, "route not found to module: %s", module) + ctx.Logger().Error("timeout on close failed", "port-id", msg.Packet.SourcePort, "error", errorsmod.Wrapf(porttypes.ErrInvalidRoute, "route not found to module: %s", module)) + return nil, errorsmod.Wrapf(porttypes.ErrInvalidRoute, "route not found to module: %s", module) } // Perform TAO verification @@ -540,16 +591,15 @@ func (k Keeper) TimeoutOnClose(goCtx context.Context, msg *channeltypes.MsgTimeo cacheCtx, writeFn := ctx.CacheContext() err = k.ChannelKeeper.TimeoutOnClose(cacheCtx, cap, msg.Packet, msg.ProofUnreceived, msg.ProofClose, msg.ProofHeight, msg.NextSequenceRecv) - // 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()) - switch err { case nil: writeFn() case channeltypes.ErrNoOpMsg: + // no-ops do not need event emission as they will be ignored return &channeltypes.MsgTimeoutOnCloseResponse{Result: channeltypes.NOOP}, nil default: - return nil, sdkerrors.Wrap(err, "timeout on close packet verification failed") + ctx.Logger().Error("timeout on close failed", "port-id", msg.Packet.SourcePort, "channel-id", msg.Packet.SourceChannel, "error", errorsmod.Wrap(err, "timeout on close packet verification failed")) + return nil, errorsmod.Wrap(err, "timeout on close packet verification failed") } // Perform application logic callback @@ -558,7 +608,8 @@ func (k Keeper) TimeoutOnClose(goCtx context.Context, msg *channeltypes.MsgTimeo // application logic callback. err = cbs.OnTimeoutPacket(ctx, msg.Packet, relayer) if err != nil { - return nil, sdkerrors.Wrap(err, "timeout packet callback failed") + ctx.Logger().Error("timeout on close failed", "port-id", msg.Packet.SourcePort, "channel-id", msg.Packet.SourceChannel, "error", errorsmod.Wrap(err, "timeout packet callback failed")) + return nil, errorsmod.Wrap(err, "timeout packet callback failed") } // Delete packet commitment @@ -580,6 +631,8 @@ func (k Keeper) TimeoutOnClose(goCtx context.Context, msg *channeltypes.MsgTimeo ) }() + ctx.Logger().Info("timeout on close callback succeeded", "port-id", msg.Packet.SourcePort, "channel-id", msg.Packet.SourceChannel, "result", channeltypes.SUCCESS.String()) + return &channeltypes.MsgTimeoutOnCloseResponse{Result: channeltypes.SUCCESS}, nil } @@ -589,19 +642,22 @@ func (k Keeper) Acknowledgement(goCtx context.Context, msg *channeltypes.MsgAckn relayer, err := sdk.AccAddressFromBech32(msg.Signer) if err != nil { - return nil, sdkerrors.Wrap(err, "Invalid address for msg Signer") + ctx.Logger().Error("acknowledgement failed", "error", errorsmod.Wrap(err, "Invalid address for msg Signer")) + return nil, errorsmod.Wrap(err, "Invalid address for msg Signer") } // Lookup module by channel capability module, cap, err := k.ChannelKeeper.LookupModuleByChannel(ctx, msg.Packet.SourcePort, msg.Packet.SourceChannel) if err != nil { - return nil, sdkerrors.Wrap(err, "could not retrieve module from port-id") + ctx.Logger().Error("acknowledgement failed", "port-id", msg.Packet.SourcePort, "channel-id", msg.Packet.SourceChannel, "error", errorsmod.Wrap(err, "could not retrieve module from port-id")) + return nil, errorsmod.Wrap(err, "could not retrieve module from port-id") } // Retrieve callbacks from router cbs, ok := k.Router.GetRoute(module) if !ok { - return nil, sdkerrors.Wrapf(porttypes.ErrInvalidRoute, "route not found to module: %s", module) + ctx.Logger().Error("acknowledgement failed", "port-id", msg.Packet.SourcePort, "error", errorsmod.Wrapf(porttypes.ErrInvalidRoute, "route not found to module: %s", module)) + return nil, errorsmod.Wrapf(porttypes.ErrInvalidRoute, "route not found to module: %s", module) } // Perform TAO verification @@ -611,22 +667,22 @@ func (k Keeper) Acknowledgement(goCtx context.Context, msg *channeltypes.MsgAckn cacheCtx, writeFn := ctx.CacheContext() err = k.ChannelKeeper.AcknowledgePacket(cacheCtx, cap, msg.Packet, msg.Acknowledgement, msg.ProofAcked, msg.ProofHeight) - // 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()) - switch err { case nil: writeFn() case channeltypes.ErrNoOpMsg: + // no-ops do not need event emission as they will be ignored return &channeltypes.MsgAcknowledgementResponse{Result: channeltypes.NOOP}, nil default: - return nil, sdkerrors.Wrap(err, "acknowledge packet verification failed") + ctx.Logger().Error("acknowledgement failed", "port-id", msg.Packet.SourcePort, "channel-id", msg.Packet.SourceChannel, "error", errorsmod.Wrap(err, "acknowledge packet verification failed")) + return nil, errorsmod.Wrap(err, "acknowledge packet verification failed") } // Perform application logic callback err = cbs.OnAcknowledgementPacket(ctx, msg.Packet, msg.Acknowledgement, relayer) if err != nil { - return nil, sdkerrors.Wrap(err, "acknowledge packet callback failed") + ctx.Logger().Error("acknowledgement failed", "port-id", msg.Packet.SourcePort, "channel-id", msg.Packet.SourceChannel, "error", errorsmod.Wrap(err, "acknowledge packet callback failed")) + return nil, errorsmod.Wrap(err, "acknowledge packet callback failed") } defer func() { @@ -642,6 +698,8 @@ func (k Keeper) Acknowledgement(goCtx context.Context, msg *channeltypes.MsgAckn ) }() + ctx.Logger().Info("acknowledgement succeeded", "port-id", msg.Packet.SourcePort, "channel-id", msg.Packet.SourceChannel, "result", channeltypes.SUCCESS.String()) + return &channeltypes.MsgAcknowledgementResponse{Result: channeltypes.SUCCESS}, nil } diff --git a/modules/core/keeper/msg_server_test.go b/modules/core/keeper/msg_server_test.go index 3205c218bc7..b46dbcff4c8 100644 --- a/modules/core/keeper/msg_server_test.go +++ b/modules/core/keeper/msg_server_test.go @@ -4,19 +4,19 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/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" - commitmenttypes "github.com/cosmos/ibc-go/v4/modules/core/23-commitment/types" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" - "github.com/cosmos/ibc-go/v4/modules/core/exported" - "github.com/cosmos/ibc-go/v4/modules/core/keeper" - ibctmtypes "github.com/cosmos/ibc-go/v4/modules/light-clients/07-tendermint/types" - ibctesting "github.com/cosmos/ibc-go/v4/testing" - ibcmock "github.com/cosmos/ibc-go/v4/testing/mock" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + commitmenttypes "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + "github.com/cosmos/ibc-go/v7/modules/core/exported" + "github.com/cosmos/ibc-go/v7/modules/core/keeper" + ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" + ibctesting "github.com/cosmos/ibc-go/v7/testing" + ibcmock "github.com/cosmos/ibc-go/v7/testing/mock" ) var ( - timeoutHeight = clienttypes.NewHeight(0, 10000) + timeoutHeight = clienttypes.NewHeight(1, 10000) maxSequence = uint64(10) ) @@ -41,17 +41,19 @@ func (suite *KeeperTestSuite) TestHandleRecvPacket() { {"success: ORDERED", func() { path.SetChannelOrdered() suite.coordinator.Setup(path) - packet = channeltypes.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) - err := path.EndpointA.SendPacket(packet) + sequence, err := path.EndpointA.SendPacket(timeoutHeight, 0, ibctesting.MockPacketData) suite.Require().NoError(err) + + packet = channeltypes.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) }, true, false}, {"success: UNORDERED", func() { suite.coordinator.Setup(path) - packet = channeltypes.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) - err := path.EndpointA.SendPacket(packet) + sequence, err := path.EndpointA.SendPacket(timeoutHeight, 0, ibctesting.MockPacketData) suite.Require().NoError(err) + + packet = channeltypes.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) }, true, false}, {"success: UNORDERED out of order packet", func() { // setup uses an UNORDERED channel @@ -59,35 +61,38 @@ func (suite *KeeperTestSuite) TestHandleRecvPacket() { // attempts to receive packet with sequence 10 without receiving packet with sequence 1 for i := uint64(1); i < 10; i++ { - packet = channeltypes.NewPacket(ibctesting.MockPacketData, i, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) - - err := path.EndpointA.SendPacket(packet) + sequence, err := path.EndpointA.SendPacket(timeoutHeight, 0, ibctesting.MockPacketData) suite.Require().NoError(err) + + packet = channeltypes.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) } }, true, false}, {"success: OnRecvPacket callback returns revert=true", func() { suite.coordinator.Setup(path) - packet = channeltypes.NewPacket(ibctesting.MockFailPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) - err := path.EndpointA.SendPacket(packet) + sequence, err := path.EndpointA.SendPacket(timeoutHeight, 0, ibctesting.MockFailPacketData) suite.Require().NoError(err) + + packet = channeltypes.NewPacket(ibctesting.MockFailPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) }, true, true}, {"success: ORDERED - async acknowledgement", func() { path.SetChannelOrdered() suite.coordinator.Setup(path) async = true - packet = channeltypes.NewPacket(ibcmock.MockAsyncPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) - err := path.EndpointA.SendPacket(packet) + sequence, err := path.EndpointA.SendPacket(timeoutHeight, 0, ibcmock.MockAsyncPacketData) suite.Require().NoError(err) + + packet = channeltypes.NewPacket(ibcmock.MockAsyncPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) }, true, false}, {"success: UNORDERED - async acknowledgement", func() { suite.coordinator.Setup(path) async = true - packet = channeltypes.NewPacket(ibcmock.MockAsyncPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) - err := path.EndpointA.SendPacket(packet) + sequence, err := path.EndpointA.SendPacket(timeoutHeight, 0, ibcmock.MockAsyncPacketData) suite.Require().NoError(err) + + packet = channeltypes.NewPacket(ibcmock.MockAsyncPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) }, true, false}, {"failure: ORDERED out of order packet", func() { path.SetChannelOrdered() @@ -95,10 +100,10 @@ func (suite *KeeperTestSuite) TestHandleRecvPacket() { // attempts to receive packet with sequence 10 without receiving packet with sequence 1 for i := uint64(1); i < 10; i++ { - packet = channeltypes.NewPacket(ibctesting.MockPacketData, i, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) - - err := path.EndpointA.SendPacket(packet) + sequence, err := path.EndpointA.SendPacket(timeoutHeight, 0, ibctesting.MockPacketData) suite.Require().NoError(err) + + packet = channeltypes.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) } }, false, false}, {"channel does not exist", func() { @@ -113,22 +118,22 @@ func (suite *KeeperTestSuite) TestHandleRecvPacket() { // mock will panic if application callback is called twice on the same packet path.SetChannelOrdered() suite.coordinator.Setup(path) - packet = channeltypes.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) - err := path.EndpointA.SendPacket(packet) + sequence, err := path.EndpointA.SendPacket(timeoutHeight, 0, ibctesting.MockPacketData) suite.Require().NoError(err) + packet = channeltypes.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) err = path.EndpointB.RecvPacket(packet) suite.Require().NoError(err) }, true, false}, {"successful no-op: UNORDERED - packet already received (replay)", func() { // mock will panic if application callback is called twice on the same packet suite.coordinator.Setup(path) - packet = channeltypes.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) - err := path.EndpointA.SendPacket(packet) + sequence, err := path.EndpointA.SendPacket(timeoutHeight, 0, ibctesting.MockPacketData) suite.Require().NoError(err) + packet = channeltypes.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) err = path.EndpointB.RecvPacket(packet) suite.Require().NoError(err) }, true, false}, @@ -211,21 +216,21 @@ func (suite *KeeperTestSuite) TestHandleAcknowledgePacket() { {"success: ORDERED", func() { path.SetChannelOrdered() suite.coordinator.Setup(path) - packet = channeltypes.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) - err := path.EndpointA.SendPacket(packet) + sequence, err := path.EndpointA.SendPacket(timeoutHeight, 0, ibctesting.MockPacketData) suite.Require().NoError(err) + packet = channeltypes.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) err = path.EndpointB.RecvPacket(packet) suite.Require().NoError(err) }, true}, {"success: UNORDERED", func() { suite.coordinator.Setup(path) - packet = channeltypes.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) - err := path.EndpointA.SendPacket(packet) + sequence, err := path.EndpointA.SendPacket(timeoutHeight, 0, ibctesting.MockPacketData) suite.Require().NoError(err) + packet = channeltypes.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) err = path.EndpointB.RecvPacket(packet) suite.Require().NoError(err) }, true}, @@ -235,11 +240,10 @@ func (suite *KeeperTestSuite) TestHandleAcknowledgePacket() { // attempts to acknowledge ack with sequence 10 without acknowledging ack with sequence 1 (removing packet commitment) for i := uint64(1); i < 10; i++ { - packet = channeltypes.NewPacket(ibctesting.MockPacketData, i, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) - - err := path.EndpointA.SendPacket(packet) + sequence, err := path.EndpointA.SendPacket(timeoutHeight, 0, ibctesting.MockPacketData) suite.Require().NoError(err) + packet = channeltypes.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) err = path.EndpointB.RecvPacket(packet) suite.Require().NoError(err) } @@ -250,11 +254,10 @@ func (suite *KeeperTestSuite) TestHandleAcknowledgePacket() { // attempts to acknowledge ack with sequence 10 without acknowledging ack with sequence 1 (removing packet commitment for i := uint64(1); i < 10; i++ { - packet = channeltypes.NewPacket(ibctesting.MockPacketData, i, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) - - err := path.EndpointA.SendPacket(packet) + sequence, err := path.EndpointA.SendPacket(timeoutHeight, 0, ibctesting.MockPacketData) suite.Require().NoError(err) + packet = channeltypes.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) err = path.EndpointB.RecvPacket(packet) suite.Require().NoError(err) } @@ -265,18 +268,19 @@ func (suite *KeeperTestSuite) TestHandleAcknowledgePacket() { }, false}, {"packet not received", func() { suite.coordinator.Setup(path) - packet = channeltypes.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) - err := path.EndpointA.SendPacket(packet) + sequence, err := path.EndpointA.SendPacket(timeoutHeight, 0, ibctesting.MockPacketData) suite.Require().NoError(err) + + packet = channeltypes.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) }, false}, {"successful no-op: ORDERED - packet already acknowledged (replay)", func() { suite.coordinator.Setup(path) - packet = channeltypes.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) - err := path.EndpointA.SendPacket(packet) + sequence, err := path.EndpointA.SendPacket(timeoutHeight, 0, ibctesting.MockPacketData) suite.Require().NoError(err) + packet = channeltypes.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) err = path.EndpointB.RecvPacket(packet) suite.Require().NoError(err) @@ -286,11 +290,10 @@ func (suite *KeeperTestSuite) TestHandleAcknowledgePacket() { {"successful no-op: UNORDERED - packet already acknowledged (replay)", func() { suite.coordinator.Setup(path) - packet = channeltypes.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) - - err := path.EndpointA.SendPacket(packet) + sequence, err := path.EndpointA.SendPacket(timeoutHeight, 0, ibctesting.MockPacketData) suite.Require().NoError(err) + packet = channeltypes.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) err = path.EndpointB.RecvPacket(packet) suite.Require().NoError(err) @@ -358,28 +361,36 @@ func (suite *KeeperTestSuite) TestHandleTimeoutPacket() { {"success: ORDERED", func() { path.SetChannelOrdered() suite.coordinator.Setup(path) - packet = channeltypes.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, clienttypes.GetSelfHeight(suite.chainB.GetContext()), uint64(suite.chainB.GetContext().BlockTime().UnixNano())) + + timeoutHeight := clienttypes.GetSelfHeight(suite.chainB.GetContext()) + timeoutTimestamp := uint64(suite.chainB.GetContext().BlockTime().UnixNano()) // create packet commitment - err := path.EndpointA.SendPacket(packet) + sequence, err := path.EndpointA.SendPacket(timeoutHeight, timeoutTimestamp, ibctesting.MockPacketData) suite.Require().NoError(err) // need to update chainA client to prove missing ack - path.EndpointA.UpdateClient() + err = path.EndpointA.UpdateClient() + suite.Require().NoError(err) + packet = channeltypes.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, timeoutTimestamp) packetKey = host.NextSequenceRecvKey(packet.GetDestPort(), packet.GetDestChannel()) }, true}, {"success: UNORDERED", func() { suite.coordinator.Setup(path) - packet = channeltypes.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, clienttypes.GetSelfHeight(suite.chainB.GetContext()), uint64(suite.chainB.GetContext().BlockTime().UnixNano())) + + timeoutHeight := clienttypes.GetSelfHeight(suite.chainB.GetContext()) + timeoutTimestamp := uint64(suite.chainB.GetContext().BlockTime().UnixNano()) // create packet commitment - err := path.EndpointA.SendPacket(packet) + sequence, err := path.EndpointA.SendPacket(timeoutHeight, timeoutTimestamp, ibctesting.MockPacketData) suite.Require().NoError(err) // need to update chainA client to prove missing ack - path.EndpointA.UpdateClient() + err = path.EndpointA.UpdateClient() + suite.Require().NoError(err) + packet = channeltypes.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, timeoutTimestamp) packetKey = host.PacketReceiptKey(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()) }, true}, {"success: UNORDERED timeout out of order packet", func() { @@ -389,14 +400,18 @@ func (suite *KeeperTestSuite) TestHandleTimeoutPacket() { // attempts to timeout the last packet sent without timing out the first packet // packet sequences begin at 1 for i := uint64(1); i < maxSequence; i++ { - packet = channeltypes.NewPacket(ibctesting.MockPacketData, i, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, clienttypes.GetSelfHeight(suite.chainB.GetContext()), 0) + timeoutHeight := clienttypes.GetSelfHeight(suite.chainB.GetContext()) // create packet commitment - err := path.EndpointA.SendPacket(packet) + sequence, err := path.EndpointA.SendPacket(timeoutHeight, 0, ibctesting.MockPacketData) suite.Require().NoError(err) + + packet = channeltypes.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) } - path.EndpointA.UpdateClient() + err := path.EndpointA.UpdateClient() + suite.Require().NoError(err) + packetKey = host.PacketReceiptKey(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()) }, true}, {"success: ORDERED timeout out of order packet", func() { @@ -406,14 +421,18 @@ func (suite *KeeperTestSuite) TestHandleTimeoutPacket() { // attempts to timeout the last packet sent without timing out the first packet // packet sequences begin at 1 for i := uint64(1); i < maxSequence; i++ { - packet = channeltypes.NewPacket(ibctesting.MockPacketData, i, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, clienttypes.GetSelfHeight(suite.chainB.GetContext()), 0) + timeoutHeight := clienttypes.GetSelfHeight(suite.chainB.GetContext()) // create packet commitment - err := path.EndpointA.SendPacket(packet) + sequence, err := path.EndpointA.SendPacket(timeoutHeight, 0, ibctesting.MockPacketData) suite.Require().NoError(err) + + packet = channeltypes.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) } - path.EndpointA.UpdateClient() + err := path.EndpointA.UpdateClient() + suite.Require().NoError(err) + packetKey = host.NextSequenceRecvKey(packet.GetDestPort(), packet.GetDestChannel()) }, true}, {"channel does not exist", func() { @@ -472,7 +491,7 @@ func (suite *KeeperTestSuite) TestHandleTimeoutPacket() { // and unordered channels. It verifies that the deletion of a packet // commitment occurs. It tests high level properties like ordering and basic // sanity checks. More rigorous testing of 'TimeoutOnClose' and -//'TimeoutExecuted' can be found in the 04-channel/keeper/timeout_test.go. +// 'TimeoutExecuted' can be found in the 04-channel/keeper/timeout_test.go. func (suite *KeeperTestSuite) TestHandleTimeoutOnClosePacket() { var ( packet channeltypes.Packet @@ -488,35 +507,39 @@ func (suite *KeeperTestSuite) TestHandleTimeoutOnClosePacket() { {"success: ORDERED", func() { path.SetChannelOrdered() suite.coordinator.Setup(path) - packet = channeltypes.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) // create packet commitment - err := path.EndpointA.SendPacket(packet) + sequence, err := path.EndpointA.SendPacket(timeoutHeight, 0, ibctesting.MockPacketData) suite.Require().NoError(err) // need to update chainA client to prove missing ack - path.EndpointA.UpdateClient() + err = path.EndpointA.UpdateClient() + suite.Require().NoError(err) + packet = channeltypes.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) packetKey = host.NextSequenceRecvKey(packet.GetDestPort(), packet.GetDestChannel()) // close counterparty channel - path.EndpointB.SetChannelClosed() + err = path.EndpointB.SetChannelState(channeltypes.CLOSED) + suite.Require().NoError(err) }, true}, {"success: UNORDERED", func() { suite.coordinator.Setup(path) - packet = channeltypes.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) // create packet commitment - err := path.EndpointA.SendPacket(packet) + sequence, err := path.EndpointA.SendPacket(timeoutHeight, 0, ibctesting.MockPacketData) suite.Require().NoError(err) // need to update chainA client to prove missing ack - path.EndpointA.UpdateClient() + err = path.EndpointA.UpdateClient() + suite.Require().NoError(err) + packet = channeltypes.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) packetKey = host.PacketReceiptKey(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()) // close counterparty channel - path.EndpointB.SetChannelClosed() + err = path.EndpointB.SetChannelState(channeltypes.CLOSED) + suite.Require().NoError(err) }, true}, {"success: UNORDERED timeout out of order packet", func() { // setup uses an UNORDERED channel @@ -525,18 +548,21 @@ func (suite *KeeperTestSuite) TestHandleTimeoutOnClosePacket() { // attempts to timeout the last packet sent without timing out the first packet // packet sequences begin at 1 for i := uint64(1); i < maxSequence; i++ { - packet = channeltypes.NewPacket(ibctesting.MockPacketData, i, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) - // create packet commitment - err := path.EndpointA.SendPacket(packet) + sequence, err := path.EndpointA.SendPacket(timeoutHeight, 0, ibctesting.MockPacketData) suite.Require().NoError(err) + + packet = channeltypes.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) } - path.EndpointA.UpdateClient() + err := path.EndpointA.UpdateClient() + suite.Require().NoError(err) + packetKey = host.PacketReceiptKey(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()) // close counterparty channel - path.EndpointB.SetChannelClosed() + err = path.EndpointB.SetChannelState(channeltypes.CLOSED) + suite.Require().NoError(err) }, true}, {"success: ORDERED timeout out of order packet", func() { path.SetChannelOrdered() @@ -545,18 +571,21 @@ func (suite *KeeperTestSuite) TestHandleTimeoutOnClosePacket() { // attempts to timeout the last packet sent without timing out the first packet // packet sequences begin at 1 for i := uint64(1); i < maxSequence; i++ { - packet = channeltypes.NewPacket(ibctesting.MockPacketData, i, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) - // create packet commitment - err := path.EndpointA.SendPacket(packet) + sequence, err := path.EndpointA.SendPacket(timeoutHeight, 0, ibctesting.MockPacketData) suite.Require().NoError(err) + + packet = channeltypes.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) } - path.EndpointA.UpdateClient() + err := path.EndpointA.UpdateClient() + suite.Require().NoError(err) + packetKey = host.NextSequenceRecvKey(packet.GetDestPort(), packet.GetDestChannel()) // close counterparty channel - path.EndpointB.SetChannelClosed() + err = path.EndpointB.SetChannelState(channeltypes.CLOSED) + suite.Require().NoError(err) }, true}, {"channel does not exist", func() { // any non-nil value of packet is valid @@ -570,20 +599,22 @@ func (suite *KeeperTestSuite) TestHandleTimeoutOnClosePacket() { packetKey = host.PacketAcknowledgementKey(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()) // close counterparty channel - path.EndpointB.SetChannelClosed() + err := path.EndpointB.SetChannelState(channeltypes.CLOSED) + suite.Require().NoError(err) }, true}, {"ORDERED: channel not closed", func() { path.SetChannelOrdered() suite.coordinator.Setup(path) - packet = channeltypes.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) // create packet commitment - err := path.EndpointA.SendPacket(packet) + sequence, err := path.EndpointA.SendPacket(timeoutHeight, 0, ibctesting.MockPacketData) suite.Require().NoError(err) // need to update chainA client to prove missing ack - path.EndpointA.UpdateClient() + err = path.EndpointA.UpdateClient() + suite.Require().NoError(err) + packet = channeltypes.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) packetKey = host.NextSequenceRecvKey(packet.GetDestPort(), packet.GetDestChannel()) }, false}, } @@ -627,15 +658,13 @@ func (suite *KeeperTestSuite) TestHandleTimeoutOnClosePacket() { func (suite *KeeperTestSuite) TestUpgradeClient() { var ( path *ibctesting.Path + newChainID string + newClientHeight clienttypes.Height upgradedClient exported.ClientState upgradedConsState exported.ConsensusState lastHeight exported.Height msg *clienttypes.MsgUpgradeClient ) - - newClientHeight := clienttypes.NewHeight(1, 1) - newChainId := "newChainId-1" - cases := []struct { name string setup func() @@ -644,11 +673,11 @@ func (suite *KeeperTestSuite) TestUpgradeClient() { { name: "successful upgrade", setup: func() { - upgradedClient = ibctmtypes.NewClientState(newChainId, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod+ibctesting.TrustingPeriod, ibctesting.MaxClockDrift, newClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) + upgradedClient = ibctm.NewClientState(newChainID, ibctm.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod+ibctesting.TrustingPeriod, ibctesting.MaxClockDrift, newClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath) // Call ZeroCustomFields on upgraded clients to clear any client-chosen parameters in test-case upgradedClient upgradedClient = upgradedClient.ZeroCustomFields() - upgradedConsState = &ibctmtypes.ConsensusState{ + upgradedConsState = &ibctm.ConsensusState{ NextValidatorsHash: []byte("nextValsHash"), } @@ -661,8 +690,8 @@ func (suite *KeeperTestSuite) TestUpgradeClient() { suite.Require().NoError(err) // zero custom fields and store in upgrade store - suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) - suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) + suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) //nolint:errcheck // ignore error for testing + suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) //nolint:errcheck // ignore error for testing // commit upgrade store changes and update clients suite.coordinator.CommitBlock(suite.chainB) @@ -684,11 +713,11 @@ func (suite *KeeperTestSuite) TestUpgradeClient() { { name: "VerifyUpgrade fails", setup: func() { - upgradedClient = ibctmtypes.NewClientState(newChainId, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod+ibctesting.TrustingPeriod, ibctesting.MaxClockDrift, newClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) + upgradedClient = ibctm.NewClientState(newChainID, ibctm.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod+ibctesting.TrustingPeriod, ibctesting.MaxClockDrift, newClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath) // Call ZeroCustomFields on upgraded clients to clear any client-chosen parameters in test-case upgradedClient upgradedClient = upgradedClient.ZeroCustomFields() - upgradedConsState = &ibctmtypes.ConsensusState{ + upgradedConsState = &ibctm.ConsensusState{ NextValidatorsHash: []byte("nextValsHash"), } @@ -701,8 +730,8 @@ func (suite *KeeperTestSuite) TestUpgradeClient() { suite.Require().NoError(err) // zero custom fields and store in upgrade store - suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) - suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) + suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) //nolint:errcheck // ignore error for testing + suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) //nolint:errcheck // ignore error for testing // commit upgrade store changes and update clients suite.coordinator.CommitBlock(suite.chainB) @@ -721,9 +750,18 @@ func (suite *KeeperTestSuite) TestUpgradeClient() { path = ibctesting.NewPath(suite.chainA, suite.chainB) suite.coordinator.SetupClients(path) + var err error + clientState := path.EndpointA.GetClientState().(*ibctm.ClientState) + revisionNumber := clienttypes.ParseChainID(clientState.ChainId) + + newChainID, err = clienttypes.SetRevisionNumber(clientState.ChainId, revisionNumber+1) + suite.Require().NoError(err) + + newClientHeight = clienttypes.NewHeight(revisionNumber+1, clientState.GetLatestHeight().GetRevisionHeight()+1) + tc.setup() - _, err := keeper.Keeper.UpgradeClient(*suite.chainA.App.GetIBCKeeper(), sdk.WrapSDKContext(suite.chainA.GetContext()), msg) + _, err = keeper.Keeper.UpgradeClient(*suite.chainA.App.GetIBCKeeper(), sdk.WrapSDKContext(suite.chainA.GetContext()), msg) if tc.expPass { suite.Require().NoError(err, "upgrade handler failed on valid case: %s", tc.name) diff --git a/modules/core/legacy/v100/genesis.go b/modules/core/legacy/v100/genesis.go deleted file mode 100644 index 78e8f0d6065..00000000000 --- a/modules/core/legacy/v100/genesis.go +++ /dev/null @@ -1,54 +0,0 @@ -package v100 - -import ( - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/codec" - genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" - tmtypes "github.com/tendermint/tendermint/types" - - clientv100 "github.com/cosmos/ibc-go/v4/modules/core/02-client/legacy/v100" - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - connectiontypes "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" - "github.com/cosmos/ibc-go/v4/modules/core/types" -) - -// MigrateGenesis accepts exported v1.0.0 IBC client genesis file and migrates it to: -// -// - Update solo machine client state protobuf definition (v1 to v2) -// - Remove all solo machine consensus states -// - Remove all expired tendermint consensus states -func MigrateGenesis(appState genutiltypes.AppMap, clientCtx client.Context, genDoc tmtypes.GenesisDoc, maxExpectedTimePerBlock uint64) (genutiltypes.AppMap, error) { - if appState[host.ModuleName] != nil { - // ensure legacy solo machines are registered - clientv100.RegisterInterfaces(clientCtx.InterfaceRegistry) - - // unmarshal relative source genesis application state - ibcGenState := &types.GenesisState{} - clientCtx.JSONCodec.MustUnmarshalJSON(appState[host.ModuleName], ibcGenState) - - clientGenState, err := clientv100.MigrateGenesis(codec.NewProtoCodec(clientCtx.InterfaceRegistry), &ibcGenState.ClientGenesis, genDoc.GenesisTime, clienttypes.NewHeight(clienttypes.ParseChainID(genDoc.ChainID), uint64(genDoc.InitialHeight))) - if err != nil { - return nil, err - } - - ibcGenState.ClientGenesis = *clientGenState - - // set max expected time per block - connectionGenesis := connectiontypes.GenesisState{ - Connections: ibcGenState.ConnectionGenesis.Connections, - ClientConnectionPaths: ibcGenState.ConnectionGenesis.ClientConnectionPaths, - NextConnectionSequence: ibcGenState.ConnectionGenesis.NextConnectionSequence, - Params: connectiontypes.NewParams(maxExpectedTimePerBlock), - } - - ibcGenState.ConnectionGenesis = connectionGenesis - - // delete old genesis state - delete(appState, host.ModuleName) - - // set new ibc genesis state - appState[host.ModuleName] = clientCtx.JSONCodec.MustMarshalJSON(ibcGenState) - } - return appState, nil -} diff --git a/modules/core/legacy/v100/genesis_test.go b/modules/core/legacy/v100/genesis_test.go deleted file mode 100644 index 85e239862d9..00000000000 --- a/modules/core/legacy/v100/genesis_test.go +++ /dev/null @@ -1,178 +0,0 @@ -package v100_test - -import ( - "testing" - - "github.com/cosmos/cosmos-sdk/client" - codectypes "github.com/cosmos/cosmos-sdk/codec/types" - genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" - "github.com/stretchr/testify/suite" - tmtypes "github.com/tendermint/tendermint/types" - - ibcclient "github.com/cosmos/ibc-go/v4/modules/core/02-client" - clientv100 "github.com/cosmos/ibc-go/v4/modules/core/02-client/legacy/v100" - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - connectiontypes "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" - v100 "github.com/cosmos/ibc-go/v4/modules/core/legacy/v100" - "github.com/cosmos/ibc-go/v4/modules/core/types" - ibctesting "github.com/cosmos/ibc-go/v4/testing" - "github.com/cosmos/ibc-go/v4/testing/simapp" -) - -type LegacyTestSuite struct { - suite.Suite - - coordinator *ibctesting.Coordinator - - // testing chains used for convenience and readability - chainA *ibctesting.TestChain - chainB *ibctesting.TestChain -} - -// TestLegacyTestSuite runs all the tests within this package. -func TestLegacyTestSuite(t *testing.T) { - suite.Run(t, new(LegacyTestSuite)) -} - -// SetupTest creates a coordinator with 2 test chains. -func (suite *LegacyTestSuite) SetupTest() { - suite.coordinator = ibctesting.NewCoordinator(suite.T(), 2) - suite.chainA = suite.coordinator.GetChain(ibctesting.GetChainID(1)) - suite.chainB = suite.coordinator.GetChain(ibctesting.GetChainID(2)) - // commit some blocks so that QueryProof returns valid proof (cannot return valid query if height <= 1) - suite.coordinator.CommitNBlocks(suite.chainA, 2) - suite.coordinator.CommitNBlocks(suite.chainB, 2) -} - -// NOTE: this test is mainly copied from 02-client/legacy/v100 -func (suite *LegacyTestSuite) TestMigrateGenesisSolomachine() { - path := ibctesting.NewPath(suite.chainA, suite.chainB) - encodingConfig := simapp.MakeTestEncodingConfig() - clientCtx := client.Context{}. - WithInterfaceRegistry(encodingConfig.InterfaceRegistry). - WithTxConfig(encodingConfig.TxConfig). - WithJSONCodec(encodingConfig.Marshaler) - - // create multiple legacy solo machine clients - solomachine := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "06-solomachine-0", "testing", 1) - solomachineMulti := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "06-solomachine-1", "testing", 4) - - // create tendermint clients - // NOTE: only 1 set of metadata is created, we aren't testing ordering - // The purpose of this test is to ensure the genesis states can be marshalled/unmarshalled - suite.coordinator.SetupClients(path) - clientGenState := ibcclient.ExportGenesis(path.EndpointA.Chain.GetContext(), path.EndpointA.Chain.App.GetIBCKeeper().ClientKeeper) - - // manually generate old proto buf definitions and set in genesis - // NOTE: we cannot use 'ExportGenesis' for the solo machines since we are - // using client states and consensus states which do not implement the exported.ClientState - // and exported.ConsensusState interface - var clients []clienttypes.IdentifiedClientState - for _, sm := range []*ibctesting.Solomachine{solomachine, solomachineMulti} { - clientState := sm.ClientState() - - var seq uint64 - if clientState.IsFrozen { - seq = 1 - } - - // generate old client state proto defintion - legacyClientState := &clientv100.ClientState{ - Sequence: clientState.Sequence, - FrozenSequence: seq, - ConsensusState: &clientv100.ConsensusState{ - PublicKey: clientState.ConsensusState.PublicKey, - Diversifier: clientState.ConsensusState.Diversifier, - Timestamp: clientState.ConsensusState.Timestamp, - }, - AllowUpdateAfterProposal: clientState.AllowUpdateAfterProposal, - } - - // set client state - any, err := codectypes.NewAnyWithValue(legacyClientState) - suite.Require().NoError(err) - suite.Require().NotNil(any) - client := clienttypes.IdentifiedClientState{ - ClientId: sm.ClientID, - ClientState: any, - } - clients = append(clients, client) - - // set in store for ease of determining expected genesis - clientStore := path.EndpointA.Chain.App.GetIBCKeeper().ClientKeeper.ClientStore(path.EndpointA.Chain.GetContext(), sm.ClientID) - bz, err := path.EndpointA.Chain.App.AppCodec().MarshalInterface(legacyClientState) - suite.Require().NoError(err) - clientStore.Set(host.ClientStateKey(), bz) - - // set some consensus states - height1 := clienttypes.NewHeight(0, 1) - height2 := clienttypes.NewHeight(1, 2) - height3 := clienttypes.NewHeight(0, 123) - - any, err = codectypes.NewAnyWithValue(legacyClientState.ConsensusState) - suite.Require().NoError(err) - suite.Require().NotNil(any) - consensusState1 := clienttypes.ConsensusStateWithHeight{ - Height: height1, - ConsensusState: any, - } - consensusState2 := clienttypes.ConsensusStateWithHeight{ - Height: height2, - ConsensusState: any, - } - consensusState3 := clienttypes.ConsensusStateWithHeight{ - Height: height3, - ConsensusState: any, - } - - clientConsensusState := clienttypes.ClientConsensusStates{ - ClientId: sm.ClientID, - ConsensusStates: []clienttypes.ConsensusStateWithHeight{consensusState1, consensusState2, consensusState3}, - } - - clientGenState.ClientsConsensus = append(clientGenState.ClientsConsensus, clientConsensusState) - - // set in store for ease of determining expected genesis - bz, err = path.EndpointA.Chain.App.AppCodec().MarshalInterface(legacyClientState.ConsensusState) - suite.Require().NoError(err) - clientStore.Set(host.ConsensusStateKey(height1), bz) - clientStore.Set(host.ConsensusStateKey(height2), bz) - clientStore.Set(host.ConsensusStateKey(height3), bz) - } - // solo machine clients must come before tendermint in expected - clientGenState.Clients = append(clients, clientGenState.Clients...) - - // migrate store get expected genesis - // store migration and genesis migration should produce identical results - err := clientv100.MigrateStore(path.EndpointA.Chain.GetContext(), path.EndpointA.Chain.GetSimApp().GetKey(host.StoreKey), path.EndpointA.Chain.App.AppCodec()) - suite.Require().NoError(err) - expectedClientGenState := ibcclient.ExportGenesis(path.EndpointA.Chain.GetContext(), path.EndpointA.Chain.App.GetIBCKeeper().ClientKeeper) - - // NOTE: these lines are added in comparison to 02-client/legacy/v100 - // generate appState with old ibc genesis state - appState := genutiltypes.AppMap{} - ibcGenState := types.DefaultGenesisState() - ibcGenState.ClientGenesis = clientGenState - clientv100.RegisterInterfaces(clientCtx.InterfaceRegistry) - appState[host.ModuleName] = clientCtx.JSONCodec.MustMarshalJSON(ibcGenState) - genDoc := tmtypes.GenesisDoc{ - ChainID: suite.chainA.ChainID, - GenesisTime: suite.coordinator.CurrentTime, - InitialHeight: suite.chainA.GetContext().BlockHeight(), - } - - // NOTE: genesis time isn't updated since we aren't testing for tendermint consensus state pruning - migrated, err := v100.MigrateGenesis(appState, clientCtx, genDoc, uint64(connectiontypes.DefaultTimePerBlock)) - suite.Require().NoError(err) - - expectedAppState := genutiltypes.AppMap{} - expectedIBCGenState := types.DefaultGenesisState() - expectedIBCGenState.ClientGenesis = expectedClientGenState - - bz, err := clientCtx.JSONCodec.MarshalJSON(expectedIBCGenState) - suite.Require().NoError(err) - expectedAppState[host.ModuleName] = bz - - suite.Require().Equal(expectedAppState, migrated) -} diff --git a/modules/core/migrations/v7/genesis.go b/modules/core/migrations/v7/genesis.go new file mode 100644 index 00000000000..8647c4c77a7 --- /dev/null +++ b/modules/core/migrations/v7/genesis.go @@ -0,0 +1,42 @@ +package v7 + +import ( + "github.com/cosmos/cosmos-sdk/codec" + genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" + + clientv7 "github.com/cosmos/ibc-go/v7/modules/core/02-client/migrations/v7" + ibcexported "github.com/cosmos/ibc-go/v7/modules/core/exported" + "github.com/cosmos/ibc-go/v7/modules/core/types" +) + +// MigrateGenesis accepts an exported IBC client genesis file and migrates it to: +// +// - Update solo machine client state protobuf definition (v2 to v3) +// - Remove all solo machine consensus states +// - Remove any localhost clients +func MigrateGenesis(appState genutiltypes.AppMap, cdc codec.ProtoCodecMarshaler) (genutiltypes.AppMap, error) { + if appState[ibcexported.ModuleName] == nil { + return appState, nil + } + + // ensure legacy solo machines types are registered + clientv7.RegisterInterfaces(cdc.InterfaceRegistry()) + + // unmarshal old ibc genesis state + ibcGenState := &types.GenesisState{} + cdc.MustUnmarshalJSON(appState[ibcexported.ModuleName], ibcGenState) + + clientGenState, err := clientv7.MigrateGenesis(&ibcGenState.ClientGenesis, cdc) + if err != nil { + return nil, err + } + + ibcGenState.ClientGenesis = *clientGenState + + // delete old genesis state + delete(appState, ibcexported.ModuleName) + + // set new ibc genesis state + appState[ibcexported.ModuleName] = cdc.MustMarshalJSON(ibcGenState) + return appState, nil +} diff --git a/modules/core/migrations/v7/genesis_test.go b/modules/core/migrations/v7/genesis_test.go new file mode 100644 index 00000000000..1a040534ff8 --- /dev/null +++ b/modules/core/migrations/v7/genesis_test.go @@ -0,0 +1,164 @@ +package v7_test + +import ( + "testing" + + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" + "github.com/stretchr/testify/suite" + + ibcclient "github.com/cosmos/ibc-go/v7/modules/core/02-client" + clientv7 "github.com/cosmos/ibc-go/v7/modules/core/02-client/migrations/v7" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + ibcexported "github.com/cosmos/ibc-go/v7/modules/core/exported" + v7 "github.com/cosmos/ibc-go/v7/modules/core/migrations/v7" + "github.com/cosmos/ibc-go/v7/modules/core/types" + ibctesting "github.com/cosmos/ibc-go/v7/testing" +) + +type MigrationsV7TestSuite struct { + suite.Suite + + coordinator *ibctesting.Coordinator + + // testing chains used for convenience and readability + chainA *ibctesting.TestChain + chainB *ibctesting.TestChain +} + +// TestMigrationsV7TestSuite runs all the tests within this package. +func TestMigrationsV7TestSuite(t *testing.T) { + suite.Run(t, new(MigrationsV7TestSuite)) +} + +// SetupTest creates a coordinator with 2 test chains. +func (suite *MigrationsV7TestSuite) SetupTest() { + suite.coordinator = ibctesting.NewCoordinator(suite.T(), 2) + suite.chainA = suite.coordinator.GetChain(ibctesting.GetChainID(1)) + suite.chainB = suite.coordinator.GetChain(ibctesting.GetChainID(2)) +} + +// NOTE: this test is mainly copied from 02-client/migrations/v7/genesis_test.go +func (suite *MigrationsV7TestSuite) TestMigrateGenesisSolomachine() { + // create tendermint clients + for i := 0; i < 3; i++ { + path := ibctesting.NewPath(suite.chainA, suite.chainB) + + suite.coordinator.SetupClients(path) + + err := path.EndpointA.UpdateClient() + suite.Require().NoError(err) + + // update a second time to add more state + err = path.EndpointA.UpdateClient() + suite.Require().NoError(err) + } + + // create multiple legacy solo machine clients + solomachine := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "06-solomachine-0", "testing", 1) + solomachineMulti := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "06-solomachine-1", "testing", 4) + + clientGenState := ibcclient.ExportGenesis(suite.chainA.GetContext(), suite.chainA.App.GetIBCKeeper().ClientKeeper) + + // manually generate old proto buf definitions and set in genesis + // NOTE: we cannot use 'ExportGenesis' for the solo machines since we are + // using client states and consensus states which do not implement the exported.ClientState + // and exported.ConsensusState interface + var clients []clienttypes.IdentifiedClientState + for _, sm := range []*ibctesting.Solomachine{solomachine, solomachineMulti} { + clientState := sm.ClientState() + + // generate old client state proto definition + legacyClientState := &clientv7.ClientState{ + Sequence: clientState.Sequence, + ConsensusState: &clientv7.ConsensusState{ + PublicKey: clientState.ConsensusState.PublicKey, + Diversifier: clientState.ConsensusState.Diversifier, + Timestamp: clientState.ConsensusState.Timestamp, + }, + AllowUpdateAfterProposal: true, + } + + // set client state + protoAny, err := codectypes.NewAnyWithValue(legacyClientState) + suite.Require().NoError(err) + suite.Require().NotNil(protoAny) + + clients = append(clients, clienttypes.IdentifiedClientState{ + ClientId: sm.ClientID, + ClientState: protoAny, + }) + + // set in store for ease of determining expected genesis + clientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), sm.ClientID) + cdc := suite.chainA.App.AppCodec().(*codec.ProtoCodec) + clientv7.RegisterInterfaces(cdc.InterfaceRegistry()) + + bz, err := cdc.MarshalInterface(legacyClientState) + suite.Require().NoError(err) + clientStore.Set(host.ClientStateKey(), bz) + + protoAny, err = codectypes.NewAnyWithValue(legacyClientState.ConsensusState) + suite.Require().NoError(err) + suite.Require().NotNil(protoAny) + + // obtain marshalled bytes to set in client store + bz, err = cdc.MarshalInterface(legacyClientState.ConsensusState) + suite.Require().NoError(err) + + var consensusStates []clienttypes.ConsensusStateWithHeight + + // set consensus states in store and genesis + for i := uint64(0); i < 10; i++ { + height := clienttypes.NewHeight(1, i) + clientStore.Set(host.ConsensusStateKey(height), bz) + consensusStates = append(consensusStates, clienttypes.ConsensusStateWithHeight{ + Height: height, + ConsensusState: protoAny, + }) + } + + clientGenState.ClientsConsensus = append(clientGenState.ClientsConsensus, clienttypes.ClientConsensusStates{ + ClientId: sm.ClientID, + ConsensusStates: consensusStates, + }) + } + + // solo machine clients must come before tendermint in expected + clientGenState.Clients = append(clients, clientGenState.Clients...) + + // migrate store get expected genesis + // store migration and genesis migration should produce identical results + // NOTE: tendermint clients are not pruned in genesis so the test should not have expired tendermint clients + err := clientv7.MigrateStore(suite.chainA.GetContext(), suite.chainA.GetSimApp().GetKey(ibcexported.StoreKey), suite.chainA.App.AppCodec(), suite.chainA.GetSimApp().IBCKeeper.ClientKeeper) + suite.Require().NoError(err) + expectedClientGenState := ibcclient.ExportGenesis(suite.chainA.GetContext(), suite.chainA.App.GetIBCKeeper().ClientKeeper) + + cdc := suite.chainA.App.AppCodec().(*codec.ProtoCodec) + + // NOTE: these lines are added in comparison to 02-client/migrations/v7/genesis_test.go + // generate appState with old ibc genesis state + appState := genutiltypes.AppMap{} + ibcGenState := types.DefaultGenesisState() + ibcGenState.ClientGenesis = clientGenState + + // ensure tests pass even if the legacy solo machine is already registered + clientv7.RegisterInterfaces(cdc.InterfaceRegistry()) + appState[ibcexported.ModuleName] = cdc.MustMarshalJSON(ibcGenState) + + // NOTE: genesis time isn't updated since we aren't testing for tendermint consensus state pruning + migrated, err := v7.MigrateGenesis(appState, cdc) + suite.Require().NoError(err) + + expectedAppState := genutiltypes.AppMap{} + expectedIBCGenState := types.DefaultGenesisState() + expectedIBCGenState.ClientGenesis = expectedClientGenState + + bz, err := cdc.MarshalJSON(expectedIBCGenState) + suite.Require().NoError(err) + expectedAppState[ibcexported.ModuleName] = bz + + suite.Require().Equal(expectedAppState, migrated) +} diff --git a/modules/core/module.go b/modules/core/module.go index b6285997652..4d55b6b2e3d 100644 --- a/modules/core/module.go +++ b/modules/core/module.go @@ -4,7 +4,6 @@ import ( "context" "encoding/json" "fmt" - "math/rand" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/codec" @@ -12,21 +11,20 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" simtypes "github.com/cosmos/cosmos-sdk/types/simulation" - "github.com/gorilla/mux" "github.com/grpc-ecosystem/grpc-gateway/runtime" "github.com/spf13/cobra" abci "github.com/tendermint/tendermint/abci/types" - ibcclient "github.com/cosmos/ibc-go/v4/modules/core/02-client" - clientkeeper "github.com/cosmos/ibc-go/v4/modules/core/02-client/keeper" - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - connectiontypes "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types" - channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" - "github.com/cosmos/ibc-go/v4/modules/core/client/cli" - "github.com/cosmos/ibc-go/v4/modules/core/keeper" - "github.com/cosmos/ibc-go/v4/modules/core/simulation" - "github.com/cosmos/ibc-go/v4/modules/core/types" + ibcclient "github.com/cosmos/ibc-go/v7/modules/core/02-client" + clientkeeper "github.com/cosmos/ibc-go/v7/modules/core/02-client/keeper" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + connectiontypes "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + "github.com/cosmos/ibc-go/v7/modules/core/client/cli" + "github.com/cosmos/ibc-go/v7/modules/core/exported" + "github.com/cosmos/ibc-go/v7/modules/core/keeper" + "github.com/cosmos/ibc-go/v7/modules/core/simulation" + "github.com/cosmos/ibc-go/v7/modules/core/types" ) var ( @@ -42,7 +40,7 @@ var _ module.AppModuleBasic = AppModuleBasic{} // Name returns the ibc module's name. func (AppModuleBasic) Name() string { - return host.ModuleName + return exported.ModuleName } // RegisterLegacyAminoCodec does nothing. IBC does not support amino. @@ -58,20 +56,26 @@ func (AppModuleBasic) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage { func (AppModuleBasic) ValidateGenesis(cdc codec.JSONCodec, config client.TxEncodingConfig, bz json.RawMessage) error { var gs types.GenesisState if err := cdc.UnmarshalJSON(bz, &gs); err != nil { - return fmt.Errorf("failed to unmarshal %s genesis state: %w", host.ModuleName, err) + return fmt.Errorf("failed to unmarshal %s genesis state: %w", exported.ModuleName, err) } return gs.Validate() } -// RegisterRESTRoutes does nothing. IBC does not support legacy REST routes. -func (AppModuleBasic) RegisterRESTRoutes(client.Context, *mux.Router) {} - // RegisterGRPCGatewayRoutes registers the gRPC Gateway routes for the ibc module. func (AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *runtime.ServeMux) { - clienttypes.RegisterQueryHandlerClient(context.Background(), mux, clienttypes.NewQueryClient(clientCtx)) - connectiontypes.RegisterQueryHandlerClient(context.Background(), mux, connectiontypes.NewQueryClient(clientCtx)) - channeltypes.RegisterQueryHandlerClient(context.Background(), mux, channeltypes.NewQueryClient(clientCtx)) + err := clienttypes.RegisterQueryHandlerClient(context.Background(), mux, clienttypes.NewQueryClient(clientCtx)) + if err != nil { + panic(err) + } + err = connectiontypes.RegisterQueryHandlerClient(context.Background(), mux, connectiontypes.NewQueryClient(clientCtx)) + if err != nil { + panic(err) + } + err = channeltypes.RegisterQueryHandlerClient(context.Background(), mux, channeltypes.NewQueryClient(clientCtx)) + if err != nil { + panic(err) + } } // GetTxCmd returns the root tx command for the ibc module. @@ -93,9 +97,6 @@ func (AppModuleBasic) RegisterInterfaces(registry codectypes.InterfaceRegistry) type AppModule struct { AppModuleBasic keeper *keeper.Keeper - - // create localhost by default - createLocalhost bool } // NewAppModule creates a new AppModule object @@ -107,7 +108,7 @@ func NewAppModule(k *keeper.Keeper) AppModule { // Name returns the ibc module's name. func (AppModule) Name() string { - return host.ModuleName + return exported.ModuleName } // RegisterInvariants registers the ibc module invariants. @@ -115,21 +116,6 @@ func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) { // TODO: } -// Route returns the message routing key for the ibc module. -func (am AppModule) Route() sdk.Route { - return sdk.Route{} -} - -// QuerierRoute returns the ibc module's querier route name. -func (AppModule) QuerierRoute() string { - return host.QuerierRoute -} - -// LegacyQuerierHandler returns nil. IBC does not support the legacy querier. -func (am AppModule) LegacyQuerierHandler(legacyQuerierCdc *codec.LegacyAmino) sdk.Querier { - return nil -} - // RegisterServices registers module services. func (am AppModule) RegisterServices(cfg module.Configurator) { clienttypes.RegisterMsgServer(cfg.MsgServer(), am.keeper) @@ -138,7 +124,10 @@ func (am AppModule) RegisterServices(cfg module.Configurator) { types.RegisterQueryService(cfg.QueryServer(), am.keeper) m := clientkeeper.NewMigrator(am.keeper.ClientKeeper) - cfg.RegisterMigration(host.ModuleName, 1, m.Migrate1to2) + err := cfg.RegisterMigration(exported.ModuleName, 2, m.Migrate2to3) + if err != nil { + panic(err) + } } // InitGenesis performs genesis initialization for the ibc module. It returns @@ -147,9 +136,9 @@ func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, bz json.Ra var gs types.GenesisState err := cdc.UnmarshalJSON(bz, &gs) if err != nil { - panic(fmt.Sprintf("failed to unmarshal %s genesis state: %s", host.ModuleName, err)) + panic(fmt.Sprintf("failed to unmarshal %s genesis state: %s", exported.ModuleName, err)) } - InitGenesis(ctx, *am.keeper, am.createLocalhost, &gs) + InitGenesis(ctx, *am.keeper, &gs) return []abci.ValidatorUpdate{} } @@ -160,7 +149,7 @@ func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.Raw } // ConsensusVersion implements AppModule/ConsensusVersion. -func (AppModule) ConsensusVersion() uint64 { return 2 } +func (AppModule) ConsensusVersion() uint64 { return 3 } // BeginBlock returns the begin blocker for the ibc module. func (am AppModule) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) { @@ -180,19 +169,9 @@ func (AppModule) GenerateGenesisState(simState *module.SimulationState) { simulation.RandomizedGenState(simState) } -// ProposalContents doesn't return any content functions for governance proposals. -func (AppModule) ProposalContents(_ module.SimulationState) []simtypes.WeightedProposalContent { - return nil -} - -// RandomizedParams returns nil since IBC doesn't register parameter changes. -func (AppModule) RandomizedParams(_ *rand.Rand) []simtypes.ParamChange { - return nil -} - // RegisterStoreDecoder registers a decoder for ibc module's types func (am AppModule) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) { - sdr[host.StoreKey] = simulation.NewDecodeStore(*am.keeper) + sdr[exported.StoreKey] = simulation.NewDecodeStore(*am.keeper) } // WeightedOperations returns the all the ibc module operations with their respective weights. diff --git a/modules/core/simulation/decoder.go b/modules/core/simulation/decoder.go index fc2cc40bf00..1ed5bf72ad8 100644 --- a/modules/core/simulation/decoder.go +++ b/modules/core/simulation/decoder.go @@ -5,11 +5,11 @@ import ( "github.com/cosmos/cosmos-sdk/types/kv" - clientsim "github.com/cosmos/ibc-go/v4/modules/core/02-client/simulation" - connectionsim "github.com/cosmos/ibc-go/v4/modules/core/03-connection/simulation" - channelsim "github.com/cosmos/ibc-go/v4/modules/core/04-channel/simulation" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" - "github.com/cosmos/ibc-go/v4/modules/core/keeper" + clientsim "github.com/cosmos/ibc-go/v7/modules/core/02-client/simulation" + connectionsim "github.com/cosmos/ibc-go/v7/modules/core/03-connection/simulation" + channelsim "github.com/cosmos/ibc-go/v7/modules/core/04-channel/simulation" + ibcexported "github.com/cosmos/ibc-go/v7/modules/core/exported" + "github.com/cosmos/ibc-go/v7/modules/core/keeper" ) // NewDecodeStore returns a decoder function closure that unmarshals the KVPair's @@ -28,6 +28,6 @@ func NewDecodeStore(k keeper.Keeper) func(kvA, kvB kv.Pair) string { return res } - panic(fmt.Sprintf("invalid %s key prefix: %s", host.ModuleName, string(kvA.Key))) + panic(fmt.Sprintf("invalid %s key prefix: %s", ibcexported.ModuleName, string(kvA.Key))) } } diff --git a/modules/core/simulation/decoder_test.go b/modules/core/simulation/decoder_test.go index f16d824c579..bf0388ccd69 100644 --- a/modules/core/simulation/decoder_test.go +++ b/modules/core/simulation/decoder_test.go @@ -7,13 +7,13 @@ import ( "github.com/cosmos/cosmos-sdk/types/kv" "github.com/stretchr/testify/require" - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - connectiontypes "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types" - channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" - "github.com/cosmos/ibc-go/v4/modules/core/simulation" - ibctmtypes "github.com/cosmos/ibc-go/v4/modules/light-clients/07-tendermint/types" - "github.com/cosmos/ibc-go/v4/testing/simapp" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + connectiontypes "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + "github.com/cosmos/ibc-go/v7/modules/core/simulation" + ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" + "github.com/cosmos/ibc-go/v7/testing/simapp" ) func TestDecodeStore(t *testing.T) { @@ -25,7 +25,7 @@ func TestDecodeStore(t *testing.T) { channelID := "channelidone" portID := "portidone" - clientState := &ibctmtypes.ClientState{ + clientState := &ibctm.ClientState{ FrozenHeight: clienttypes.NewHeight(0, 10), } connection := connectiontypes.ConnectionEnd{ diff --git a/modules/core/simulation/genesis.go b/modules/core/simulation/genesis.go index 9f5e0bc7f8d..187d986fd5f 100644 --- a/modules/core/simulation/genesis.go +++ b/modules/core/simulation/genesis.go @@ -9,14 +9,14 @@ import ( "github.com/cosmos/cosmos-sdk/types/module" - clientsims "github.com/cosmos/ibc-go/v4/modules/core/02-client/simulation" - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - connectionsims "github.com/cosmos/ibc-go/v4/modules/core/03-connection/simulation" - connectiontypes "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types" - channelsims "github.com/cosmos/ibc-go/v4/modules/core/04-channel/simulation" - channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" - "github.com/cosmos/ibc-go/v4/modules/core/types" + clientsims "github.com/cosmos/ibc-go/v7/modules/core/02-client/simulation" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + connectionsims "github.com/cosmos/ibc-go/v7/modules/core/03-connection/simulation" + connectiontypes "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" + channelsims "github.com/cosmos/ibc-go/v7/modules/core/04-channel/simulation" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + ibcexported "github.com/cosmos/ibc-go/v7/modules/core/exported" + "github.com/cosmos/ibc-go/v7/modules/core/types" ) // Simulation parameter constants @@ -59,6 +59,6 @@ func RandomizedGenState(simState *module.SimulationState) { if err != nil { panic(err) } - fmt.Printf("Selected randomly generated %s parameters:\n%s\n", host.ModuleName, bz) - simState.GenState[host.ModuleName] = simState.Cdc.MustMarshalJSON(&ibcGenesis) + fmt.Printf("Selected randomly generated %s parameters:\n%s\n", ibcexported.ModuleName, bz) + simState.GenState[ibcexported.ModuleName] = simState.Cdc.MustMarshalJSON(&ibcGenesis) } diff --git a/modules/core/simulation/genesis_test.go b/modules/core/simulation/genesis_test.go index 20946fd5fd3..55dde9df7a9 100644 --- a/modules/core/simulation/genesis_test.go +++ b/modules/core/simulation/genesis_test.go @@ -5,21 +5,24 @@ import ( "math/rand" "testing" + "cosmossdk.io/math" "github.com/cosmos/cosmos-sdk/codec" codectypes "github.com/cosmos/cosmos-sdk/codec/types" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" "github.com/cosmos/cosmos-sdk/types/module" simtypes "github.com/cosmos/cosmos-sdk/types/simulation" "github.com/stretchr/testify/require" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" - "github.com/cosmos/ibc-go/v4/modules/core/simulation" - "github.com/cosmos/ibc-go/v4/modules/core/types" + ibcexported "github.com/cosmos/ibc-go/v7/modules/core/exported" + "github.com/cosmos/ibc-go/v7/modules/core/simulation" + "github.com/cosmos/ibc-go/v7/modules/core/types" ) // TestRandomizedGenState tests the normal scenario of applying RandomizedGenState. // Abonormal scenarios are not tested here. func TestRandomizedGenState(t *testing.T) { interfaceRegistry := codectypes.NewInterfaceRegistry() + cryptocodec.RegisterInterfaces(interfaceRegistry) cdc := codec.NewProtoCodec(interfaceRegistry) s := rand.NewSource(1) @@ -31,7 +34,7 @@ func TestRandomizedGenState(t *testing.T) { Rand: r, NumBonded: 3, Accounts: simtypes.RandomAccounts(r, 3), - InitialStake: 1000, + InitialStake: math.NewInt(1000), GenState: make(map[string]json.RawMessage), } @@ -41,7 +44,7 @@ func TestRandomizedGenState(t *testing.T) { simulation.RandomizedGenState(&simState) var ibcGenesis types.GenesisState - simState.Cdc.MustUnmarshalJSON(simState.GenState[host.ModuleName], &ibcGenesis) + simState.Cdc.MustUnmarshalJSON(simState.GenState[ibcexported.ModuleName], &ibcGenesis) require.NotNil(t, ibcGenesis.ClientGenesis) require.NotNil(t, ibcGenesis.ConnectionGenesis) diff --git a/modules/core/spec/01_concepts.md b/modules/core/spec/01_concepts.md index 0ab6fa70710..3a06684f655 100644 --- a/modules/core/spec/01_concepts.md +++ b/modules/core/spec/01_concepts.md @@ -6,58 +6,58 @@ order: 1 ## Client Misbehaviour -IBC clients must freeze when the counterparty chain becomes byzantine and -takes actions that could fool the light client into accepting invalid state -transitions. Thus, relayers are able to submit Misbehaviour proofs that prove -that a counterparty chain has signed two Headers for the same height. This -constitutes misbehaviour as the IBC client could have accepted either header -as valid. Upon verifying the misbehaviour the IBC client must freeze at that +IBC clients must freeze when the counterparty chain becomes byzantine and +takes actions that could fool the light client into accepting invalid state +transitions. Thus, relayers are able to submit Misbehaviour proofs that prove +that a counterparty chain has signed two Headers for the same height. This +constitutes misbehaviour as the IBC client could have accepted either header +as valid. Upon verifying the misbehaviour the IBC client must freeze at that height so that any proof verifications for the frozen height or later fail. -Note, there is a difference between the chain-level Misbehaviour that IBC is -concerned with and the validator-level Evidence that Tendermint is concerned -with. Tendermint must be able to detect, submit, and punish any evidence of -individual validators breaking the Tendermint consensus protocol and attempting -to mount an attack. IBC clients must only act when an attack is successful -and the chain has successfully forked. In this case, valid Headers submitted +Note, there is a difference between the chain-level Misbehaviour that IBC is +concerned with and the validator-level Evidence that Tendermint is concerned +with. Tendermint must be able to detect, submit, and punish any evidence of +individual validators breaking the Tendermint consensus protocol and attempting +to mount an attack. IBC clients must only act when an attack is successful +and the chain has successfully forked. In this case, valid Headers submitted to the IBC client can no longer be trusted and the client must freeze. -Governance may then choose to override a frozen client and provide the correct, -canonical Header so that the client can continue operating after the Misbehaviour +Governance may then choose to override a frozen client and provide the correct, +canonical Header so that the client can continue operating after the Misbehaviour submission. ## Connection Handshake The connection handshake occurs in 4 steps as defined in [ICS 03](https://github.com/cosmos/ibc/blob/master/spec/core/ics-003-connection-semantics). -`ConnOpenInit` is the first attempt to initialize a connection on the executing chain. -The handshake is expected to succeed if the version selected is supported. The connection +`ConnOpenInit` is the first attempt to initialize a connection on the executing chain. +The handshake is expected to succeed if the version selected is supported. The connection identifier for the counterparty connection must be left empty indicating that the counterparty -must select its own identifier. The connection identifier is auto derived in the format: +must select its own identifier. The connection identifier is auto derived in the format: `connection{N}` where N is the next sequence to be used. The counter begins at 0 and increments by 1. The connection is set and stored in the INIT state upon success. `ConnOpenTry` is a response to a chain executing `ConnOpenInit`. The executing chain will validate -the chain level parameters the counterparty has stored such as its chainID. The executing chain -will also verify that if a previous connection exists for the specified connection identifier -that all the parameters match and its previous state was in INIT. This may occur when both +the chain level parameters the counterparty has stored such as its chainID. The executing chain +will also verify that if a previous connection exists for the specified connection identifier +that all the parameters match and its previous state was in INIT. This may occur when both chains execute `ConnOpenInit` simultaneously. If the connection does not exist then a connection identifier is generated in the same format done in `ConnOpenInit`. The executing chain will verify -that the counterparty created a connection in INIT state. The executing chain will also verify -The `ClientState` and `ConsensusState` the counterparty stores for the executing chain. The -executing chain will select a version from the intersection of its supported versions and the -versions set by the counterparty. The connection is set and stored in the TRYOPEN state upon -success. +that the counterparty created a connection in INIT state. The executing chain will also verify +The `ClientState` and `ConsensusState` the counterparty stores for the executing chain. The +executing chain will select a version from the intersection of its supported versions and the +versions set by the counterparty. The connection is set and stored in the TRYOPEN state upon +success. `ConnOpenAck` may be called on a chain when the counterparty connection has entered TRYOPEN. A previous connection on the executing chain must exist in either INIT or TRYOPEN. The executing -chain will verify the version the counterparty selected. If the counterparty selected its own -connection identifier, it will be validated in the basic validation of a `MsgConnOpenAck`. +chain will verify the version the counterparty selected. If the counterparty selected its own +connection identifier, it will be validated in the basic validation of a `MsgConnOpenAck`. The counterparty connection state is verified along with the `ClientState` and `ConsensusState` stored for the executing chain. The connection is set and stored in the OPEN state upon success. `ConnOpenConfirm` is a response to a chain executing `ConnOpenAck`. The executing chain's connection -must be in TRYOPEN. The counterparty connection state is verified to be in the OPEN state. The +must be in TRYOPEN. The counterparty connection state is verified to be in the OPEN state. The connection is set and stored in the OPEN state upon success. ## Connection Version Negotiation @@ -72,7 +72,7 @@ versions is from most preferred to least preferred. This is not a strict requirement for the SDK implementation of IBC because the party calling `ConnOpenTry` will greedily select the latest version it supports that the counterparty supports as well. A specific version can optionally be passed -as `Version` to ensure that the handshake will either complete with that +as `Version` to ensure that the handshake will either complete with that version or fail. During `ConnOpenTry`, party B will select a version from the counterparty's @@ -83,19 +83,18 @@ During `ConnOpenAck`, party A will verify that they can support the version party B selected. If they do not support the selected version an error is returned. After this step, the connection version is considered agreed upon. - A `Version` is defined as follows: ```go type Version struct { - // unique version identifier - Identifier string - // list of features compatible with the specified identifier - Features []string + // unique version identifier + Identifier string + // list of features compatible with the specified identifier + Features []string } ``` -A version must contain a non empty identifier. Empty feature sets are allowed, but each +A version must contain a non empty identifier. Empty feature sets are allowed, but each feature must be a non empty string. ::: warning @@ -105,7 +104,6 @@ with regards to version selection in `ConnOpenTry`. Each version in a set of versions should have a unique version identifier. ::: - ## Channel Version Negotiation During the channel handshake procedure a version must be agreed upon between @@ -128,7 +126,7 @@ the version proposed by party B using the `MsgChanOpenAck` `CounterpartyVersion` field. The application module should throw an error if the version string is not valid. -In general empty version strings are to be considered valid options for an +In general empty version strings are to be considered valid options for an application module. Application modules may implement their own versioning system, such as semantic @@ -145,70 +143,69 @@ basic string matching using a single compatible version. Terminology: **Packet Commitment** A hash of the packet stored on the sending chain. -**Packet Receipt** A single bit indicating that a packet has been received. -Used for timeouts. +**Packet Receipt** A single bit indicating that a packet has been received. +Used for timeouts. **Acknowledgement** Data written to indicate the result of receiving a packet. Typically conveying either success or failure of the receive. A packet may be associated with one of the following states: + - the packet does not exist (ie it has not been sent) -- the packet has been sent but not received (the packet commitment exists on the +- the packet has been sent but not received (the packet commitment exists on the sending chain, but no receipt exists on the receiving chain) -- the packet has been received but not acknowledged (packet commitment exists +- the packet has been received but not acknowledged (packet commitment exists on the sending chain, a receipt exists on the receiving chain, but no acknowledgement exists on the receiving chain) -- the packet has been acknowledgement but the acknowledgement has not been relayed +- the packet has been acknowledgement but the acknowledgement has not been relayed (the packet commitment exists on the sending chain, the receipt and acknowledgement exist on the receiving chain) - the packet has completed its life cycle (the packet commitment does not exist on the sending chain, but a receipt and acknowledgement exist on the receiving chain) -Sending of a packet is initiated by a call to the `ChannelKeeper.SendPacket` +Sending of a packet is initiated by a call to the `ChannelKeeper.SendPacket` function by an application module. Packets being sent will be verified for correctness (core logic only). If the packet is valid, a hash of the packet will be stored as a packet commitment using the packet sequence in the key. Packet commitments are stored on the sending chain. A message should be sent to the receving chain indicating that the packet -has been committed on the sending chain and should be received on the +has been committed on the sending chain and should be received on the receiving chain. The light client on the receiving chain, which verifies the sending chain's state, should be updated to the lastest sending chain state if possible. The verification will fail if the latest state of the light client does not include the packet commitment. The receiving chain -is responsible for verifying that the counterparty set the hash of the +is responsible for verifying that the counterparty set the hash of the packet. If verification of the packet to be received is successful, the receiving chain should store a receipt of the packet and call application logic if necessary. An acknowledgement may be processed and stored at this time (synchronously) -or at another point in the future (asynchronously). +or at another point in the future (asynchronously). -Acknowledgements written on the receiving chain may be verified on the +Acknowledgements written on the receiving chain may be verified on the sending chain. If the sending chain successfully verifies the acknowledgement then it may delete the packet commitment stored at that sequence. There is no requirement for acknowledgements to be written. Only the hash of the acknowledgement is stored on the chain. Application logic may be executed in conjunction with verifying an acknowledgement. For example, in fungible cross-chain token transfer, a failed acknowledgement results in locked or -burned funds being refunded. +burned funds being refunded. -Relayers are responsible for reconstructing packets between the sending, -receiving, and acknowledging of packets. +Relayers are responsible for reconstructing packets between the sending, +receiving, and acknowledging of packets. IBC applications sending and receiving packets are expected to appropriately -handle data contained within a packet. For example, cross-chain token +handle data contained within a packet. For example, cross-chain token transfers will unmarshal the data into proto definitions representing -a token transfer. +a token transfer. -Future optimizations may allow for storage cleanup. Stored packet +Future optimizations may allow for storage cleanup. Stored packet commitments could be removed from channels which do not write packet acknowledgements and acknowledgements could be removed when a packet has completed its life cycle. -on channel closure will additionally verify that the counterparty channel has +on channel closure will additionally verify that the counterparty channel has been closed. A successful timeout may execute application logic as appropriate. -Both the packet's timeout timestamp and the timeout height must have been -surpassed on the receiving chain for a timeout to be valid. A timeout timestamp -or timeout height with a 0 value indicates the timeout field may be ignored. -Each packet is required to have at least one valid timeout field. - - +Both the packet's timeout timestamp and the timeout height must have been +surpassed on the receiving chain for a timeout to be valid. A timeout timestamp +or timeout height with a 0 value indicates the timeout field may be ignored. +Each packet is required to have at least one valid timeout field. diff --git a/modules/core/types/codec.go b/modules/core/types/codec.go index c879aa3234e..447b05c849b 100644 --- a/modules/core/types/codec.go +++ b/modules/core/types/codec.go @@ -3,13 +3,10 @@ package types import ( codectypes "github.com/cosmos/cosmos-sdk/codec/types" - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - connectiontypes "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types" - channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" - commitmenttypes "github.com/cosmos/ibc-go/v4/modules/core/23-commitment/types" - solomachinetypes "github.com/cosmos/ibc-go/v4/modules/light-clients/06-solomachine/types" - ibctmtypes "github.com/cosmos/ibc-go/v4/modules/light-clients/07-tendermint/types" - localhosttypes "github.com/cosmos/ibc-go/v4/modules/light-clients/09-localhost/types" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + connectiontypes "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + commitmenttypes "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/types" ) // RegisterInterfaces registers x/ibc interfaces into protobuf Any. @@ -17,8 +14,5 @@ func RegisterInterfaces(registry codectypes.InterfaceRegistry) { clienttypes.RegisterInterfaces(registry) connectiontypes.RegisterInterfaces(registry) channeltypes.RegisterInterfaces(registry) - solomachinetypes.RegisterInterfaces(registry) - ibctmtypes.RegisterInterfaces(registry) - localhosttypes.RegisterInterfaces(registry) commitmenttypes.RegisterInterfaces(registry) } diff --git a/modules/core/types/genesis.go b/modules/core/types/genesis.go index 36a346cafff..bc14953cb0d 100644 --- a/modules/core/types/genesis.go +++ b/modules/core/types/genesis.go @@ -3,9 +3,9 @@ package types import ( codectypes "github.com/cosmos/cosmos-sdk/codec/types" - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - connectiontypes "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types" - channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + connectiontypes "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" ) var _ codectypes.UnpackInterfacesMessage = GenesisState{} diff --git a/modules/core/types/genesis.pb.go b/modules/core/types/genesis.pb.go index 6072e44d1bd..05dea516395 100644 --- a/modules/core/types/genesis.pb.go +++ b/modules/core/types/genesis.pb.go @@ -5,11 +5,11 @@ package types import ( fmt "fmt" - types "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - types1 "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types" - types2 "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" - _ "github.com/gogo/protobuf/gogoproto" - proto "github.com/gogo/protobuf/proto" + _ "github.com/cosmos/gogoproto/gogoproto" + proto "github.com/cosmos/gogoproto/proto" + types "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + types1 "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" + types2 "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" io "io" math "math" math_bits "math/bits" @@ -100,24 +100,24 @@ var fileDescriptor_b9a49c5663e6fc59 = []byte{ // 324 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x64, 0x92, 0xb1, 0x4e, 0xeb, 0x30, 0x14, 0x86, 0x93, 0x5e, 0xe9, 0x0e, 0x01, 0x8a, 0x1a, 0x01, 0x82, 0x4a, 0xb8, 0x6d, 0xd4, 0x81, - 0x05, 0x5b, 0x05, 0x26, 0xc6, 0x2e, 0x30, 0x87, 0x8d, 0x05, 0x25, 0xc6, 0xa4, 0x46, 0x89, 0x4f, - 0x55, 0xbb, 0x91, 0xfa, 0x16, 0x3c, 0x56, 0xc7, 0x8e, 0x88, 0xa1, 0x42, 0xc9, 0x1b, 0xf0, 0x04, - 0xa8, 0xb1, 0x49, 0x52, 0x79, 0x8b, 0xfe, 0xf3, 0x9d, 0xff, 0x3b, 0x4a, 0xe2, 0x0d, 0x78, 0x4c, - 0x09, 0x85, 0x05, 0x23, 0x6a, 0x35, 0x67, 0x92, 0xe4, 0x13, 0x92, 0x30, 0xc1, 0x24, 0x97, 0x78, - 0xbe, 0x00, 0x05, 0x7e, 0x8f, 0xc7, 0x14, 0xef, 0x00, 0x5c, 0x01, 0x38, 0x9f, 0xf4, 0x4f, 0x12, - 0x48, 0xa0, 0x9a, 0x92, 0xdd, 0x93, 0x06, 0xfb, 0xc3, 0xba, 0x89, 0xa6, 0x9c, 0x09, 0x65, 0x55, - 0xf5, 0xc7, 0x0d, 0x01, 0x42, 0x30, 0xaa, 0x38, 0x08, 0x9b, 0x1a, 0x35, 0xd4, 0x2c, 0x12, 0x82, - 0xa5, 0x16, 0x12, 0x7c, 0x75, 0xbc, 0xc3, 0x07, 0x9d, 0x3c, 0xa9, 0x48, 0x31, 0xff, 0xcd, 0xeb, - 0x6a, 0xe9, 0x8b, 0x01, 0xcf, 0xdd, 0xa1, 0x7b, 0x75, 0x70, 0x33, 0xc4, 0xf5, 0xf5, 0x7a, 0x8e, - 0xf3, 0x09, 0x6e, 0x6f, 0x4e, 0x2f, 0xd7, 0xdb, 0x81, 0xf3, 0xb3, 0x1d, 0x9c, 0xae, 0xa2, 0x2c, - 0xbd, 0x0f, 0xf6, 0x5b, 0x82, 0xf0, 0x48, 0x07, 0x66, 0xc5, 0xcf, 0x3d, 0xbf, 0x39, 0xbd, 0x76, - 0x75, 0x2a, 0xd7, 0xb8, 0xe5, 0xaa, 0x19, 0xcb, 0x37, 0x32, 0xbe, 0x0b, 0xe3, 0xb3, 0xda, 0x82, - 0xb0, 0xd7, 0x84, 0x7f, 0xde, 0x77, 0xef, 0xd8, 0xbc, 0x8c, 0x5a, 0xfa, 0xaf, 0x92, 0x8e, 0x5a, - 0x52, 0x0d, 0x58, 0x46, 0x64, 0x8c, 0x67, 0xc6, 0xb8, 0xdf, 0x13, 0x84, 0x5d, 0x93, 0x98, 0xa5, - 0xe9, 0xe3, 0xba, 0x40, 0xee, 0xa6, 0x40, 0xee, 0x77, 0x81, 0xdc, 0x8f, 0x12, 0x39, 0x9b, 0x12, - 0x39, 0x9f, 0x25, 0x72, 0x9e, 0x71, 0xc2, 0xd5, 0x6c, 0x19, 0x63, 0x0a, 0x19, 0xa1, 0x20, 0x33, - 0x90, 0x84, 0xc7, 0xf4, 0x3a, 0x01, 0x92, 0xdf, 0x91, 0x0c, 0x5e, 0x97, 0x29, 0x93, 0xad, 0x7f, - 0x29, 0xfe, 0x5f, 0x7d, 0xad, 0xdb, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, 0xd2, 0xbe, 0x54, 0x19, + 0x05, 0x5b, 0x85, 0x01, 0x89, 0xb1, 0x0b, 0xcc, 0x61, 0x63, 0x41, 0x89, 0x31, 0xa9, 0x51, 0xe2, + 0x53, 0xd5, 0x6e, 0xa4, 0xbe, 0x05, 0x8f, 0xd5, 0xb1, 0x23, 0x62, 0xa8, 0x50, 0xf2, 0x06, 0x3c, + 0x01, 0x6a, 0x6c, 0x92, 0x54, 0xde, 0xa2, 0xff, 0x7c, 0xe7, 0xff, 0x8e, 0x92, 0x78, 0x03, 0x1e, + 0x53, 0x42, 0x61, 0xc1, 0x88, 0x5a, 0xcd, 0x99, 0x24, 0xf9, 0x84, 0x24, 0x4c, 0x30, 0xc9, 0x25, + 0x9e, 0x2f, 0x40, 0x81, 0xdf, 0xe3, 0x31, 0xc5, 0x3b, 0x00, 0x57, 0x00, 0xce, 0x27, 0xfd, 0x93, + 0x04, 0x12, 0xa8, 0xa6, 0x64, 0xf7, 0xa4, 0xc1, 0xfe, 0xb0, 0x6e, 0xa2, 0x29, 0x67, 0x42, 0x59, + 0x55, 0xfd, 0x71, 0x43, 0x80, 0x10, 0x8c, 0x2a, 0x0e, 0xc2, 0xa6, 0x46, 0x0d, 0x35, 0x8b, 0x84, + 0x60, 0xa9, 0x85, 0x04, 0x5f, 0x1d, 0xef, 0xf0, 0x41, 0x27, 0x4f, 0x2a, 0x52, 0xcc, 0x7f, 0xf3, + 0xba, 0x5a, 0xfa, 0x62, 0xc0, 0x73, 0x77, 0xe8, 0x5e, 0x1d, 0xdc, 0x0c, 0x71, 0x7d, 0xbd, 0x9e, + 0xe3, 0x7c, 0x82, 0xdb, 0x9b, 0xd3, 0xcb, 0xf5, 0x76, 0xe0, 0xfc, 0x6c, 0x07, 0xa7, 0xab, 0x28, + 0x4b, 0xef, 0x83, 0xfd, 0x96, 0x20, 0x3c, 0xd2, 0x81, 0x59, 0xf1, 0x73, 0xcf, 0x6f, 0x4e, 0xaf, + 0x5d, 0x9d, 0xca, 0x35, 0x6e, 0xb9, 0x6a, 0xc6, 0xf2, 0x8d, 0x8c, 0xef, 0xc2, 0xf8, 0xac, 0xb6, + 0x20, 0xec, 0x35, 0xe1, 0x9f, 0xf7, 0xdd, 0x3b, 0x36, 0x2f, 0xa3, 0x96, 0xfe, 0xab, 0xa4, 0xa3, + 0x96, 0x54, 0x03, 0x96, 0x11, 0x19, 0xe3, 0x99, 0x31, 0xee, 0xf7, 0x04, 0x61, 0xd7, 0x24, 0x66, + 0x69, 0xfa, 0xb8, 0x2e, 0x90, 0xbb, 0x29, 0x90, 0xfb, 0x5d, 0x20, 0xf7, 0xa3, 0x44, 0xce, 0xa6, + 0x44, 0xce, 0x67, 0x89, 0x9c, 0x67, 0x9c, 0x70, 0x35, 0x5b, 0xc6, 0x98, 0x42, 0x46, 0x28, 0xc8, + 0x0c, 0x24, 0xe1, 0x31, 0xbd, 0x4e, 0x80, 0xe4, 0x77, 0x24, 0x83, 0xd7, 0x65, 0xca, 0x64, 0xeb, + 0x5f, 0x8a, 0xff, 0x57, 0x5f, 0xeb, 0xf6, 0x37, 0x00, 0x00, 0xff, 0xff, 0xf5, 0xb9, 0x8a, 0x1b, 0x64, 0x02, 0x00, 0x00, } diff --git a/modules/core/types/query.go b/modules/core/types/query.go index 6ebeb77ba5e..c79190a84c6 100644 --- a/modules/core/types/query.go +++ b/modules/core/types/query.go @@ -1,14 +1,14 @@ package types import ( - "github.com/gogo/protobuf/grpc" + "github.com/cosmos/gogoproto/grpc" - client "github.com/cosmos/ibc-go/v4/modules/core/02-client" - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - connection "github.com/cosmos/ibc-go/v4/modules/core/03-connection" - connectiontypes "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types" - channel "github.com/cosmos/ibc-go/v4/modules/core/04-channel" - channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" + client "github.com/cosmos/ibc-go/v7/modules/core/02-client" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + connection "github.com/cosmos/ibc-go/v7/modules/core/03-connection" + connectiontypes "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" + channel "github.com/cosmos/ibc-go/v7/modules/core/04-channel" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" ) // QueryServer defines the IBC interfaces that the gRPC query server must implement diff --git a/modules/light-clients/06-solomachine/client_state.go b/modules/light-clients/06-solomachine/client_state.go new file mode 100644 index 00000000000..2c316dacaec --- /dev/null +++ b/modules/light-clients/06-solomachine/client_state.go @@ -0,0 +1,247 @@ +package solomachine + +import ( + "reflect" + + errorsmod "cosmossdk.io/errors" + "github.com/cosmos/cosmos-sdk/codec" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/tx/signing" + + ibcerrors "github.com/cosmos/ibc-go/v7/internal/errors" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + commitmenttypes "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + "github.com/cosmos/ibc-go/v7/modules/core/exported" +) + +var _ exported.ClientState = (*ClientState)(nil) + +// NewClientState creates a new ClientState instance. +func NewClientState(latestSequence uint64, consensusState *ConsensusState) *ClientState { + return &ClientState{ + Sequence: latestSequence, + IsFrozen: false, + ConsensusState: consensusState, + } +} + +// ClientType is Solo Machine. +func (cs ClientState) ClientType() string { + return exported.Solomachine +} + +// GetLatestHeight returns the latest sequence number. +// Return exported.Height to satisfy ClientState interface +// Revision number is always 0 for a solo-machine. +func (cs ClientState) GetLatestHeight() exported.Height { + return clienttypes.NewHeight(0, cs.Sequence) +} + +// GetTimestampAtHeight returns the timestamp in nanoseconds of the consensus state at the given height. +func (cs ClientState) GetTimestampAtHeight( + _ sdk.Context, + clientStore sdk.KVStore, + cdc codec.BinaryCodec, + height exported.Height, +) (uint64, error) { + return cs.ConsensusState.Timestamp, nil +} + +// Status returns the status of the solo machine client. +// The client may be: +// - Active: if frozen sequence is 0 +// - Frozen: otherwise solo machine is frozen +func (cs ClientState) Status(_ sdk.Context, _ sdk.KVStore, _ codec.BinaryCodec) exported.Status { + if cs.IsFrozen { + return exported.Frozen + } + + return exported.Active +} + +// Validate performs basic validation of the client state fields. +func (cs ClientState) Validate() error { + if cs.Sequence == 0 { + return errorsmod.Wrap(clienttypes.ErrInvalidClient, "sequence cannot be 0") + } + if cs.ConsensusState == nil { + return errorsmod.Wrap(clienttypes.ErrInvalidConsensus, "consensus state cannot be nil") + } + return cs.ConsensusState.ValidateBasic() +} + +// ZeroCustomFields is not implemented for solo machine +func (cs ClientState) ZeroCustomFields() exported.ClientState { + panic("ZeroCustomFields is not implemented as the solo machine implementation does not support upgrades.") +} + +// Initialize checks that the initial consensus state is equal to the latest consensus state of the initial client and +// sets the client state in the provided client store. +func (cs ClientState) Initialize(_ sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, consState exported.ConsensusState) error { + if !reflect.DeepEqual(cs.ConsensusState, consState) { + return errorsmod.Wrapf(clienttypes.ErrInvalidConsensus, "consensus state in initial client does not equal initial consensus state. expected: %s, got: %s", + cs.ConsensusState, consState) + } + + setClientState(clientStore, cdc, &cs) + + return nil +} + +// ExportMetadata is a no-op since solomachine does not store any metadata in client store +func (cs ClientState) ExportMetadata(_ sdk.KVStore) []exported.GenesisMetadata { + return nil +} + +// VerifyUpgradeAndUpdateState returns an error since solomachine client does not support upgrades +func (cs ClientState) VerifyUpgradeAndUpdateState( + _ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, + _ exported.ClientState, _ exported.ConsensusState, _, _ []byte, +) error { + return errorsmod.Wrap(clienttypes.ErrInvalidUpgradeClient, "cannot upgrade solomachine client") +} + +// VerifyMembership is a generic proof verification method which verifies a proof of the existence of a value at a given CommitmentPath at the latest sequence. +// The caller is expected to construct the full CommitmentPath from a CommitmentPrefix and a standardized path (as defined in ICS 24). +func (cs *ClientState) VerifyMembership( + ctx sdk.Context, + clientStore sdk.KVStore, + cdc codec.BinaryCodec, + _ exported.Height, + delayTimePeriod uint64, + delayBlockPeriod uint64, + proof []byte, + path exported.Path, + value []byte, +) error { + publicKey, sigData, timestamp, sequence, err := produceVerificationArgs(cdc, cs, proof) + if err != nil { + return err + } + + merklePath, ok := path.(commitmenttypes.MerklePath) + if !ok { + return errorsmod.Wrapf(ibcerrors.ErrInvalidType, "expected %T, got %T", commitmenttypes.MerklePath{}, path) + } + + if merklePath.Empty() { + return errorsmod.Wrap(commitmenttypes.ErrInvalidProof, "path is empty") + } + + signBytes := &SignBytes{ + Sequence: sequence, + Timestamp: timestamp, + Diversifier: cs.ConsensusState.Diversifier, + Path: []byte(merklePath.String()), + Data: value, + } + + signBz, err := cdc.Marshal(signBytes) + if err != nil { + return err + } + + if err := VerifySignature(publicKey, signBz, sigData); err != nil { + return err + } + + cs.Sequence++ + cs.ConsensusState.Timestamp = timestamp + setClientState(clientStore, cdc, cs) + + return nil +} + +// VerifyNonMembership is a generic proof verification method which verifies the absence of a given CommitmentPath at the latest sequence. +// The caller is expected to construct the full CommitmentPath from a CommitmentPrefix and a standardized path (as defined in ICS 24). +func (cs *ClientState) VerifyNonMembership( + ctx sdk.Context, + clientStore sdk.KVStore, + cdc codec.BinaryCodec, + _ exported.Height, + delayTimePeriod uint64, + delayBlockPeriod uint64, + proof []byte, + path exported.Path, +) error { + publicKey, sigData, timestamp, sequence, err := produceVerificationArgs(cdc, cs, proof) + if err != nil { + return err + } + + merklePath, ok := path.(commitmenttypes.MerklePath) + if !ok { + return errorsmod.Wrapf(ibcerrors.ErrInvalidType, "expected %T, got %T", commitmenttypes.MerklePath{}, path) + } + + signBytes := &SignBytes{ + Sequence: sequence, + Timestamp: timestamp, + Diversifier: cs.ConsensusState.Diversifier, + Path: []byte(merklePath.String()), + Data: nil, + } + + signBz, err := cdc.Marshal(signBytes) + if err != nil { + return err + } + + if err := VerifySignature(publicKey, signBz, sigData); err != nil { + return err + } + + cs.Sequence++ + cs.ConsensusState.Timestamp = timestamp + setClientState(clientStore, cdc, cs) + + return nil +} + +// produceVerificationArgs perfoms the basic checks on the arguments that are +// shared between the verification functions and returns the public key of the +// consensus state, the unmarshalled proof representing the signature and timestamp. +func produceVerificationArgs( + cdc codec.BinaryCodec, + cs *ClientState, + proof []byte, +) (cryptotypes.PubKey, signing.SignatureData, uint64, uint64, error) { + if proof == nil { + return nil, nil, 0, 0, errorsmod.Wrap(ErrInvalidProof, "proof cannot be empty") + } + + var timestampedSigData TimestampedSignatureData + if err := cdc.Unmarshal(proof, ×tampedSigData); err != nil { + return nil, nil, 0, 0, errorsmod.Wrapf(err, "failed to unmarshal proof into type %T", timestampedSigData) + } + + timestamp := timestampedSigData.Timestamp + if len(timestampedSigData.SignatureData) == 0 { + return nil, nil, 0, 0, errorsmod.Wrap(ErrInvalidProof, "signature data cannot be empty") + } + + sigData, err := UnmarshalSignatureData(cdc, timestampedSigData.SignatureData) + if err != nil { + return nil, nil, 0, 0, err + } + + if cs.ConsensusState.GetTimestamp() > timestamp { + return nil, nil, 0, 0, errorsmod.Wrapf(ErrInvalidProof, "the consensus state timestamp is greater than the signature timestamp (%d >= %d)", cs.ConsensusState.GetTimestamp(), timestamp) + } + + sequence := cs.GetLatestHeight().GetRevisionHeight() + publicKey, err := cs.ConsensusState.GetPubKey() + if err != nil { + return nil, nil, 0, 0, err + } + + return publicKey, sigData, timestamp, sequence, nil +} + +// sets the client state to the store +func setClientState(store sdk.KVStore, cdc codec.BinaryCodec, clientState exported.ClientState) { + bz := clienttypes.MustMarshalClientState(cdc, clientState) + store.Set([]byte(host.KeyClientState), bz) +} diff --git a/modules/light-clients/06-solomachine/client_state_test.go b/modules/light-clients/06-solomachine/client_state_test.go new file mode 100644 index 00000000000..33888252e4e --- /dev/null +++ b/modules/light-clients/06-solomachine/client_state_test.go @@ -0,0 +1,830 @@ +package solomachine_test + +import ( + "bytes" + + sdk "github.com/cosmos/cosmos-sdk/types" + + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + commitmenttypes "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + "github.com/cosmos/ibc-go/v7/modules/core/exported" + solomachine "github.com/cosmos/ibc-go/v7/modules/light-clients/06-solomachine" + ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" + ibctesting "github.com/cosmos/ibc-go/v7/testing" + ibcmock "github.com/cosmos/ibc-go/v7/testing/mock" +) + +const ( + counterpartyClientIdentifier = "chainA" + testConnectionID = "connectionid" + testChannelID = "testchannelid" + testPortID = "testportid" +) + +func (suite *SoloMachineTestSuite) TestStatus() { + clientState := suite.solomachine.ClientState() + // solo machine discards arguments + status := clientState.Status(suite.chainA.GetContext(), nil, nil) + suite.Require().Equal(exported.Active, status) + + // freeze solo machine + clientState.IsFrozen = true + status = clientState.Status(suite.chainA.GetContext(), nil, nil) + suite.Require().Equal(exported.Frozen, status) +} + +func (suite *SoloMachineTestSuite) TestClientStateValidateBasic() { + // test singlesig and multisig public keys + for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + + testCases := []struct { + name string + clientState *solomachine.ClientState + expPass bool + }{ + { + "valid client state", + sm.ClientState(), + true, + }, + { + "empty ClientState", + &solomachine.ClientState{}, + false, + }, + { + "sequence is zero", + solomachine.NewClientState(0, &solomachine.ConsensusState{sm.ConsensusState().PublicKey, sm.Diversifier, sm.Time}), + false, + }, + { + "timestamp is zero", + solomachine.NewClientState(1, &solomachine.ConsensusState{sm.ConsensusState().PublicKey, sm.Diversifier, 0}), + false, + }, + { + "diversifier is blank", + solomachine.NewClientState(1, &solomachine.ConsensusState{sm.ConsensusState().PublicKey, " ", 1}), + false, + }, + { + "pubkey is empty", + solomachine.NewClientState(1, &solomachine.ConsensusState{nil, sm.Diversifier, sm.Time}), + false, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + err := tc.clientState.Validate() + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } + } +} + +func (suite *SoloMachineTestSuite) TestInitialize() { + // test singlesig and multisig public keys + for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + malleatedConsensus := sm.ClientState().ConsensusState + malleatedConsensus.Timestamp += 10 + + testCases := []struct { + name string + consState exported.ConsensusState + expPass bool + }{ + { + "valid consensus state", + sm.ConsensusState(), + true, + }, + { + "nil consensus state", + nil, + false, + }, + { + "invalid consensus state: Tendermint consensus state", + &ibctm.ConsensusState{}, + false, + }, + { + "invalid consensus state: consensus state does not match consensus state in client", + malleatedConsensus, + false, + }, + } + + for _, tc := range testCases { + suite.SetupTest() + + store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), "solomachine") + err := sm.ClientState().Initialize( + suite.chainA.GetContext(), suite.chainA.Codec, + store, tc.consState, + ) + + if tc.expPass { + suite.Require().NoError(err, "valid testcase: %s failed", tc.name) + suite.Require().True(store.Has(host.ClientStateKey())) + } else { + suite.Require().Error(err, "invalid testcase: %s passed", tc.name) + suite.Require().False(store.Has(host.ClientStateKey())) + } + } + } +} + +func (suite *SoloMachineTestSuite) TestVerifyMembership() { + // test singlesig and multisig public keys + for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + + var ( + clientState *solomachine.ClientState + path exported.Path + proof []byte + testingPath *ibctesting.Path + signBytes solomachine.SignBytes + err error + ) + + testCases := []struct { + name string + malleate func() + expPass bool + }{ + { + "success", + func() {}, + true, + }, + { + "success: client state verification", + func() { + clientState = sm.ClientState() + clientStateBz, err := suite.chainA.Codec.Marshal(clientState) + suite.Require().NoError(err) + + path = suite.solomachine.GetClientStatePath(counterpartyClientIdentifier) + signBytes = solomachine.SignBytes{ + Sequence: sm.GetHeight().GetRevisionHeight(), + Timestamp: sm.Time, + Diversifier: sm.Diversifier, + Path: []byte(path.String()), + Data: clientStateBz, + } + + signBz, err := suite.chainA.Codec.Marshal(&signBytes) + suite.Require().NoError(err) + + sig := sm.GenerateSignature(signBz) + + signatureDoc := &solomachine.TimestampedSignatureData{ + SignatureData: sig, + Timestamp: sm.Time, + } + + proof, err = suite.chainA.Codec.Marshal(signatureDoc) + suite.Require().NoError(err) + }, + true, + }, + { + "success: consensus state verification", + func() { + clientState = sm.ClientState() + consensusState := clientState.ConsensusState + consensusStateBz, err := suite.chainA.Codec.Marshal(consensusState) + suite.Require().NoError(err) + + path = sm.GetConsensusStatePath(counterpartyClientIdentifier, clienttypes.NewHeight(0, 1)) + signBytes = solomachine.SignBytes{ + Sequence: sm.Sequence, + Timestamp: sm.Time, + Diversifier: sm.Diversifier, + Path: []byte(path.String()), + Data: consensusStateBz, + } + + signBz, err := suite.chainA.Codec.Marshal(&signBytes) + suite.Require().NoError(err) + + sig := sm.GenerateSignature(signBz) + + signatureDoc := &solomachine.TimestampedSignatureData{ + SignatureData: sig, + Timestamp: sm.Time, + } + + proof, err = suite.chainA.Codec.Marshal(signatureDoc) + suite.Require().NoError(err) + }, + true, + }, + { + "success: connection state verification", + func() { + suite.coordinator.SetupConnections(testingPath) + + connectionEnd, found := suite.chainA.GetSimApp().IBCKeeper.ConnectionKeeper.GetConnection(suite.chainA.GetContext(), ibctesting.FirstConnectionID) + suite.Require().True(found) + + connectionEndBz, err := suite.chainA.Codec.Marshal(&connectionEnd) + suite.Require().NoError(err) + + path = sm.GetConnectionStatePath(ibctesting.FirstConnectionID) + signBytes = solomachine.SignBytes{ + Sequence: sm.Sequence, + Timestamp: sm.Time, + Diversifier: sm.Diversifier, + Path: []byte(path.String()), + Data: connectionEndBz, + } + + signBz, err := suite.chainA.Codec.Marshal(&signBytes) + suite.Require().NoError(err) + + sig := sm.GenerateSignature(signBz) + + signatureDoc := &solomachine.TimestampedSignatureData{ + SignatureData: sig, + Timestamp: sm.Time, + } + + proof, err = suite.chainA.Codec.Marshal(signatureDoc) + suite.Require().NoError(err) + }, + true, + }, + { + "success: channel state verification", + func() { + suite.coordinator.SetupConnections(testingPath) + suite.coordinator.CreateMockChannels(testingPath) + + channelEnd, found := suite.chainA.GetSimApp().IBCKeeper.ChannelKeeper.GetChannel(suite.chainA.GetContext(), ibctesting.MockPort, ibctesting.FirstChannelID) + suite.Require().True(found) + + channelEndBz, err := suite.chainA.Codec.Marshal(&channelEnd) + suite.Require().NoError(err) + + path = sm.GetChannelStatePath(ibctesting.MockPort, ibctesting.FirstChannelID) + signBytes = solomachine.SignBytes{ + Sequence: sm.Sequence, + Timestamp: sm.Time, + Diversifier: sm.Diversifier, + Path: []byte(path.String()), + Data: channelEndBz, + } + + signBz, err := suite.chainA.Codec.Marshal(&signBytes) + suite.Require().NoError(err) + + sig := sm.GenerateSignature(signBz) + + signatureDoc := &solomachine.TimestampedSignatureData{ + SignatureData: sig, + Timestamp: sm.Time, + } + + proof, err = suite.chainA.Codec.Marshal(signatureDoc) + suite.Require().NoError(err) + }, + true, + }, + { + "success: next sequence recv verification", + func() { + suite.coordinator.SetupConnections(testingPath) + suite.coordinator.CreateMockChannels(testingPath) + + nextSeqRecv, found := suite.chainA.GetSimApp().IBCKeeper.ChannelKeeper.GetNextSequenceRecv(suite.chainA.GetContext(), ibctesting.MockPort, ibctesting.FirstChannelID) + suite.Require().True(found) + + path = sm.GetNextSequenceRecvPath(ibctesting.MockPort, ibctesting.FirstChannelID) + signBytes = solomachine.SignBytes{ + Sequence: sm.Sequence, + Timestamp: sm.Time, + Diversifier: sm.Diversifier, + Path: []byte(path.String()), + Data: sdk.Uint64ToBigEndian(nextSeqRecv), + } + + signBz, err := suite.chainA.Codec.Marshal(&signBytes) + suite.Require().NoError(err) + + sig := sm.GenerateSignature(signBz) + + signatureDoc := &solomachine.TimestampedSignatureData{ + SignatureData: sig, + Timestamp: sm.Time, + } + + proof, err = suite.chainA.Codec.Marshal(signatureDoc) + suite.Require().NoError(err) + }, + true, + }, + { + "success: packet commitment verification", + func() { + packet := channeltypes.NewPacket( + ibctesting.MockPacketData, + 1, + ibctesting.MockPort, + ibctesting.FirstChannelID, + ibctesting.MockPort, + ibctesting.FirstChannelID, + clienttypes.NewHeight(0, 10), + 0, + ) + + commitmentBz := channeltypes.CommitPacket(suite.chainA.Codec, packet) + path = sm.GetPacketCommitmentPath(packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) + signBytes = solomachine.SignBytes{ + Sequence: sm.Sequence, + Timestamp: sm.Time, + Diversifier: sm.Diversifier, + Path: []byte(path.String()), + Data: commitmentBz, + } + + signBz, err := suite.chainA.Codec.Marshal(&signBytes) + suite.Require().NoError(err) + + sig := sm.GenerateSignature(signBz) + + signatureDoc := &solomachine.TimestampedSignatureData{ + SignatureData: sig, + Timestamp: sm.Time, + } + + proof, err = suite.chainA.Codec.Marshal(signatureDoc) + suite.Require().NoError(err) + }, + true, + }, + { + "success: packet acknowledgement verification", + func() { + path = sm.GetPacketAcknowledgementPath(ibctesting.MockPort, ibctesting.FirstChannelID, 1) + signBytes = solomachine.SignBytes{ + Sequence: sm.Sequence, + Timestamp: sm.Time, + Diversifier: sm.Diversifier, + Path: []byte(path.String()), + Data: ibctesting.MockAcknowledgement, + } + + signBz, err := suite.chainA.Codec.Marshal(&signBytes) + suite.Require().NoError(err) + + sig := sm.GenerateSignature(signBz) + + signatureDoc := &solomachine.TimestampedSignatureData{ + SignatureData: sig, + Timestamp: sm.Time, + } + + proof, err = suite.chainA.Codec.Marshal(signatureDoc) + suite.Require().NoError(err) + }, + true, + }, + { + "success: packet receipt verification", + func() { + path = sm.GetPacketReceiptPath(ibctesting.MockPort, ibctesting.FirstChannelID, 1) + signBytes = solomachine.SignBytes{ + Sequence: sm.Sequence, + Timestamp: sm.Time, + Diversifier: sm.Diversifier, + Path: []byte(path.String()), + Data: []byte{byte(1)}, // packet receipt is stored as a single byte + } + + signBz, err := suite.chainA.Codec.Marshal(&signBytes) + suite.Require().NoError(err) + + sig := sm.GenerateSignature(signBz) + + signatureDoc := &solomachine.TimestampedSignatureData{ + SignatureData: sig, + Timestamp: sm.Time, + } + + proof, err = suite.chainA.Codec.Marshal(signatureDoc) + suite.Require().NoError(err) + }, + true, + }, + { + "invalid path type", + func() { + path = ibcmock.KeyPath{} + }, + false, + }, + { + "malformed proof fails to unmarshal", + func() { + path = suite.solomachine.GetClientStatePath(counterpartyClientIdentifier) + proof = []byte("invalid proof") + }, + false, + }, + { + "consensus state timestamp is greater than signature", + func() { + consensusState := &solomachine.ConsensusState{ + Timestamp: sm.Time + 1, + PublicKey: sm.ConsensusState().PublicKey, + } + + clientState = solomachine.NewClientState(sm.Sequence, consensusState) + }, + false, + }, + { + "signature data is nil", + func() { + signatureDoc := &solomachine.TimestampedSignatureData{ + SignatureData: nil, + Timestamp: sm.Time, + } + + proof, err = suite.chainA.Codec.Marshal(signatureDoc) + suite.Require().NoError(err) + }, + false, + }, + { + "consensus state public key is nil", + func() { + clientState.ConsensusState.PublicKey = nil + }, + false, + }, + { + "malformed signature data fails to unmarshal", + func() { + signatureDoc := &solomachine.TimestampedSignatureData{ + SignatureData: []byte("invalid signature data"), + Timestamp: sm.Time, + } + + proof, err = suite.chainA.Codec.Marshal(signatureDoc) + suite.Require().NoError(err) + }, + false, + }, + { + "proof is nil", + func() { + proof = nil + }, + false, + }, + { + "proof verification failed", + func() { + signBytes.Data = []byte("invalid membership data value") + }, + false, + }, + { + "empty path", + func() { + path = commitmenttypes.MerklePath{} + }, + false, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + suite.SetupTest() + testingPath = ibctesting.NewPath(suite.chainA, suite.chainB) + + clientState = sm.ClientState() + + path = commitmenttypes.NewMerklePath("ibc", "solomachine") + signBytes = solomachine.SignBytes{ + Sequence: sm.GetHeight().GetRevisionHeight(), + Timestamp: sm.Time, + Diversifier: sm.Diversifier, + Path: []byte(path.String()), + Data: []byte("solomachine"), + } + + signBz, err := suite.chainA.Codec.Marshal(&signBytes) + suite.Require().NoError(err) + + sig := sm.GenerateSignature(signBz) + + signatureDoc := &solomachine.TimestampedSignatureData{ + SignatureData: sig, + Timestamp: sm.Time, + } + + proof, err = suite.chainA.Codec.Marshal(signatureDoc) + suite.Require().NoError(err) + + tc.malleate() + + var expSeq uint64 + if clientState.ConsensusState != nil { + expSeq = clientState.Sequence + 1 + } + + err = clientState.VerifyMembership( + suite.chainA.GetContext(), suite.store, suite.chainA.Codec, + clienttypes.ZeroHeight(), 0, 0, // solomachine does not check delay periods + proof, path, signBytes.Data, + ) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().Equal(expSeq, clientState.Sequence) + suite.Require().Equal(expSeq, suite.GetSequenceFromStore(), "sequence not updated in the store (%d) on valid test case %s", suite.GetSequenceFromStore(), tc.name) + } else { + suite.Require().Error(err) + } + }) + } + } +} + +func (suite *SoloMachineTestSuite) TestSignBytesMarshalling() { + sm := suite.solomachine + merklePath := commitmenttypes.NewMerklePath("ibc", "solomachine") + signBytesNilData := solomachine.SignBytes{ + Sequence: sm.GetHeight().GetRevisionHeight(), + Timestamp: sm.Time, + Diversifier: sm.Diversifier, + Path: []byte(merklePath.String()), + Data: nil, + } + + signBytesEmptyArray := solomachine.SignBytes{ + Sequence: sm.GetHeight().GetRevisionHeight(), + Timestamp: sm.Time, + Diversifier: sm.Diversifier, + Path: []byte(merklePath.String()), + Data: []byte{}, + } + + signBzNil, err := suite.chainA.Codec.Marshal(&signBytesNilData) + suite.Require().NoError(err) + + signBzEmptyArray, err := suite.chainA.Codec.Marshal(&signBytesEmptyArray) + suite.Require().NoError(err) + + suite.Require().True(bytes.Equal(signBzNil, signBzEmptyArray)) +} + +func (suite *SoloMachineTestSuite) TestVerifyNonMembership() { + // test singlesig and multisig public keys + for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + + var ( + clientState *solomachine.ClientState + path exported.Path + proof []byte + signBytes solomachine.SignBytes + err error + ) + + testCases := []struct { + name string + malleate func() + expPass bool + }{ + { + "success", + func() {}, + true, + }, + { + "success: packet receipt absence verification", + func() { + path = suite.solomachine.GetPacketReceiptPath(ibctesting.MockPort, ibctesting.FirstChannelID, 1) + signBytes = solomachine.SignBytes{ + Sequence: sm.GetHeight().GetRevisionHeight(), + Timestamp: sm.Time, + Diversifier: sm.Diversifier, + Path: []byte(path.String()), + Data: nil, + } + + signBz, err := suite.chainA.Codec.Marshal(&signBytes) + suite.Require().NoError(err) + + sig := sm.GenerateSignature(signBz) + + signatureDoc := &solomachine.TimestampedSignatureData{ + SignatureData: sig, + Timestamp: sm.Time, + } + + proof, err = suite.chainA.Codec.Marshal(signatureDoc) + suite.Require().NoError(err) + }, + true, + }, + { + "invalid path type", + func() { + path = ibcmock.KeyPath{} + }, + false, + }, + { + "malformed proof fails to unmarshal", + func() { + path = suite.solomachine.GetClientStatePath(counterpartyClientIdentifier) + proof = []byte("invalid proof") + }, + false, + }, + { + "consensus state timestamp is greater than signature", + func() { + consensusState := &solomachine.ConsensusState{ + Timestamp: sm.Time + 1, + PublicKey: sm.ConsensusState().PublicKey, + } + + clientState = solomachine.NewClientState(sm.Sequence, consensusState) + }, + false, + }, + { + "signature data is nil", + func() { + signatureDoc := &solomachine.TimestampedSignatureData{ + SignatureData: nil, + Timestamp: sm.Time, + } + + proof, err = suite.chainA.Codec.Marshal(signatureDoc) + suite.Require().NoError(err) + }, + false, + }, + { + "consensus state public key is nil", + func() { + clientState.ConsensusState.PublicKey = nil + }, + false, + }, + { + "malformed signature data fails to unmarshal", + func() { + signatureDoc := &solomachine.TimestampedSignatureData{ + SignatureData: []byte("invalid signature data"), + Timestamp: sm.Time, + } + + proof, err = suite.chainA.Codec.Marshal(signatureDoc) + suite.Require().NoError(err) + }, + false, + }, + { + "proof is nil", + func() { + proof = nil + }, + false, + }, + { + "proof verification failed", + func() { + signBytes.Data = []byte("invalid non-membership data value") + + signBz, err := suite.chainA.Codec.Marshal(&signBytes) + suite.Require().NoError(err) + + sig := sm.GenerateSignature(signBz) + + signatureDoc := &solomachine.TimestampedSignatureData{ + SignatureData: sig, + Timestamp: sm.Time, + } + + proof, err = suite.chainA.Codec.Marshal(signatureDoc) + suite.Require().NoError(err) + }, + false, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + clientState = sm.ClientState() + + path = commitmenttypes.NewMerklePath("ibc", "solomachine") + signBytes = solomachine.SignBytes{ + Sequence: sm.GetHeight().GetRevisionHeight(), + Timestamp: sm.Time, + Diversifier: sm.Diversifier, + Path: []byte(path.String()), + Data: nil, + } + + signBz, err := suite.chainA.Codec.Marshal(&signBytes) + suite.Require().NoError(err) + + sig := sm.GenerateSignature(signBz) + + signatureDoc := &solomachine.TimestampedSignatureData{ + SignatureData: sig, + Timestamp: sm.Time, + } + + proof, err = suite.chainA.Codec.Marshal(signatureDoc) + suite.Require().NoError(err) + + tc.malleate() + + var expSeq uint64 + if clientState.ConsensusState != nil { + expSeq = clientState.Sequence + 1 + } + + err = clientState.VerifyNonMembership( + suite.chainA.GetContext(), suite.store, suite.chainA.Codec, + clienttypes.ZeroHeight(), 0, 0, // solomachine does not check delay periods + proof, path, + ) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().Equal(expSeq, clientState.Sequence) + suite.Require().Equal(expSeq, suite.GetSequenceFromStore(), "sequence not updated in the store (%d) on valid test case %s", suite.GetSequenceFromStore(), tc.name) + } else { + suite.Require().Error(err) + } + }) + } + } +} + +func (suite *SoloMachineTestSuite) TestGetTimestampAtHeight() { + tmPath := ibctesting.NewPath(suite.chainA, suite.chainB) + suite.coordinator.SetupClients(tmPath) + // Single setup for all test cases. + suite.SetupTest() + + testCases := []struct { + name string + clientState *solomachine.ClientState + height exported.Height + expValue uint64 + expPass bool + }{ + { + name: "get timestamp at height exists", + clientState: suite.solomachine.ClientState(), + height: suite.solomachine.ClientState().GetLatestHeight(), + expValue: suite.solomachine.ClientState().ConsensusState.Timestamp, + expPass: true, + }, + } + + for i, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + ctx := suite.chainA.GetContext() + + ts, err := tc.clientState.GetTimestampAtHeight( + ctx, suite.store, suite.chainA.Codec, tc.height, + ) + + suite.Require().Equal(tc.expValue, ts) + + if tc.expPass { + suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.name) + } else { + suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.name) + } + }) + } +} diff --git a/modules/light-clients/06-solomachine/codec.go b/modules/light-clients/06-solomachine/codec.go new file mode 100644 index 00000000000..e18cd8b563c --- /dev/null +++ b/modules/light-clients/06-solomachine/codec.go @@ -0,0 +1,42 @@ +package solomachine + +import ( + errorsmod "cosmossdk.io/errors" + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/types/tx/signing" + + "github.com/cosmos/ibc-go/v7/modules/core/exported" +) + +// RegisterInterfaces register the ibc channel submodule interfaces to protobuf +// Any. +func RegisterInterfaces(registry codectypes.InterfaceRegistry) { + registry.RegisterImplementations( + (*exported.ClientState)(nil), + &ClientState{}, + ) + registry.RegisterImplementations( + (*exported.ConsensusState)(nil), + &ConsensusState{}, + ) + registry.RegisterImplementations( + (*exported.ClientMessage)(nil), + &Header{}, + ) + registry.RegisterImplementations( + (*exported.ClientMessage)(nil), + &Misbehaviour{}, + ) +} + +func UnmarshalSignatureData(cdc codec.BinaryCodec, data []byte) (signing.SignatureData, error) { + protoSigData := &signing.SignatureDescriptor_Data{} + if err := cdc.Unmarshal(data, protoSigData); err != nil { + return nil, errorsmod.Wrapf(err, "failed to unmarshal proof into type %T", protoSigData) + } + + sigData := signing.SignatureDataFromProto(protoSigData) + + return sigData, nil +} diff --git a/modules/light-clients/06-solomachine/types/consensus_state.go b/modules/light-clients/06-solomachine/consensus_state.go similarity index 66% rename from modules/light-clients/06-solomachine/types/consensus_state.go rename to modules/light-clients/06-solomachine/consensus_state.go index e6b53fced72..ed3bfa55d37 100644 --- a/modules/light-clients/06-solomachine/types/consensus_state.go +++ b/modules/light-clients/06-solomachine/consensus_state.go @@ -1,13 +1,13 @@ -package types +package solomachine import ( "strings" + errorsmod "cosmossdk.io/errors" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v4/modules/core/exported" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v7/modules/core/exported" ) var _ exported.ConsensusState = &ConsensusState{} @@ -22,22 +22,17 @@ func (cs ConsensusState) GetTimestamp() uint64 { return cs.Timestamp } -// GetRoot returns nil since solo machines do not have roots. -func (cs ConsensusState) GetRoot() exported.Root { - return nil -} - // GetPubKey unmarshals the public key into a cryptotypes.PubKey type. // An error is returned if the public key is nil or the cached value // is not a PubKey. func (cs ConsensusState) GetPubKey() (cryptotypes.PubKey, error) { if cs.PublicKey == nil { - return nil, sdkerrors.Wrap(clienttypes.ErrInvalidConsensus, "consensus state PublicKey cannot be nil") + return nil, errorsmod.Wrap(clienttypes.ErrInvalidConsensus, "consensus state PublicKey cannot be nil") } publicKey, ok := cs.PublicKey.GetCachedValue().(cryptotypes.PubKey) if !ok { - return nil, sdkerrors.Wrap(clienttypes.ErrInvalidConsensus, "consensus state PublicKey is not cryptotypes.PubKey") + return nil, errorsmod.Wrap(clienttypes.ErrInvalidConsensus, "consensus state PublicKey is not cryptotypes.PubKey") } return publicKey, nil @@ -46,15 +41,15 @@ func (cs ConsensusState) GetPubKey() (cryptotypes.PubKey, error) { // ValidateBasic defines basic validation for the solo machine consensus state. func (cs ConsensusState) ValidateBasic() error { if cs.Timestamp == 0 { - return sdkerrors.Wrap(clienttypes.ErrInvalidConsensus, "timestamp cannot be 0") + return errorsmod.Wrap(clienttypes.ErrInvalidConsensus, "timestamp cannot be 0") } if cs.Diversifier != "" && strings.TrimSpace(cs.Diversifier) == "" { - return sdkerrors.Wrap(clienttypes.ErrInvalidConsensus, "diversifier cannot contain only spaces") + return errorsmod.Wrap(clienttypes.ErrInvalidConsensus, "diversifier cannot contain only spaces") } publicKey, err := cs.GetPubKey() if err != nil || publicKey == nil || len(publicKey.Bytes()) == 0 { - return sdkerrors.Wrap(clienttypes.ErrInvalidConsensus, "public key cannot be empty") + return errorsmod.Wrap(clienttypes.ErrInvalidConsensus, "public key cannot be empty") } return nil diff --git a/modules/light-clients/06-solomachine/types/consensus_state_test.go b/modules/light-clients/06-solomachine/consensus_state_test.go similarity index 56% rename from modules/light-clients/06-solomachine/types/consensus_state_test.go rename to modules/light-clients/06-solomachine/consensus_state_test.go index 29ea918a00b..4585ae533de 100644 --- a/modules/light-clients/06-solomachine/types/consensus_state_test.go +++ b/modules/light-clients/06-solomachine/consensus_state_test.go @@ -1,9 +1,9 @@ -package types_test +package solomachine_test import ( - "github.com/cosmos/ibc-go/v4/modules/core/exported" - "github.com/cosmos/ibc-go/v4/modules/light-clients/06-solomachine/types" - ibctesting "github.com/cosmos/ibc-go/v4/testing" + "github.com/cosmos/ibc-go/v7/modules/core/exported" + solomachine "github.com/cosmos/ibc-go/v7/modules/light-clients/06-solomachine" + ibctesting "github.com/cosmos/ibc-go/v7/testing" ) func (suite *SoloMachineTestSuite) TestConsensusState() { @@ -11,46 +11,45 @@ func (suite *SoloMachineTestSuite) TestConsensusState() { suite.Require().Equal(exported.Solomachine, consensusState.ClientType()) suite.Require().Equal(suite.solomachine.Time, consensusState.GetTimestamp()) - suite.Require().Nil(consensusState.GetRoot()) } func (suite *SoloMachineTestSuite) TestConsensusStateValidateBasic() { // test singlesig and multisig public keys - for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { testCases := []struct { name string - consensusState *types.ConsensusState + consensusState *solomachine.ConsensusState expPass bool }{ { "valid consensus state", - solomachine.ConsensusState(), + sm.ConsensusState(), true, }, { "timestamp is zero", - &types.ConsensusState{ - PublicKey: solomachine.ConsensusState().PublicKey, + &solomachine.ConsensusState{ + PublicKey: sm.ConsensusState().PublicKey, Timestamp: 0, - Diversifier: solomachine.Diversifier, + Diversifier: sm.Diversifier, }, false, }, { "diversifier is blank", - &types.ConsensusState{ - PublicKey: solomachine.ConsensusState().PublicKey, - Timestamp: solomachine.Time, + &solomachine.ConsensusState{ + PublicKey: sm.ConsensusState().PublicKey, + Timestamp: sm.Time, Diversifier: " ", }, false, }, { "pubkey is nil", - &types.ConsensusState{ - Timestamp: solomachine.Time, - Diversifier: solomachine.Diversifier, + &solomachine.ConsensusState{ + Timestamp: sm.Time, + Diversifier: sm.Diversifier, PublicKey: nil, }, false, diff --git a/modules/light-clients/06-solomachine/doc.go b/modules/light-clients/06-solomachine/doc.go index bbd5fb48493..c4d15dfdc76 100644 --- a/modules/light-clients/06-solomachine/doc.go +++ b/modules/light-clients/06-solomachine/doc.go @@ -1,7 +1,7 @@ /* -Package solomachine implements a concrete `ConsensusState`, `Header`, -`Misbehaviour` and `Equivocation` types for the Solo Machine light client. -This implementation is based off the ICS 06 specification: -https://github.com/cosmos/ibc/tree/master/spec/client/ics-006-solo-machine-client +Package solomachine implements a concrete ClientState, ConsensusState, +Header and Misbehaviour types for the Solo Machine light client. +This implementation is based off the ICS 06 specification +(https://github.com/cosmos/ibc/tree/master/spec/client/ics-006-solo-machine-client) */ package solomachine diff --git a/modules/light-clients/06-solomachine/errors.go b/modules/light-clients/06-solomachine/errors.go new file mode 100644 index 00000000000..91c22286241 --- /dev/null +++ b/modules/light-clients/06-solomachine/errors.go @@ -0,0 +1,13 @@ +package solomachine + +import ( + errorsmod "cosmossdk.io/errors" +) + +var ( + ErrInvalidHeader = errorsmod.Register(ModuleName, 2, "invalid header") + ErrInvalidSequence = errorsmod.Register(ModuleName, 3, "invalid sequence") + ErrInvalidSignatureAndData = errorsmod.Register(ModuleName, 4, "invalid signature and data") + ErrSignatureVerificationFailed = errorsmod.Register(ModuleName, 5, "signature verification failed") + ErrInvalidProof = errorsmod.Register(ModuleName, 6, "invalid solo machine proof") +) diff --git a/modules/light-clients/06-solomachine/types/header.go b/modules/light-clients/06-solomachine/header.go similarity index 50% rename from modules/light-clients/06-solomachine/types/header.go rename to modules/light-clients/06-solomachine/header.go index 2f9ab4147fb..e9dd883f2fd 100644 --- a/modules/light-clients/06-solomachine/types/header.go +++ b/modules/light-clients/06-solomachine/header.go @@ -1,67 +1,59 @@ -package types +package solomachine import ( "strings" + errorsmod "cosmossdk.io/errors" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v4/modules/core/exported" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v7/modules/core/exported" ) -var _ exported.Header = &Header{} +// SentinelHeaderPath defines a placeholder path value used for headers in solomachine client updates +const SentinelHeaderPath = "solomachine:header" + +var _ exported.ClientMessage = &Header{} // ClientType defines that the Header is a Solo Machine. func (Header) ClientType() string { return exported.Solomachine } -// GetHeight returns the current sequence number as the height. -// Return clientexported.Height to satisfy interface -// Revision number is always 0 for a solo-machine -func (h Header) GetHeight() exported.Height { - return clienttypes.NewHeight(0, h.Sequence) -} - // GetPubKey unmarshals the new public key into a cryptotypes.PubKey type. // An error is returned if the new public key is nil or the cached value // is not a PubKey. func (h Header) GetPubKey() (cryptotypes.PubKey, error) { if h.NewPublicKey == nil { - return nil, sdkerrors.Wrap(ErrInvalidHeader, "header NewPublicKey cannot be nil") + return nil, errorsmod.Wrap(ErrInvalidHeader, "header NewPublicKey cannot be nil") } publicKey, ok := h.NewPublicKey.GetCachedValue().(cryptotypes.PubKey) if !ok { - return nil, sdkerrors.Wrap(ErrInvalidHeader, "header NewPublicKey is not cryptotypes.PubKey") + return nil, errorsmod.Wrap(ErrInvalidHeader, "header NewPublicKey is not cryptotypes.PubKey") } return publicKey, nil } -// ValidateBasic ensures that the sequence, signature and public key have all +// ValidateBasic ensures that the timestamp, signature and public key have all // been initialized. func (h Header) ValidateBasic() error { - if h.Sequence == 0 { - return sdkerrors.Wrap(clienttypes.ErrInvalidHeader, "sequence number cannot be zero") - } - if h.Timestamp == 0 { - return sdkerrors.Wrap(clienttypes.ErrInvalidHeader, "timestamp cannot be zero") + return errorsmod.Wrap(clienttypes.ErrInvalidHeader, "timestamp cannot be zero") } if h.NewDiversifier != "" && strings.TrimSpace(h.NewDiversifier) == "" { - return sdkerrors.Wrap(clienttypes.ErrInvalidHeader, "diversifier cannot contain only spaces") + return errorsmod.Wrap(clienttypes.ErrInvalidHeader, "diversifier cannot contain only spaces") } if len(h.Signature) == 0 { - return sdkerrors.Wrap(clienttypes.ErrInvalidHeader, "signature cannot be empty") + return errorsmod.Wrap(clienttypes.ErrInvalidHeader, "signature cannot be empty") } newPublicKey, err := h.GetPubKey() if err != nil || newPublicKey == nil || len(newPublicKey.Bytes()) == 0 { - return sdkerrors.Wrap(clienttypes.ErrInvalidHeader, "new public key cannot be empty") + return errorsmod.Wrap(clienttypes.ErrInvalidHeader, "new public key cannot be empty") } return nil diff --git a/modules/light-clients/06-solomachine/types/header_test.go b/modules/light-clients/06-solomachine/header_test.go similarity index 61% rename from modules/light-clients/06-solomachine/types/header_test.go rename to modules/light-clients/06-solomachine/header_test.go index 607306f40c3..d0e8e31b29a 100644 --- a/modules/light-clients/06-solomachine/types/header_test.go +++ b/modules/light-clients/06-solomachine/header_test.go @@ -1,20 +1,20 @@ -package types_test +package solomachine_test import ( - "github.com/cosmos/ibc-go/v4/modules/core/exported" - "github.com/cosmos/ibc-go/v4/modules/light-clients/06-solomachine/types" - ibctesting "github.com/cosmos/ibc-go/v4/testing" + "github.com/cosmos/ibc-go/v7/modules/core/exported" + solomachine "github.com/cosmos/ibc-go/v7/modules/light-clients/06-solomachine" + ibctesting "github.com/cosmos/ibc-go/v7/testing" ) func (suite *SoloMachineTestSuite) TestHeaderValidateBasic() { // test singlesig and multisig public keys - for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { - header := solomachine.CreateHeader() + header := sm.CreateHeader(sm.Diversifier) cases := []struct { name string - header *types.Header + header *solomachine.Header expPass bool }{ { @@ -22,21 +22,9 @@ func (suite *SoloMachineTestSuite) TestHeaderValidateBasic() { header, true, }, - { - "sequence is zero", - &types.Header{ - Sequence: 0, - Timestamp: header.Timestamp, - Signature: header.Signature, - NewPublicKey: header.NewPublicKey, - NewDiversifier: header.NewDiversifier, - }, - false, - }, { "timestamp is zero", - &types.Header{ - Sequence: header.Sequence, + &solomachine.Header{ Timestamp: 0, Signature: header.Signature, NewPublicKey: header.NewPublicKey, @@ -46,8 +34,7 @@ func (suite *SoloMachineTestSuite) TestHeaderValidateBasic() { }, { "signature is empty", - &types.Header{ - Sequence: header.Sequence, + &solomachine.Header{ Timestamp: header.Timestamp, Signature: []byte{}, NewPublicKey: header.NewPublicKey, @@ -57,8 +44,7 @@ func (suite *SoloMachineTestSuite) TestHeaderValidateBasic() { }, { "diversifier contains only spaces", - &types.Header{ - Sequence: header.Sequence, + &solomachine.Header{ Timestamp: header.Timestamp, Signature: header.Signature, NewPublicKey: header.NewPublicKey, @@ -68,8 +54,7 @@ func (suite *SoloMachineTestSuite) TestHeaderValidateBasic() { }, { "public key is nil", - &types.Header{ - Sequence: header.Sequence, + &solomachine.Header{ Timestamp: header.Timestamp, Signature: header.Signature, NewPublicKey: nil, diff --git a/modules/light-clients/06-solomachine/keys.go b/modules/light-clients/06-solomachine/keys.go new file mode 100644 index 00000000000..cbb665d6fe3 --- /dev/null +++ b/modules/light-clients/06-solomachine/keys.go @@ -0,0 +1,5 @@ +package solomachine + +const ( + ModuleName = "06-solomachine" +) diff --git a/modules/light-clients/06-solomachine/misbehaviour.go b/modules/light-clients/06-solomachine/misbehaviour.go new file mode 100644 index 00000000000..8533f0b4fb3 --- /dev/null +++ b/modules/light-clients/06-solomachine/misbehaviour.go @@ -0,0 +1,68 @@ +package solomachine + +import ( + "bytes" + + errorsmod "cosmossdk.io/errors" + + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v7/modules/core/exported" +) + +var _ exported.ClientMessage = &Misbehaviour{} + +// ClientType is a Solo Machine light client. +func (misbehaviour Misbehaviour) ClientType() string { + return exported.Solomachine +} + +// Type implements Misbehaviour interface. +func (misbehaviour Misbehaviour) Type() string { + return exported.TypeClientMisbehaviour +} + +// ValidateBasic implements Misbehaviour interface. +func (misbehaviour Misbehaviour) ValidateBasic() error { + if misbehaviour.Sequence == 0 { + return errorsmod.Wrap(clienttypes.ErrInvalidMisbehaviour, "sequence cannot be 0") + } + + if err := misbehaviour.SignatureOne.ValidateBasic(); err != nil { + return errorsmod.Wrap(err, "signature one failed basic validation") + } + + if err := misbehaviour.SignatureTwo.ValidateBasic(); err != nil { + return errorsmod.Wrap(err, "signature two failed basic validation") + } + + // misbehaviour signatures cannot be identical. + if bytes.Equal(misbehaviour.SignatureOne.Signature, misbehaviour.SignatureTwo.Signature) { + return errorsmod.Wrap(clienttypes.ErrInvalidMisbehaviour, "misbehaviour signatures cannot be equal") + } + + // message data signed cannot be identical if both paths are the same. + if bytes.Equal(misbehaviour.SignatureOne.Path, misbehaviour.SignatureTwo.Path) && + bytes.Equal(misbehaviour.SignatureOne.Data, misbehaviour.SignatureTwo.Data) { + return errorsmod.Wrap(clienttypes.ErrInvalidMisbehaviour, "misbehaviour signature data must be signed over different messages") + } + + return nil +} + +// ValidateBasic ensures that the signature and data fields are non-empty. +func (sd SignatureAndData) ValidateBasic() error { + if len(sd.Signature) == 0 { + return errorsmod.Wrap(ErrInvalidSignatureAndData, "signature cannot be empty") + } + if len(sd.Data) == 0 { + return errorsmod.Wrap(ErrInvalidSignatureAndData, "data for signature cannot be empty") + } + if len(sd.Path) == 0 { + return errorsmod.Wrap(ErrInvalidSignatureAndData, "path for signature cannot be empty") + } + if sd.Timestamp == 0 { + return errorsmod.Wrap(ErrInvalidSignatureAndData, "timestamp cannot be 0") + } + + return nil +} diff --git a/modules/light-clients/06-solomachine/misbehaviour_handle.go b/modules/light-clients/06-solomachine/misbehaviour_handle.go new file mode 100644 index 00000000000..7e8b3822410 --- /dev/null +++ b/modules/light-clients/06-solomachine/misbehaviour_handle.go @@ -0,0 +1,75 @@ +package solomachine + +import ( + "github.com/cosmos/cosmos-sdk/codec" + + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + + commitmenttypes "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/types" + "github.com/cosmos/ibc-go/v7/modules/core/exported" +) + +// CheckForMisbehaviour returns true for type Misbehaviour (passed VerifyClientMessage check), otherwise returns false +func (cs ClientState) CheckForMisbehaviour(_ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, clientMsg exported.ClientMessage) bool { + if _, ok := clientMsg.(*Misbehaviour); ok { + return true + } + + return false +} + +func (cs ClientState) verifyMisbehaviour(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, misbehaviour *Misbehaviour) error { + // NOTE: a check that the misbehaviour message data are not equal is done by + // misbehaviour.ValidateBasic which is called by the 02-client keeper. + // verify first signature + if err := cs.verifySignatureAndData(cdc, misbehaviour, misbehaviour.SignatureOne); err != nil { + return errorsmod.Wrap(err, "failed to verify signature one") + } + + // verify second signature + if err := cs.verifySignatureAndData(cdc, misbehaviour, misbehaviour.SignatureTwo); err != nil { + return errorsmod.Wrap(err, "failed to verify signature two") + } + + return nil +} + +// verifySignatureAndData verifies that the currently registered public key has signed +// over the provided data and that the data is valid. The data is valid if it can be +// unmarshaled into the specified data type. +func (cs ClientState) verifySignatureAndData(cdc codec.BinaryCodec, misbehaviour *Misbehaviour, sigAndData *SignatureAndData) error { + // do not check misbehaviour timestamp since we want to allow processing of past misbehaviour + if err := cdc.Unmarshal(sigAndData.Path, new(commitmenttypes.MerklePath)); err != nil { + return err + } + + signBytes := SignBytes{ + Sequence: misbehaviour.Sequence, + Timestamp: sigAndData.Timestamp, + Diversifier: cs.ConsensusState.Diversifier, + Path: sigAndData.Path, + Data: sigAndData.Data, + } + + data, err := cdc.Marshal(&signBytes) + if err != nil { + return err + } + + sigData, err := UnmarshalSignatureData(cdc, sigAndData.Signature) + if err != nil { + return err + } + + publicKey, err := cs.ConsensusState.GetPubKey() + if err != nil { + return err + } + + if err := VerifySignature(publicKey, data, sigData); err != nil { + return err + } + + return nil +} diff --git a/modules/light-clients/06-solomachine/types/misbehaviour_test.go b/modules/light-clients/06-solomachine/misbehaviour_test.go similarity index 54% rename from modules/light-clients/06-solomachine/types/misbehaviour_test.go rename to modules/light-clients/06-solomachine/misbehaviour_test.go index 47724375494..0ac774a6ea5 100644 --- a/modules/light-clients/06-solomachine/types/misbehaviour_test.go +++ b/modules/light-clients/06-solomachine/misbehaviour_test.go @@ -1,109 +1,109 @@ -package types_test +package solomachine_test import ( - "github.com/cosmos/ibc-go/v4/modules/core/exported" - "github.com/cosmos/ibc-go/v4/modules/light-clients/06-solomachine/types" - ibctesting "github.com/cosmos/ibc-go/v4/testing" + "github.com/cosmos/ibc-go/v7/modules/core/exported" + solomachine "github.com/cosmos/ibc-go/v7/modules/light-clients/06-solomachine" + ibctesting "github.com/cosmos/ibc-go/v7/testing" ) func (suite *SoloMachineTestSuite) TestMisbehaviour() { misbehaviour := suite.solomachine.CreateMisbehaviour() suite.Require().Equal(exported.Solomachine, misbehaviour.ClientType()) - suite.Require().Equal(suite.solomachine.ClientID, misbehaviour.GetClientID()) } func (suite *SoloMachineTestSuite) TestMisbehaviourValidateBasic() { // test singlesig and multisig public keys - for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { testCases := []struct { name string - malleateMisbehaviour func(misbehaviour *types.Misbehaviour) + malleateMisbehaviour func(misbehaviour *solomachine.Misbehaviour) expPass bool }{ { "valid misbehaviour", - func(*types.Misbehaviour) {}, + func(*solomachine.Misbehaviour) {}, true, }, - { - "invalid client ID", - func(misbehaviour *types.Misbehaviour) { - misbehaviour.ClientId = "(badclientid)" - }, - false, - }, { "sequence is zero", - func(misbehaviour *types.Misbehaviour) { + func(misbehaviour *solomachine.Misbehaviour) { misbehaviour.Sequence = 0 }, false, }, { "signature one sig is empty", - func(misbehaviour *types.Misbehaviour) { + func(misbehaviour *solomachine.Misbehaviour) { misbehaviour.SignatureOne.Signature = []byte{} }, false, }, { "signature two sig is empty", - func(misbehaviour *types.Misbehaviour) { + func(misbehaviour *solomachine.Misbehaviour) { misbehaviour.SignatureTwo.Signature = []byte{} }, false, }, { "signature one data is empty", - func(misbehaviour *types.Misbehaviour) { + func(misbehaviour *solomachine.Misbehaviour) { misbehaviour.SignatureOne.Data = nil }, false, }, { "signature two data is empty", - func(misbehaviour *types.Misbehaviour) { + func(misbehaviour *solomachine.Misbehaviour) { misbehaviour.SignatureTwo.Data = []byte{} }, false, }, { "signatures are identical", - func(misbehaviour *types.Misbehaviour) { + func(misbehaviour *solomachine.Misbehaviour) { misbehaviour.SignatureTwo.Signature = misbehaviour.SignatureOne.Signature }, false, }, { - "data signed is identical", - func(misbehaviour *types.Misbehaviour) { + "data signed is identical but path differs", + func(misbehaviour *solomachine.Misbehaviour) { + misbehaviour.SignatureTwo.Data = misbehaviour.SignatureOne.Data + }, + true, + }, + { + "data signed and path are identical", + func(misbehaviour *solomachine.Misbehaviour) { + misbehaviour.SignatureTwo.Path = misbehaviour.SignatureOne.Path misbehaviour.SignatureTwo.Data = misbehaviour.SignatureOne.Data }, false, }, { - "data type for SignatureOne is unspecified", - func(misbehaviour *types.Misbehaviour) { - misbehaviour.SignatureOne.DataType = types.UNSPECIFIED + "data path for SignatureOne is unspecified", + func(misbehaviour *solomachine.Misbehaviour) { + misbehaviour.SignatureOne.Path = []byte{} }, false, }, { - "data type for SignatureTwo is unspecified", - func(misbehaviour *types.Misbehaviour) { - misbehaviour.SignatureTwo.DataType = types.UNSPECIFIED + "data path for SignatureTwo is unspecified", + func(misbehaviour *solomachine.Misbehaviour) { + misbehaviour.SignatureTwo.Path = []byte{} }, false, }, { "timestamp for SignatureOne is zero", - func(misbehaviour *types.Misbehaviour) { + func(misbehaviour *solomachine.Misbehaviour) { misbehaviour.SignatureOne.Timestamp = 0 }, false, }, { "timestamp for SignatureTwo is zero", - func(misbehaviour *types.Misbehaviour) { + func(misbehaviour *solomachine.Misbehaviour) { misbehaviour.SignatureTwo.Timestamp = 0 }, false, }, @@ -113,7 +113,7 @@ func (suite *SoloMachineTestSuite) TestMisbehaviourValidateBasic() { tc := tc suite.Run(tc.name, func() { - misbehaviour := solomachine.CreateMisbehaviour() + misbehaviour := sm.CreateMisbehaviour() tc.malleateMisbehaviour(misbehaviour) err := misbehaviour.ValidateBasic() diff --git a/modules/light-clients/06-solomachine/module.go b/modules/light-clients/06-solomachine/module.go index 60cfedb88f0..3b168e0bcea 100644 --- a/modules/light-clients/06-solomachine/module.go +++ b/modules/light-clients/06-solomachine/module.go @@ -1,10 +1,56 @@ package solomachine import ( - "github.com/cosmos/ibc-go/v4/modules/light-clients/06-solomachine/types" + "encoding/json" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/types/module" + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "github.com/spf13/cobra" ) -// Name returns the solo machine client name. -func Name() string { - return types.SubModuleName +var _ module.AppModuleBasic = AppModuleBasic{} + +// AppModuleBasic defines the basic application module used by the solo machine light client. +// Only the RegisterInterfaces function needs to be implemented. All other function perform +// a no-op. +type AppModuleBasic struct{} + +// Name returns the solo machine module name. +func (AppModuleBasic) Name() string { + return ModuleName +} + +// RegisterLegacyAminoCodec performs a no-op. The solo machine client does not support amino. +func (AppModuleBasic) RegisterLegacyAminoCodec(*codec.LegacyAmino) {} + +// RegisterInterfaces registers module concrete types into protobuf Any. This allows core IBC +// to unmarshal solo machine types. +func (AppModuleBasic) RegisterInterfaces(registry codectypes.InterfaceRegistry) { + RegisterInterfaces(registry) +} + +// DefaultGenesis performs a no-op. Genesis is not supported for solo machine. +func (AppModuleBasic) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage { + return nil +} + +// ValidateGenesis performs a no-op. Genesis is not supported for solo machine. +func (AppModuleBasic) ValidateGenesis(cdc codec.JSONCodec, config client.TxEncodingConfig, bz json.RawMessage) error { + return nil +} + +// RegisterGRPCGatewayRoutes performs a no-op. +func (AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *runtime.ServeMux) {} + +// GetTxCmd performs a no-op. Please see the 02-client cli commands. +func (AppModuleBasic) GetTxCmd() *cobra.Command { + return nil +} + +// GetQueryCmd performs a no-op. Please see the 02-client cli commands. +func (AppModuleBasic) GetQueryCmd() *cobra.Command { + return nil } diff --git a/modules/light-clients/06-solomachine/proof.go b/modules/light-clients/06-solomachine/proof.go new file mode 100644 index 00000000000..aab15c17d3c --- /dev/null +++ b/modules/light-clients/06-solomachine/proof.go @@ -0,0 +1,43 @@ +package solomachine + +import ( + errorsmod "cosmossdk.io/errors" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/crypto/types/multisig" + "github.com/cosmos/cosmos-sdk/types/tx/signing" +) + +// VerifySignature verifies if the the provided public key generated the signature +// over the given data. Single and Multi signature public keys are supported. +// The signature data type must correspond to the public key type. An error is +// returned if signature verification fails or an invalid SignatureData type is +// provided. +func VerifySignature(pubKey cryptotypes.PubKey, signBytes []byte, sigData signing.SignatureData) error { + switch pubKey := pubKey.(type) { + case multisig.PubKey: + data, ok := sigData.(*signing.MultiSignatureData) + if !ok { + return errorsmod.Wrapf(ErrSignatureVerificationFailed, "invalid signature data type, expected %T, got %T", (*signing.MultiSignatureData)(nil), data) + } + + // The function supplied fulfills the VerifyMultisignature interface. No special + // adjustments need to be made to the sign bytes based on the sign mode. + if err := pubKey.VerifyMultisignature(func(signing.SignMode) ([]byte, error) { + return signBytes, nil + }, data); err != nil { + return err + } + + default: + data, ok := sigData.(*signing.SingleSignatureData) + if !ok { + return errorsmod.Wrapf(ErrSignatureVerificationFailed, "invalid signature data type, expected %T, got %T", (*signing.SingleSignatureData)(nil), data) + } + + if !pubKey.VerifySignature(signBytes, data.Signature) { + return ErrSignatureVerificationFailed + } + } + + return nil +} diff --git a/modules/light-clients/06-solomachine/proof_test.go b/modules/light-clients/06-solomachine/proof_test.go new file mode 100644 index 00000000000..25702a19c84 --- /dev/null +++ b/modules/light-clients/06-solomachine/proof_test.go @@ -0,0 +1,67 @@ +package solomachine_test + +import ( + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/types/tx/signing" + + solomachine "github.com/cosmos/ibc-go/v7/modules/light-clients/06-solomachine" +) + +func (suite *SoloMachineTestSuite) TestVerifySignature() { + cdc := suite.chainA.App.AppCodec() + signBytes := []byte("sign bytes") + + singleSignature := suite.solomachine.GenerateSignature(signBytes) + singleSigData, err := solomachine.UnmarshalSignatureData(cdc, singleSignature) + suite.Require().NoError(err) + + multiSignature := suite.solomachineMulti.GenerateSignature(signBytes) + multiSigData, err := solomachine.UnmarshalSignatureData(cdc, multiSignature) + suite.Require().NoError(err) + + testCases := []struct { + name string + publicKey cryptotypes.PubKey + sigData signing.SignatureData + expPass bool + }{ + { + "single signature with regular public key", + suite.solomachine.PublicKey, + singleSigData, + true, + }, + { + "multi signature with multisig public key", + suite.solomachineMulti.PublicKey, + multiSigData, + true, + }, + { + "single signature with multisig public key", + suite.solomachineMulti.PublicKey, + singleSigData, + false, + }, + { + "multi signature with regular public key", + suite.solomachine.PublicKey, + multiSigData, + false, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + err := solomachine.VerifySignature(tc.publicKey, signBytes, tc.sigData) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } +} diff --git a/modules/light-clients/06-solomachine/types/proposal_handle.go b/modules/light-clients/06-solomachine/proposal_handle.go similarity index 52% rename from modules/light-clients/06-solomachine/types/proposal_handle.go rename to modules/light-clients/06-solomachine/proposal_handle.go index c4316f982a8..2e6fdfd7ed8 100644 --- a/modules/light-clients/06-solomachine/types/proposal_handle.go +++ b/modules/light-clients/06-solomachine/proposal_handle.go @@ -1,14 +1,14 @@ -package types +package solomachine import ( "reflect" + errorsmod "cosmossdk.io/errors" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v4/modules/core/exported" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v7/modules/core/exported" ) // CheckSubstituteAndUpdateState verifies that the subject is allowed to be updated by @@ -21,43 +21,32 @@ import ( func (cs ClientState) CheckSubstituteAndUpdateState( ctx sdk.Context, cdc codec.BinaryCodec, subjectClientStore, _ sdk.KVStore, substituteClient exported.ClientState, -) (exported.ClientState, error) { - if !cs.AllowUpdateAfterProposal { - return nil, sdkerrors.Wrapf( - clienttypes.ErrUpdateClientFailed, - "solo machine client is not allowed to updated with a proposal", - ) - } - +) error { substituteClientState, ok := substituteClient.(*ClientState) if !ok { - return nil, sdkerrors.Wrapf( - clienttypes.ErrInvalidClientType, "substitute client state type %T, expected %T", substituteClient, &ClientState{}, - ) + return errorsmod.Wrapf(clienttypes.ErrInvalidClientType, "substitute client state type %T, expected %T", substituteClient, &ClientState{}) } subjectPublicKey, err := cs.ConsensusState.GetPubKey() if err != nil { - return nil, sdkerrors.Wrap(err, "failed to get consensus public key") + return errorsmod.Wrap(err, "failed to get consensus public key") } substitutePublicKey, err := substituteClientState.ConsensusState.GetPubKey() if err != nil { - return nil, sdkerrors.Wrap(err, "failed to get substitute client public key") + return errorsmod.Wrap(err, "failed to get substitute client public key") } if reflect.DeepEqual(subjectPublicKey, substitutePublicKey) { - return nil, sdkerrors.Wrapf( - clienttypes.ErrInvalidHeader, "subject and substitute have the same public key", - ) + return errorsmod.Wrapf(clienttypes.ErrInvalidHeader, "subject and substitute have the same public key") } - clientState := &cs - // update to substitute parameters - clientState.Sequence = substituteClientState.Sequence - clientState.ConsensusState = substituteClientState.ConsensusState - clientState.IsFrozen = false + cs.Sequence = substituteClientState.Sequence + cs.ConsensusState = substituteClientState.ConsensusState + cs.IsFrozen = false + + setClientState(subjectClientStore, cdc, &cs) - return clientState, nil + return nil } diff --git a/modules/light-clients/06-solomachine/proposal_handle_test.go b/modules/light-clients/06-solomachine/proposal_handle_test.go new file mode 100644 index 00000000000..22a3c0d624a --- /dev/null +++ b/modules/light-clients/06-solomachine/proposal_handle_test.go @@ -0,0 +1,83 @@ +package solomachine_test + +import ( + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + "github.com/cosmos/ibc-go/v7/modules/core/exported" + solomachine "github.com/cosmos/ibc-go/v7/modules/light-clients/06-solomachine" + ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" + ibctesting "github.com/cosmos/ibc-go/v7/testing" +) + +func (suite *SoloMachineTestSuite) TestCheckSubstituteAndUpdateState() { + var ( + subjectClientState *solomachine.ClientState + substituteClientState exported.ClientState + ) + + // test singlesig and multisig public keys + for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + + testCases := []struct { + name string + malleate func() + expPass bool + }{ + { + "substitute is not the solo machine", func() { + substituteClientState = &ibctm.ClientState{} + }, false, + }, + { + "subject public key is nil", func() { + subjectClientState.ConsensusState.PublicKey = nil + }, false, + }, + + { + "substitute public key is nil", func() { + substituteClientState.(*solomachine.ClientState).ConsensusState.PublicKey = nil + }, false, + }, + { + "subject and substitute use the same public key", func() { + substituteClientState.(*solomachine.ClientState).ConsensusState.PublicKey = subjectClientState.ConsensusState.PublicKey + }, false, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + suite.SetupTest() + + subjectClientState = sm.ClientState() + substitute := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "substitute", "testing", 5) + substituteClientState = substitute.ClientState() + + tc.malleate() + + subjectClientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), sm.ClientID) + substituteClientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), substitute.ClientID) + + err := subjectClientState.CheckSubstituteAndUpdateState(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), subjectClientStore, substituteClientStore, substituteClientState) + + if tc.expPass { + suite.Require().NoError(err) + + // ensure updated client state is set in store + bz := subjectClientStore.Get(host.ClientStateKey()) + updatedClient := clienttypes.MustUnmarshalClientState(suite.chainA.App.AppCodec(), bz).(*solomachine.ClientState) + + suite.Require().Equal(substituteClientState.(*solomachine.ClientState).ConsensusState, updatedClient.ConsensusState) + suite.Require().Equal(substituteClientState.(*solomachine.ClientState).Sequence, updatedClient.Sequence) + suite.Require().Equal(false, updatedClient.IsFrozen) + + } else { + suite.Require().Error(err) + } + }) + } + } +} diff --git a/modules/light-clients/06-solomachine/types/solomachine.go b/modules/light-clients/06-solomachine/solomachine.go similarity index 69% rename from modules/light-clients/06-solomachine/types/solomachine.go rename to modules/light-clients/06-solomachine/solomachine.go index 0ce953e12c8..41511910bf4 100644 --- a/modules/light-clients/06-solomachine/types/solomachine.go +++ b/modules/light-clients/06-solomachine/solomachine.go @@ -1,10 +1,8 @@ -package types +package solomachine import ( codectypes "github.com/cosmos/cosmos-sdk/codec/types" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" - - "github.com/cosmos/ibc-go/v4/modules/core/exported" ) // Interface implementation checks. @@ -32,13 +30,3 @@ func (h Header) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { func (hd HeaderData) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { return unpacker.UnpackAny(hd.NewPubKey, new(cryptotypes.PubKey)) } - -// UnpackInterfaces implements the UnpackInterfaceMessages.UnpackInterfaces method -func (csd ClientStateData) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { - return unpacker.UnpackAny(csd.ClientState, new(exported.ClientState)) -} - -// UnpackInterfaces implements the UnpackInterfaceMessages.UnpackInterfaces method -func (csd ConsensusStateData) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { - return unpacker.UnpackAny(csd.ConsensusState, new(exported.ConsensusState)) -} diff --git a/modules/light-clients/06-solomachine/solomachine.pb.go b/modules/light-clients/06-solomachine/solomachine.pb.go new file mode 100644 index 00000000000..f2152041bdf --- /dev/null +++ b/modules/light-clients/06-solomachine/solomachine.pb.go @@ -0,0 +1,2236 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: ibc/lightclients/solomachine/v3/solomachine.proto + +package solomachine + +import ( + fmt "fmt" + types "github.com/cosmos/cosmos-sdk/codec/types" + _ "github.com/cosmos/gogoproto/gogoproto" + proto "github.com/cosmos/gogoproto/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// ClientState defines a solo machine client that tracks the current consensus +// state and if the client is frozen. +type ClientState struct { + // latest sequence of the client state + Sequence uint64 `protobuf:"varint,1,opt,name=sequence,proto3" json:"sequence,omitempty"` + // frozen sequence of the solo machine + IsFrozen bool `protobuf:"varint,2,opt,name=is_frozen,json=isFrozen,proto3" json:"is_frozen,omitempty" yaml:"is_frozen"` + ConsensusState *ConsensusState `protobuf:"bytes,3,opt,name=consensus_state,json=consensusState,proto3" json:"consensus_state,omitempty" yaml:"consensus_state"` +} + +func (m *ClientState) Reset() { *m = ClientState{} } +func (m *ClientState) String() string { return proto.CompactTextString(m) } +func (*ClientState) ProtoMessage() {} +func (*ClientState) Descriptor() ([]byte, []int) { + return fileDescriptor_264187157b9220a4, []int{0} +} +func (m *ClientState) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ClientState) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ClientState.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ClientState) XXX_Merge(src proto.Message) { + xxx_messageInfo_ClientState.Merge(m, src) +} +func (m *ClientState) XXX_Size() int { + return m.Size() +} +func (m *ClientState) XXX_DiscardUnknown() { + xxx_messageInfo_ClientState.DiscardUnknown(m) +} + +var xxx_messageInfo_ClientState proto.InternalMessageInfo + +// ConsensusState defines a solo machine consensus state. The sequence of a +// consensus state is contained in the "height" key used in storing the +// consensus state. +type ConsensusState struct { + // public key of the solo machine + PublicKey *types.Any `protobuf:"bytes,1,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty" yaml:"public_key"` + // diversifier allows the same public key to be re-used across different solo + // machine clients (potentially on different chains) without being considered + // misbehaviour. + Diversifier string `protobuf:"bytes,2,opt,name=diversifier,proto3" json:"diversifier,omitempty"` + Timestamp uint64 `protobuf:"varint,3,opt,name=timestamp,proto3" json:"timestamp,omitempty"` +} + +func (m *ConsensusState) Reset() { *m = ConsensusState{} } +func (m *ConsensusState) String() string { return proto.CompactTextString(m) } +func (*ConsensusState) ProtoMessage() {} +func (*ConsensusState) Descriptor() ([]byte, []int) { + return fileDescriptor_264187157b9220a4, []int{1} +} +func (m *ConsensusState) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ConsensusState) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ConsensusState.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 *ConsensusState) XXX_Merge(src proto.Message) { + xxx_messageInfo_ConsensusState.Merge(m, src) +} +func (m *ConsensusState) XXX_Size() int { + return m.Size() +} +func (m *ConsensusState) XXX_DiscardUnknown() { + xxx_messageInfo_ConsensusState.DiscardUnknown(m) +} + +var xxx_messageInfo_ConsensusState proto.InternalMessageInfo + +// Header defines a solo machine consensus header +type Header struct { + Timestamp uint64 `protobuf:"varint,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + Signature []byte `protobuf:"bytes,2,opt,name=signature,proto3" json:"signature,omitempty"` + NewPublicKey *types.Any `protobuf:"bytes,3,opt,name=new_public_key,json=newPublicKey,proto3" json:"new_public_key,omitempty" yaml:"new_public_key"` + NewDiversifier string `protobuf:"bytes,4,opt,name=new_diversifier,json=newDiversifier,proto3" json:"new_diversifier,omitempty" yaml:"new_diversifier"` +} + +func (m *Header) Reset() { *m = Header{} } +func (m *Header) String() string { return proto.CompactTextString(m) } +func (*Header) ProtoMessage() {} +func (*Header) Descriptor() ([]byte, []int) { + return fileDescriptor_264187157b9220a4, []int{2} +} +func (m *Header) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Header) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Header.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 *Header) XXX_Merge(src proto.Message) { + xxx_messageInfo_Header.Merge(m, src) +} +func (m *Header) XXX_Size() int { + return m.Size() +} +func (m *Header) XXX_DiscardUnknown() { + xxx_messageInfo_Header.DiscardUnknown(m) +} + +var xxx_messageInfo_Header proto.InternalMessageInfo + +// Misbehaviour defines misbehaviour for a solo machine which consists +// of a sequence and two signatures over different messages at that sequence. +type Misbehaviour struct { + Sequence uint64 `protobuf:"varint,1,opt,name=sequence,proto3" json:"sequence,omitempty"` + SignatureOne *SignatureAndData `protobuf:"bytes,2,opt,name=signature_one,json=signatureOne,proto3" json:"signature_one,omitempty" yaml:"signature_one"` + SignatureTwo *SignatureAndData `protobuf:"bytes,3,opt,name=signature_two,json=signatureTwo,proto3" json:"signature_two,omitempty" yaml:"signature_two"` +} + +func (m *Misbehaviour) Reset() { *m = Misbehaviour{} } +func (m *Misbehaviour) String() string { return proto.CompactTextString(m) } +func (*Misbehaviour) ProtoMessage() {} +func (*Misbehaviour) Descriptor() ([]byte, []int) { + return fileDescriptor_264187157b9220a4, []int{3} +} +func (m *Misbehaviour) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Misbehaviour) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Misbehaviour.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 *Misbehaviour) XXX_Merge(src proto.Message) { + xxx_messageInfo_Misbehaviour.Merge(m, src) +} +func (m *Misbehaviour) XXX_Size() int { + return m.Size() +} +func (m *Misbehaviour) XXX_DiscardUnknown() { + xxx_messageInfo_Misbehaviour.DiscardUnknown(m) +} + +var xxx_messageInfo_Misbehaviour proto.InternalMessageInfo + +// SignatureAndData contains a signature and the data signed over to create that +// signature. +type SignatureAndData struct { + Signature []byte `protobuf:"bytes,1,opt,name=signature,proto3" json:"signature,omitempty"` + Path []byte `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"` + Data []byte `protobuf:"bytes,3,opt,name=data,proto3" json:"data,omitempty"` + Timestamp uint64 `protobuf:"varint,4,opt,name=timestamp,proto3" json:"timestamp,omitempty"` +} + +func (m *SignatureAndData) Reset() { *m = SignatureAndData{} } +func (m *SignatureAndData) String() string { return proto.CompactTextString(m) } +func (*SignatureAndData) ProtoMessage() {} +func (*SignatureAndData) Descriptor() ([]byte, []int) { + return fileDescriptor_264187157b9220a4, []int{4} +} +func (m *SignatureAndData) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SignatureAndData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SignatureAndData.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 *SignatureAndData) XXX_Merge(src proto.Message) { + xxx_messageInfo_SignatureAndData.Merge(m, src) +} +func (m *SignatureAndData) XXX_Size() int { + return m.Size() +} +func (m *SignatureAndData) XXX_DiscardUnknown() { + xxx_messageInfo_SignatureAndData.DiscardUnknown(m) +} + +var xxx_messageInfo_SignatureAndData proto.InternalMessageInfo + +// TimestampedSignatureData contains the signature data and the timestamp of the +// signature. +type TimestampedSignatureData struct { + SignatureData []byte `protobuf:"bytes,1,opt,name=signature_data,json=signatureData,proto3" json:"signature_data,omitempty" yaml:"signature_data"` + Timestamp uint64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` +} + +func (m *TimestampedSignatureData) Reset() { *m = TimestampedSignatureData{} } +func (m *TimestampedSignatureData) String() string { return proto.CompactTextString(m) } +func (*TimestampedSignatureData) ProtoMessage() {} +func (*TimestampedSignatureData) Descriptor() ([]byte, []int) { + return fileDescriptor_264187157b9220a4, []int{5} +} +func (m *TimestampedSignatureData) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TimestampedSignatureData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_TimestampedSignatureData.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 *TimestampedSignatureData) XXX_Merge(src proto.Message) { + xxx_messageInfo_TimestampedSignatureData.Merge(m, src) +} +func (m *TimestampedSignatureData) XXX_Size() int { + return m.Size() +} +func (m *TimestampedSignatureData) XXX_DiscardUnknown() { + xxx_messageInfo_TimestampedSignatureData.DiscardUnknown(m) +} + +var xxx_messageInfo_TimestampedSignatureData proto.InternalMessageInfo + +// SignBytes defines the signed bytes used for signature verification. +type SignBytes struct { + // the sequence number + Sequence uint64 `protobuf:"varint,1,opt,name=sequence,proto3" json:"sequence,omitempty"` + // the proof timestamp + Timestamp uint64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + // the public key diversifier + Diversifier string `protobuf:"bytes,3,opt,name=diversifier,proto3" json:"diversifier,omitempty"` + // the standardised path bytes + Path []byte `protobuf:"bytes,4,opt,name=path,proto3" json:"path,omitempty"` + // the marshaled data bytes + Data []byte `protobuf:"bytes,5,opt,name=data,proto3" json:"data,omitempty"` +} + +func (m *SignBytes) Reset() { *m = SignBytes{} } +func (m *SignBytes) String() string { return proto.CompactTextString(m) } +func (*SignBytes) ProtoMessage() {} +func (*SignBytes) Descriptor() ([]byte, []int) { + return fileDescriptor_264187157b9220a4, []int{6} +} +func (m *SignBytes) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SignBytes) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SignBytes.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 *SignBytes) XXX_Merge(src proto.Message) { + xxx_messageInfo_SignBytes.Merge(m, src) +} +func (m *SignBytes) XXX_Size() int { + return m.Size() +} +func (m *SignBytes) XXX_DiscardUnknown() { + xxx_messageInfo_SignBytes.DiscardUnknown(m) +} + +var xxx_messageInfo_SignBytes proto.InternalMessageInfo + +// HeaderData returns the SignBytes data for update verification. +type HeaderData struct { + // header public key + NewPubKey *types.Any `protobuf:"bytes,1,opt,name=new_pub_key,json=newPubKey,proto3" json:"new_pub_key,omitempty" yaml:"new_pub_key"` + // header diversifier + NewDiversifier string `protobuf:"bytes,2,opt,name=new_diversifier,json=newDiversifier,proto3" json:"new_diversifier,omitempty" yaml:"new_diversifier"` +} + +func (m *HeaderData) Reset() { *m = HeaderData{} } +func (m *HeaderData) String() string { return proto.CompactTextString(m) } +func (*HeaderData) ProtoMessage() {} +func (*HeaderData) Descriptor() ([]byte, []int) { + return fileDescriptor_264187157b9220a4, []int{7} +} +func (m *HeaderData) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *HeaderData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_HeaderData.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 *HeaderData) XXX_Merge(src proto.Message) { + xxx_messageInfo_HeaderData.Merge(m, src) +} +func (m *HeaderData) XXX_Size() int { + return m.Size() +} +func (m *HeaderData) XXX_DiscardUnknown() { + xxx_messageInfo_HeaderData.DiscardUnknown(m) +} + +var xxx_messageInfo_HeaderData proto.InternalMessageInfo + +func init() { + proto.RegisterType((*ClientState)(nil), "ibc.lightclients.solomachine.v3.ClientState") + proto.RegisterType((*ConsensusState)(nil), "ibc.lightclients.solomachine.v3.ConsensusState") + proto.RegisterType((*Header)(nil), "ibc.lightclients.solomachine.v3.Header") + proto.RegisterType((*Misbehaviour)(nil), "ibc.lightclients.solomachine.v3.Misbehaviour") + proto.RegisterType((*SignatureAndData)(nil), "ibc.lightclients.solomachine.v3.SignatureAndData") + proto.RegisterType((*TimestampedSignatureData)(nil), "ibc.lightclients.solomachine.v3.TimestampedSignatureData") + proto.RegisterType((*SignBytes)(nil), "ibc.lightclients.solomachine.v3.SignBytes") + proto.RegisterType((*HeaderData)(nil), "ibc.lightclients.solomachine.v3.HeaderData") +} + +func init() { + proto.RegisterFile("ibc/lightclients/solomachine/v3/solomachine.proto", fileDescriptor_264187157b9220a4) +} + +var fileDescriptor_264187157b9220a4 = []byte{ + // 709 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x55, 0xcd, 0x6e, 0xd3, 0x4a, + 0x14, 0xce, 0xa4, 0xbe, 0x55, 0x33, 0xc9, 0x4d, 0x7b, 0xad, 0xb4, 0x4a, 0xa3, 0xab, 0x38, 0xf2, + 0xaa, 0x9b, 0xda, 0xb7, 0xad, 0x74, 0x91, 0xca, 0x86, 0xa6, 0x15, 0x42, 0x50, 0x04, 0x72, 0xcb, + 0x86, 0x4d, 0x34, 0x76, 0x26, 0xce, 0x08, 0x7b, 0x26, 0x64, 0xc6, 0x89, 0x82, 0x58, 0xb0, 0x64, + 0xc9, 0x02, 0xf6, 0x08, 0x89, 0x77, 0x61, 0xd9, 0x65, 0x57, 0x11, 0x6a, 0x25, 0x1e, 0x20, 0x4f, + 0x80, 0x3c, 0x76, 0xe2, 0x9f, 0x96, 0x54, 0x42, 0xec, 0xe6, 0x9c, 0x39, 0x3f, 0xdf, 0xf7, 0xcd, + 0x39, 0x36, 0xdc, 0x23, 0xb6, 0x63, 0x7a, 0xc4, 0xed, 0x0b, 0xc7, 0x23, 0x98, 0x0a, 0x6e, 0x72, + 0xe6, 0x31, 0x1f, 0x39, 0x7d, 0x42, 0xb1, 0x39, 0x3a, 0x48, 0x9b, 0xc6, 0x60, 0xc8, 0x04, 0x53, + 0x35, 0x62, 0x3b, 0x46, 0x3a, 0xc5, 0x48, 0xc7, 0x8c, 0x0e, 0x1a, 0x35, 0x97, 0xb9, 0x4c, 0xc6, + 0x9a, 0xe1, 0x29, 0x4a, 0x6b, 0x6c, 0xbb, 0x8c, 0xb9, 0x1e, 0x36, 0xa5, 0x65, 0x07, 0x3d, 0x13, + 0xd1, 0x49, 0x74, 0xa5, 0x5f, 0x02, 0x58, 0x3e, 0x96, 0xb5, 0xce, 0x04, 0x12, 0x58, 0x6d, 0xc0, + 0x35, 0x8e, 0x5f, 0x07, 0x98, 0x3a, 0xb8, 0x0e, 0x5a, 0x60, 0x47, 0xb1, 0x16, 0xb6, 0xba, 0x07, + 0x4b, 0x84, 0x77, 0x7a, 0x43, 0xf6, 0x06, 0xd3, 0x7a, 0xb1, 0x05, 0x76, 0xd6, 0xda, 0xb5, 0xd9, + 0x54, 0xdb, 0x98, 0x20, 0xdf, 0x3b, 0xd4, 0x17, 0x57, 0xba, 0xb5, 0x46, 0xf8, 0x43, 0x79, 0x54, + 0x05, 0x5c, 0x77, 0x18, 0xe5, 0x98, 0xf2, 0x80, 0x77, 0x78, 0xd8, 0xa1, 0xbe, 0xd2, 0x02, 0x3b, + 0xe5, 0x7d, 0xd3, 0xb8, 0x83, 0x8a, 0x71, 0x3c, 0xcf, 0x93, 0xc0, 0xda, 0x8d, 0xd9, 0x54, 0xdb, + 0x8a, 0x3a, 0xe5, 0x2a, 0xea, 0x56, 0xd5, 0xc9, 0xc4, 0x1e, 0x2a, 0xef, 0x3f, 0x6b, 0x05, 0xfd, + 0x0b, 0x80, 0xd5, 0x6c, 0x11, 0xf5, 0x31, 0x84, 0x83, 0xc0, 0xf6, 0x88, 0xd3, 0x79, 0x85, 0x27, + 0x92, 0x5f, 0x79, 0xbf, 0x66, 0x44, 0xea, 0x18, 0x73, 0x75, 0x8c, 0x23, 0x3a, 0x69, 0x6f, 0xce, + 0xa6, 0xda, 0x3f, 0x51, 0xbb, 0x24, 0x43, 0xb7, 0x4a, 0x91, 0xf1, 0x04, 0x4f, 0xd4, 0x16, 0x2c, + 0x77, 0xc9, 0x08, 0x0f, 0x39, 0xe9, 0x11, 0x3c, 0x94, 0x7a, 0x94, 0xac, 0xb4, 0x4b, 0xfd, 0x17, + 0x96, 0x04, 0xf1, 0x31, 0x17, 0xc8, 0x1f, 0x48, 0xda, 0x8a, 0x95, 0x38, 0x62, 0x90, 0x3f, 0x00, + 0x5c, 0x7d, 0x84, 0x51, 0x37, 0x1f, 0x0e, 0x72, 0xe1, 0xe1, 0x2d, 0x27, 0x2e, 0x45, 0x22, 0x18, + 0x62, 0xd9, 0xac, 0x62, 0x25, 0x0e, 0xf5, 0x05, 0xac, 0x52, 0x3c, 0xee, 0xa4, 0xc8, 0xad, 0x2c, + 0x21, 0xb7, 0x3d, 0x9b, 0x6a, 0x9b, 0x11, 0xb9, 0x6c, 0x96, 0x6e, 0x55, 0x28, 0x1e, 0x3f, 0x5f, + 0x70, 0x3c, 0x86, 0xeb, 0x61, 0x40, 0x9a, 0xa7, 0x12, 0xf2, 0x4c, 0xbf, 0x46, 0x2e, 0x40, 0xb7, + 0x42, 0x24, 0x27, 0x89, 0x23, 0x26, 0xfa, 0xb1, 0x08, 0x2b, 0x4f, 0x09, 0xb7, 0x71, 0x1f, 0x8d, + 0x08, 0x0b, 0x86, 0x4b, 0x27, 0x6d, 0x00, 0xff, 0x5e, 0x70, 0xeb, 0x30, 0x1a, 0x11, 0x2e, 0xef, + 0xef, 0xdd, 0x39, 0x34, 0x67, 0xf3, 0xac, 0x23, 0xda, 0x3d, 0x41, 0x02, 0xb5, 0xeb, 0xb3, 0xa9, + 0x56, 0x8b, 0x80, 0x66, 0x2a, 0xea, 0x56, 0x65, 0x61, 0x3f, 0xa3, 0xb9, 0x8e, 0x62, 0xcc, 0x62, + 0xfd, 0xfe, 0x54, 0x47, 0x31, 0x66, 0xe9, 0x8e, 0xe7, 0x63, 0x16, 0xcb, 0xf2, 0x16, 0x6e, 0xe4, + 0x2b, 0x64, 0x9f, 0x1a, 0xe4, 0x9f, 0x5a, 0x85, 0xca, 0x00, 0x89, 0x7e, 0x3c, 0x03, 0xf2, 0x1c, + 0xfa, 0xba, 0x48, 0x20, 0x09, 0xba, 0x62, 0xc9, 0x73, 0x76, 0x9c, 0x94, 0xdb, 0xa7, 0xef, 0x1d, + 0x80, 0xf5, 0xf3, 0xb9, 0x0f, 0x77, 0x17, 0x48, 0x24, 0x8c, 0x07, 0xb0, 0x9a, 0x10, 0x90, 0xe5, + 0x25, 0x96, 0xf4, 0xf4, 0x64, 0xef, 0x75, 0x2b, 0xd1, 0xf0, 0xe4, 0x06, 0x84, 0xe2, 0xed, 0x10, + 0x3e, 0x01, 0x58, 0x0a, 0xfb, 0xb6, 0x27, 0x02, 0xf3, 0xa5, 0x43, 0xb1, 0xb4, 0x5a, 0x7e, 0x1d, + 0x57, 0x6e, 0xae, 0xe3, 0x5c, 0x38, 0xe5, 0x16, 0xe1, 0xfe, 0x4a, 0x84, 0x8b, 0x71, 0x7d, 0x05, + 0x10, 0x46, 0x8b, 0x29, 0xa9, 0x9c, 0xc2, 0x72, 0xbc, 0x2a, 0x77, 0x7e, 0x3a, 0xb6, 0x66, 0x53, + 0x4d, 0xcd, 0x6c, 0x57, 0xfc, 0xed, 0x88, 0x56, 0xeb, 0x17, 0x7b, 0x55, 0xfc, 0xbd, 0xbd, 0x6a, + 0xf7, 0xbe, 0x5d, 0x35, 0xc1, 0xc5, 0x55, 0x13, 0x7c, 0xbf, 0x6a, 0x82, 0x0f, 0xd7, 0xcd, 0xc2, + 0xc5, 0x75, 0xb3, 0x70, 0x79, 0xdd, 0x2c, 0xbc, 0x3c, 0x75, 0x89, 0xe8, 0x07, 0xb6, 0xe1, 0x30, + 0xdf, 0x74, 0x18, 0xf7, 0x19, 0x37, 0x89, 0xed, 0xec, 0xba, 0xcc, 0x1c, 0xdd, 0x33, 0x7d, 0xd6, + 0x0d, 0x3c, 0xcc, 0xa3, 0xff, 0xcf, 0xee, 0xfc, 0x07, 0xf4, 0xdf, 0xff, 0xbb, 0xa9, 0xf1, 0xbe, + 0x9f, 0x3a, 0xdb, 0xab, 0x92, 0xe3, 0xc1, 0xcf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x92, 0xe1, 0xf5, + 0xc9, 0xb6, 0x06, 0x00, 0x00, +} + +func (m *ClientState) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ClientState) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ClientState) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.ConsensusState != nil { + { + size, err := m.ConsensusState.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSolomachine(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if m.IsFrozen { + i-- + if m.IsFrozen { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x10 + } + if m.Sequence != 0 { + i = encodeVarintSolomachine(dAtA, i, uint64(m.Sequence)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *ConsensusState) 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 *ConsensusState) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ConsensusState) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Timestamp != 0 { + i = encodeVarintSolomachine(dAtA, i, uint64(m.Timestamp)) + i-- + dAtA[i] = 0x18 + } + if len(m.Diversifier) > 0 { + i -= len(m.Diversifier) + copy(dAtA[i:], m.Diversifier) + i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Diversifier))) + i-- + dAtA[i] = 0x12 + } + if m.PublicKey != nil { + { + size, err := m.PublicKey.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSolomachine(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Header) 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 *Header) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Header) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.NewDiversifier) > 0 { + i -= len(m.NewDiversifier) + copy(dAtA[i:], m.NewDiversifier) + i = encodeVarintSolomachine(dAtA, i, uint64(len(m.NewDiversifier))) + i-- + dAtA[i] = 0x22 + } + if m.NewPublicKey != nil { + { + size, err := m.NewPublicKey.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSolomachine(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if len(m.Signature) > 0 { + i -= len(m.Signature) + copy(dAtA[i:], m.Signature) + i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Signature))) + i-- + dAtA[i] = 0x12 + } + if m.Timestamp != 0 { + i = encodeVarintSolomachine(dAtA, i, uint64(m.Timestamp)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *Misbehaviour) 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 *Misbehaviour) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Misbehaviour) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.SignatureTwo != nil { + { + size, err := m.SignatureTwo.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSolomachine(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if m.SignatureOne != nil { + { + size, err := m.SignatureOne.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSolomachine(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if m.Sequence != 0 { + i = encodeVarintSolomachine(dAtA, i, uint64(m.Sequence)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *SignatureAndData) 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 *SignatureAndData) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SignatureAndData) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Timestamp != 0 { + i = encodeVarintSolomachine(dAtA, i, uint64(m.Timestamp)) + i-- + dAtA[i] = 0x20 + } + if len(m.Data) > 0 { + i -= len(m.Data) + copy(dAtA[i:], m.Data) + i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Data))) + i-- + dAtA[i] = 0x1a + } + if len(m.Path) > 0 { + i -= len(m.Path) + copy(dAtA[i:], m.Path) + i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Path))) + i-- + dAtA[i] = 0x12 + } + if len(m.Signature) > 0 { + i -= len(m.Signature) + copy(dAtA[i:], m.Signature) + i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Signature))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *TimestampedSignatureData) 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 *TimestampedSignatureData) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TimestampedSignatureData) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Timestamp != 0 { + i = encodeVarintSolomachine(dAtA, i, uint64(m.Timestamp)) + i-- + dAtA[i] = 0x10 + } + if len(m.SignatureData) > 0 { + i -= len(m.SignatureData) + copy(dAtA[i:], m.SignatureData) + i = encodeVarintSolomachine(dAtA, i, uint64(len(m.SignatureData))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *SignBytes) 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 *SignBytes) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SignBytes) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Data) > 0 { + i -= len(m.Data) + copy(dAtA[i:], m.Data) + i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Data))) + i-- + dAtA[i] = 0x2a + } + if len(m.Path) > 0 { + i -= len(m.Path) + copy(dAtA[i:], m.Path) + i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Path))) + i-- + dAtA[i] = 0x22 + } + if len(m.Diversifier) > 0 { + i -= len(m.Diversifier) + copy(dAtA[i:], m.Diversifier) + i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Diversifier))) + i-- + dAtA[i] = 0x1a + } + if m.Timestamp != 0 { + i = encodeVarintSolomachine(dAtA, i, uint64(m.Timestamp)) + i-- + dAtA[i] = 0x10 + } + if m.Sequence != 0 { + i = encodeVarintSolomachine(dAtA, i, uint64(m.Sequence)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *HeaderData) 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 *HeaderData) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *HeaderData) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.NewDiversifier) > 0 { + i -= len(m.NewDiversifier) + copy(dAtA[i:], m.NewDiversifier) + i = encodeVarintSolomachine(dAtA, i, uint64(len(m.NewDiversifier))) + i-- + dAtA[i] = 0x12 + } + if m.NewPubKey != nil { + { + size, err := m.NewPubKey.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSolomachine(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintSolomachine(dAtA []byte, offset int, v uint64) int { + offset -= sovSolomachine(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *ClientState) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Sequence != 0 { + n += 1 + sovSolomachine(uint64(m.Sequence)) + } + if m.IsFrozen { + n += 2 + } + if m.ConsensusState != nil { + l = m.ConsensusState.Size() + n += 1 + l + sovSolomachine(uint64(l)) + } + return n +} + +func (m *ConsensusState) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.PublicKey != nil { + l = m.PublicKey.Size() + n += 1 + l + sovSolomachine(uint64(l)) + } + l = len(m.Diversifier) + if l > 0 { + n += 1 + l + sovSolomachine(uint64(l)) + } + if m.Timestamp != 0 { + n += 1 + sovSolomachine(uint64(m.Timestamp)) + } + return n +} + +func (m *Header) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Timestamp != 0 { + n += 1 + sovSolomachine(uint64(m.Timestamp)) + } + l = len(m.Signature) + if l > 0 { + n += 1 + l + sovSolomachine(uint64(l)) + } + if m.NewPublicKey != nil { + l = m.NewPublicKey.Size() + n += 1 + l + sovSolomachine(uint64(l)) + } + l = len(m.NewDiversifier) + if l > 0 { + n += 1 + l + sovSolomachine(uint64(l)) + } + return n +} + +func (m *Misbehaviour) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Sequence != 0 { + n += 1 + sovSolomachine(uint64(m.Sequence)) + } + if m.SignatureOne != nil { + l = m.SignatureOne.Size() + n += 1 + l + sovSolomachine(uint64(l)) + } + if m.SignatureTwo != nil { + l = m.SignatureTwo.Size() + n += 1 + l + sovSolomachine(uint64(l)) + } + return n +} + +func (m *SignatureAndData) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Signature) + if l > 0 { + n += 1 + l + sovSolomachine(uint64(l)) + } + l = len(m.Path) + if l > 0 { + n += 1 + l + sovSolomachine(uint64(l)) + } + l = len(m.Data) + if l > 0 { + n += 1 + l + sovSolomachine(uint64(l)) + } + if m.Timestamp != 0 { + n += 1 + sovSolomachine(uint64(m.Timestamp)) + } + return n +} + +func (m *TimestampedSignatureData) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.SignatureData) + if l > 0 { + n += 1 + l + sovSolomachine(uint64(l)) + } + if m.Timestamp != 0 { + n += 1 + sovSolomachine(uint64(m.Timestamp)) + } + return n +} + +func (m *SignBytes) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Sequence != 0 { + n += 1 + sovSolomachine(uint64(m.Sequence)) + } + if m.Timestamp != 0 { + n += 1 + sovSolomachine(uint64(m.Timestamp)) + } + l = len(m.Diversifier) + if l > 0 { + n += 1 + l + sovSolomachine(uint64(l)) + } + l = len(m.Path) + if l > 0 { + n += 1 + l + sovSolomachine(uint64(l)) + } + l = len(m.Data) + if l > 0 { + n += 1 + l + sovSolomachine(uint64(l)) + } + return n +} + +func (m *HeaderData) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.NewPubKey != nil { + l = m.NewPubKey.Size() + n += 1 + l + sovSolomachine(uint64(l)) + } + l = len(m.NewDiversifier) + if l > 0 { + n += 1 + l + sovSolomachine(uint64(l)) + } + return n +} + +func sovSolomachine(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozSolomachine(x uint64) (n int) { + return sovSolomachine(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *ClientState) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ClientState: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ClientState: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 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 ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Sequence |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field IsFrozen", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.IsFrozen = bool(v != 0) + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ConsensusState", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthSolomachine + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.ConsensusState == nil { + m.ConsensusState = &ConsensusState{} + } + if err := m.ConsensusState.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipSolomachine(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthSolomachine + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ConsensusState) 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 ErrIntOverflowSolomachine + } + 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: ConsensusState: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ConsensusState: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PublicKey", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthSolomachine + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.PublicKey == nil { + m.PublicKey = &types.Any{} + } + if err := m.PublicKey.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Diversifier", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + 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 ErrInvalidLengthSolomachine + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Diversifier = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType) + } + m.Timestamp = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Timestamp |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipSolomachine(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthSolomachine + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Header) 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 ErrIntOverflowSolomachine + } + 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: Header: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Header: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType) + } + m.Timestamp = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Timestamp |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signature", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthSolomachine + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Signature = append(m.Signature[:0], dAtA[iNdEx:postIndex]...) + if m.Signature == nil { + m.Signature = []byte{} + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NewPublicKey", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthSolomachine + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.NewPublicKey == nil { + m.NewPublicKey = &types.Any{} + } + if err := m.NewPublicKey.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NewDiversifier", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + 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 ErrInvalidLengthSolomachine + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.NewDiversifier = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipSolomachine(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthSolomachine + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Misbehaviour) 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 ErrIntOverflowSolomachine + } + 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: Misbehaviour: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Misbehaviour: 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 ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Sequence |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SignatureOne", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthSolomachine + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.SignatureOne == nil { + m.SignatureOne = &SignatureAndData{} + } + if err := m.SignatureOne.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SignatureTwo", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthSolomachine + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.SignatureTwo == nil { + m.SignatureTwo = &SignatureAndData{} + } + if err := m.SignatureTwo.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipSolomachine(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthSolomachine + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SignatureAndData) 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 ErrIntOverflowSolomachine + } + 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: SignatureAndData: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SignatureAndData: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signature", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthSolomachine + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Signature = append(m.Signature[:0], dAtA[iNdEx:postIndex]...) + if m.Signature == nil { + m.Signature = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthSolomachine + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Path = append(m.Path[:0], dAtA[iNdEx:postIndex]...) + if m.Path == nil { + m.Path = []byte{} + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthSolomachine + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...) + if m.Data == nil { + m.Data = []byte{} + } + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType) + } + m.Timestamp = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Timestamp |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipSolomachine(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthSolomachine + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TimestampedSignatureData) 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 ErrIntOverflowSolomachine + } + 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: TimestampedSignatureData: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TimestampedSignatureData: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SignatureData", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthSolomachine + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SignatureData = append(m.SignatureData[:0], dAtA[iNdEx:postIndex]...) + if m.SignatureData == nil { + m.SignatureData = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType) + } + m.Timestamp = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Timestamp |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipSolomachine(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthSolomachine + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SignBytes) 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 ErrIntOverflowSolomachine + } + 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: SignBytes: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SignBytes: 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 ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Sequence |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType) + } + m.Timestamp = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Timestamp |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Diversifier", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + 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 ErrInvalidLengthSolomachine + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Diversifier = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthSolomachine + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Path = append(m.Path[:0], dAtA[iNdEx:postIndex]...) + if m.Path == nil { + m.Path = []byte{} + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthSolomachine + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...) + if m.Data == nil { + m.Data = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipSolomachine(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthSolomachine + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *HeaderData) 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 ErrIntOverflowSolomachine + } + 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: HeaderData: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: HeaderData: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NewPubKey", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthSolomachine + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.NewPubKey == nil { + m.NewPubKey = &types.Any{} + } + if err := m.NewPubKey.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NewDiversifier", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + 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 ErrInvalidLengthSolomachine + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.NewDiversifier = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipSolomachine(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthSolomachine + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipSolomachine(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowSolomachine + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowSolomachine + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowSolomachine + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthSolomachine + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupSolomachine + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthSolomachine + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthSolomachine = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowSolomachine = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupSolomachine = fmt.Errorf("proto: unexpected end of group") +) diff --git a/modules/light-clients/06-solomachine/solomachine_test.go b/modules/light-clients/06-solomachine/solomachine_test.go new file mode 100644 index 00000000000..15c4461edbd --- /dev/null +++ b/modules/light-clients/06-solomachine/solomachine_test.go @@ -0,0 +1,204 @@ +package solomachine_test + +import ( + "testing" + "time" + + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + "github.com/cosmos/cosmos-sdk/testutil/testdata" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + + transfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + "github.com/cosmos/ibc-go/v7/modules/core/exported" + solomachine "github.com/cosmos/ibc-go/v7/modules/light-clients/06-solomachine" + ibctesting "github.com/cosmos/ibc-go/v7/testing" + "github.com/cosmos/ibc-go/v7/testing/mock" +) + +var ( + channelIDSolomachine = "channel-on-solomachine" // channelID generated on solo machine side + clientIDSolomachine = "06-solomachine-0" +) + +type SoloMachineTestSuite struct { + suite.Suite + + solomachine *ibctesting.Solomachine // singlesig public key + solomachineMulti *ibctesting.Solomachine // multisig public key + coordinator *ibctesting.Coordinator + + // testing chain used for convenience and readability + chainA *ibctesting.TestChain + chainB *ibctesting.TestChain + + store sdk.KVStore +} + +func (suite *SoloMachineTestSuite) SetupTest() { + suite.coordinator = ibctesting.NewCoordinator(suite.T(), 2) + suite.chainA = suite.coordinator.GetChain(ibctesting.GetChainID(1)) + suite.chainB = suite.coordinator.GetChain(ibctesting.GetChainID(2)) + + suite.solomachine = ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachinesingle", "testing", 1) + suite.solomachineMulti = ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachinemulti", "testing", 4) + + suite.store = suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), exported.Solomachine) +} + +func TestSoloMachineTestSuite(t *testing.T) { + suite.Run(t, new(SoloMachineTestSuite)) +} + +func (suite *SoloMachineTestSuite) SetupSolomachine() string { + clientID := suite.solomachine.CreateClient(suite.chainA) + + connectionID := suite.solomachine.ConnOpenInit(suite.chainA, clientID) + + // open try is not necessary as the solo machine implementation is mocked + + suite.solomachine.ConnOpenAck(suite.chainA, clientID, connectionID) + + // open confirm is not necessary as the solo machine implementation is mocked + + channelID := suite.solomachine.ChanOpenInit(suite.chainA, connectionID) + + // open try is not necessary as the solo machine implementation is mocked + + suite.solomachine.ChanOpenAck(suite.chainA, channelID) + + // open confirm is not necessary as the solo machine implementation is mocked + + return channelID +} + +func (suite *SoloMachineTestSuite) TestRecvPacket() { + channelID := suite.SetupSolomachine() + packet := channeltypes.NewPacket( + mock.MockPacketData, + 1, + transfertypes.PortID, + channelIDSolomachine, + transfertypes.PortID, + channelID, + clienttypes.ZeroHeight(), + uint64(suite.chainA.GetContext().BlockTime().Add(time.Hour).UnixNano()), + ) + + // send packet is not necessary as the solo machine implementation is mocked + + suite.solomachine.RecvPacket(suite.chainA, packet) + + // close init is not necessary as the solomachine implementation is mocked + + suite.solomachine.ChanCloseConfirm(suite.chainA, transfertypes.PortID, channelID) +} + +func (suite *SoloMachineTestSuite) TestAcknowledgePacket() { + channelID := suite.SetupSolomachine() + + packet := suite.solomachine.SendTransfer(suite.chainA, transfertypes.PortID, channelID) + + // recv packet is not necessary as the solo machine implementation is mocked + + suite.solomachine.AcknowledgePacket(suite.chainA, packet) + + // close init is not necessary as the solomachine implementation is mocked + + suite.solomachine.ChanCloseConfirm(suite.chainA, transfertypes.PortID, channelID) +} + +func (suite *SoloMachineTestSuite) TestTimeout() { + channelID := suite.SetupSolomachine() + packet := suite.solomachine.SendTransfer(suite.chainA, transfertypes.PortID, channelID, func(msg *transfertypes.MsgTransfer) { + msg.TimeoutTimestamp = suite.solomachine.Time + 1 + }) + + // simulate solomachine time increment + suite.solomachine.Time++ + + suite.solomachine.UpdateClient(suite.chainA, clientIDSolomachine) + + suite.solomachine.TimeoutPacket(suite.chainA, packet) + + suite.solomachine.ChanCloseConfirm(suite.chainA, transfertypes.PortID, channelID) +} + +func (suite *SoloMachineTestSuite) TestTimeoutOnClose() { + channelID := suite.SetupSolomachine() + + packet := suite.solomachine.SendTransfer(suite.chainA, transfertypes.PortID, channelID) + + suite.solomachine.TimeoutPacketOnClose(suite.chainA, packet, channelID) +} + +func (suite *SoloMachineTestSuite) GetSequenceFromStore() uint64 { + bz := suite.store.Get(host.ClientStateKey()) + suite.Require().NotNil(bz) + + var clientState exported.ClientState + err := suite.chainA.Codec.UnmarshalInterface(bz, &clientState) + suite.Require().NoError(err) + return clientState.GetLatestHeight().GetRevisionHeight() +} + +func (suite *SoloMachineTestSuite) GetInvalidProof() []byte { + invalidProof, err := suite.chainA.Codec.Marshal(&solomachine.TimestampedSignatureData{Timestamp: suite.solomachine.Time}) + suite.Require().NoError(err) + + return invalidProof +} + +func TestUnpackInterfaces_Header(t *testing.T) { + registry := testdata.NewTestInterfaceRegistry() + cryptocodec.RegisterInterfaces(registry) + + pk := secp256k1.GenPrivKey().PubKey() + protoAny, err := codectypes.NewAnyWithValue(pk) + require.NoError(t, err) + + header := solomachine.Header{ + NewPublicKey: protoAny, + } + bz, err := header.Marshal() + require.NoError(t, err) + + var header2 solomachine.Header + err = header2.Unmarshal(bz) + require.NoError(t, err) + + err = codectypes.UnpackInterfaces(header2, registry) + require.NoError(t, err) + + require.Equal(t, pk, header2.NewPublicKey.GetCachedValue()) +} + +func TestUnpackInterfaces_HeaderData(t *testing.T) { + registry := testdata.NewTestInterfaceRegistry() + cryptocodec.RegisterInterfaces(registry) + + pk := secp256k1.GenPrivKey().PubKey() + protoAny, err := codectypes.NewAnyWithValue(pk) + require.NoError(t, err) + + hd := solomachine.HeaderData{ + NewPubKey: protoAny, + } + bz, err := hd.Marshal() + require.NoError(t, err) + + var hd2 solomachine.HeaderData + err = hd2.Unmarshal(bz) + require.NoError(t, err) + + err = codectypes.UnpackInterfaces(hd2, registry) + require.NoError(t, err) + + require.Equal(t, pk, hd2.NewPubKey.GetCachedValue()) +} diff --git a/modules/light-clients/06-solomachine/types/client_state.go b/modules/light-clients/06-solomachine/types/client_state.go deleted file mode 100644 index aa43e77e56d..00000000000 --- a/modules/light-clients/06-solomachine/types/client_state.go +++ /dev/null @@ -1,491 +0,0 @@ -package types - -import ( - "reflect" - - "github.com/cosmos/cosmos-sdk/codec" - cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" - sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/cosmos/cosmos-sdk/types/tx/signing" - - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - commitmenttypes "github.com/cosmos/ibc-go/v4/modules/core/23-commitment/types" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" - "github.com/cosmos/ibc-go/v4/modules/core/exported" -) - -var _ exported.ClientState = (*ClientState)(nil) - -// NewClientState creates a new ClientState instance. -func NewClientState(latestSequence uint64, consensusState *ConsensusState, allowUpdateAfterProposal bool) *ClientState { - return &ClientState{ - Sequence: latestSequence, - IsFrozen: false, - ConsensusState: consensusState, - AllowUpdateAfterProposal: allowUpdateAfterProposal, - } -} - -// ClientType is Solo Machine. -func (cs ClientState) ClientType() string { - return exported.Solomachine -} - -// GetLatestHeight returns the latest sequence number. -// Return exported.Height to satisfy ClientState interface -// Revision number is always 0 for a solo-machine. -func (cs ClientState) GetLatestHeight() exported.Height { - return clienttypes.NewHeight(0, cs.Sequence) -} - -// Status returns the status of the solo machine client. -// The client may be: -// - Active: if frozen sequence is 0 -// - Frozen: otherwise solo machine is frozen -func (cs ClientState) Status(_ sdk.Context, _ sdk.KVStore, _ codec.BinaryCodec) exported.Status { - if cs.IsFrozen { - return exported.Frozen - } - - return exported.Active -} - -// Validate performs basic validation of the client state fields. -func (cs ClientState) Validate() error { - if cs.Sequence == 0 { - return sdkerrors.Wrap(clienttypes.ErrInvalidClient, "sequence cannot be 0") - } - if cs.ConsensusState == nil { - return sdkerrors.Wrap(clienttypes.ErrInvalidConsensus, "consensus state cannot be nil") - } - return cs.ConsensusState.ValidateBasic() -} - -// ZeroCustomFields returns solomachine client state with client-specific fields FrozenSequence, -// and AllowUpdateAfterProposal zeroed out -func (cs ClientState) ZeroCustomFields() exported.ClientState { - return NewClientState( - cs.Sequence, cs.ConsensusState, false, - ) -} - -// Initialize will check that initial consensus state is equal to the latest consensus state of the initial client. -func (cs ClientState) Initialize(_ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, consState exported.ConsensusState) error { - if !reflect.DeepEqual(cs.ConsensusState, consState) { - return sdkerrors.Wrapf(clienttypes.ErrInvalidConsensus, "consensus state in initial client does not equal initial consensus state. expected: %s, got: %s", - cs.ConsensusState, consState) - } - return nil -} - -// ExportMetadata is a no-op since solomachine does not store any metadata in client store -func (cs ClientState) ExportMetadata(_ sdk.KVStore) []exported.GenesisMetadata { - return nil -} - -// VerifyUpgradeAndUpdateState returns an error since solomachine client does not support upgrades -func (cs ClientState) VerifyUpgradeAndUpdateState( - _ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, - _ exported.ClientState, _ exported.ConsensusState, _, _ []byte, -) (exported.ClientState, exported.ConsensusState, error) { - return nil, nil, sdkerrors.Wrap(clienttypes.ErrInvalidUpgradeClient, "cannot upgrade solomachine client") -} - -// VerifyClientState verifies a proof of the client state of the running chain -// stored on the solo machine. -func (cs *ClientState) VerifyClientState( - store sdk.KVStore, - cdc codec.BinaryCodec, - height exported.Height, - prefix exported.Prefix, - counterpartyClientIdentifier string, - proof []byte, - clientState exported.ClientState, -) error { - // NOTE: the proof height sequence is incremented by one due to the connection handshake verification ordering - height = clienttypes.NewHeight(height.GetRevisionNumber(), height.GetRevisionHeight()+1) - - publicKey, sigData, timestamp, sequence, err := produceVerificationArgs(cdc, cs, height, prefix, proof) - if err != nil { - return err - } - - clientPrefixedPath := commitmenttypes.NewMerklePath(host.FullClientStatePath(counterpartyClientIdentifier)) - path, err := commitmenttypes.ApplyPrefix(prefix, clientPrefixedPath) - if err != nil { - return err - } - - signBz, err := ClientStateSignBytes(cdc, sequence, timestamp, cs.ConsensusState.Diversifier, path, clientState) - if err != nil { - return err - } - - if err := VerifySignature(publicKey, signBz, sigData); err != nil { - return err - } - - cs.Sequence++ - cs.ConsensusState.Timestamp = timestamp - setClientState(store, cdc, cs) - return nil -} - -// VerifyClientConsensusState verifies a proof of the consensus state of the -// running chain stored on the solo machine. -func (cs *ClientState) VerifyClientConsensusState( - store sdk.KVStore, - cdc codec.BinaryCodec, - height exported.Height, - counterpartyClientIdentifier string, - consensusHeight exported.Height, - prefix exported.Prefix, - proof []byte, - consensusState exported.ConsensusState, -) error { - // NOTE: the proof height sequence is incremented by two due to the connection handshake verification ordering - height = clienttypes.NewHeight(height.GetRevisionNumber(), height.GetRevisionHeight()+2) - - publicKey, sigData, timestamp, sequence, err := produceVerificationArgs(cdc, cs, height, prefix, proof) - if err != nil { - return err - } - - clientPrefixedPath := commitmenttypes.NewMerklePath(host.FullConsensusStatePath(counterpartyClientIdentifier, consensusHeight)) - path, err := commitmenttypes.ApplyPrefix(prefix, clientPrefixedPath) - if err != nil { - return err - } - - signBz, err := ConsensusStateSignBytes(cdc, sequence, timestamp, cs.ConsensusState.Diversifier, path, consensusState) - if err != nil { - return err - } - - if err := VerifySignature(publicKey, signBz, sigData); err != nil { - return err - } - - cs.Sequence++ - cs.ConsensusState.Timestamp = timestamp - setClientState(store, cdc, cs) - return nil -} - -// VerifyConnectionState verifies a proof of the connection state of the -// specified connection end stored on the target machine. -func (cs *ClientState) VerifyConnectionState( - store sdk.KVStore, - cdc codec.BinaryCodec, - height exported.Height, - prefix exported.Prefix, - proof []byte, - connectionID string, - connectionEnd exported.ConnectionI, -) error { - publicKey, sigData, timestamp, sequence, err := produceVerificationArgs(cdc, cs, height, prefix, proof) - if err != nil { - return err - } - - connectionPath := commitmenttypes.NewMerklePath(host.ConnectionPath(connectionID)) - path, err := commitmenttypes.ApplyPrefix(prefix, connectionPath) - if err != nil { - return err - } - - signBz, err := ConnectionStateSignBytes(cdc, sequence, timestamp, cs.ConsensusState.Diversifier, path, connectionEnd) - if err != nil { - return err - } - - if err := VerifySignature(publicKey, signBz, sigData); err != nil { - return err - } - - cs.Sequence++ - cs.ConsensusState.Timestamp = timestamp - setClientState(store, cdc, cs) - return nil -} - -// VerifyChannelState verifies a proof of the channel state of the specified -// channel end, under the specified port, stored on the target machine. -func (cs *ClientState) VerifyChannelState( - store sdk.KVStore, - cdc codec.BinaryCodec, - height exported.Height, - prefix exported.Prefix, - proof []byte, - portID, - channelID string, - channel exported.ChannelI, -) error { - publicKey, sigData, timestamp, sequence, err := produceVerificationArgs(cdc, cs, height, prefix, proof) - if err != nil { - return err - } - - channelPath := commitmenttypes.NewMerklePath(host.ChannelPath(portID, channelID)) - path, err := commitmenttypes.ApplyPrefix(prefix, channelPath) - if err != nil { - return err - } - - signBz, err := ChannelStateSignBytes(cdc, sequence, timestamp, cs.ConsensusState.Diversifier, path, channel) - if err != nil { - return err - } - - if err := VerifySignature(publicKey, signBz, sigData); err != nil { - return err - } - - cs.Sequence++ - cs.ConsensusState.Timestamp = timestamp - setClientState(store, cdc, cs) - return nil -} - -// VerifyPacketCommitment verifies a proof of an outgoing packet commitment at -// the specified port, specified channel, and specified sequence. -func (cs *ClientState) VerifyPacketCommitment( - ctx sdk.Context, - store sdk.KVStore, - cdc codec.BinaryCodec, - height exported.Height, - _ uint64, - _ uint64, - prefix exported.Prefix, - proof []byte, - portID, - channelID string, - packetSequence uint64, - commitmentBytes []byte, -) error { - publicKey, sigData, timestamp, sequence, err := produceVerificationArgs(cdc, cs, height, prefix, proof) - if err != nil { - return err - } - - commitmentPath := commitmenttypes.NewMerklePath(host.PacketCommitmentPath(portID, channelID, packetSequence)) - path, err := commitmenttypes.ApplyPrefix(prefix, commitmentPath) - if err != nil { - return err - } - - signBz, err := PacketCommitmentSignBytes(cdc, sequence, timestamp, cs.ConsensusState.Diversifier, path, commitmentBytes) - if err != nil { - return err - } - - if err := VerifySignature(publicKey, signBz, sigData); err != nil { - return err - } - - cs.Sequence++ - cs.ConsensusState.Timestamp = timestamp - setClientState(store, cdc, cs) - return nil -} - -// VerifyPacketAcknowledgement verifies a proof of an incoming packet -// acknowledgement at the specified port, specified channel, and specified sequence. -func (cs *ClientState) VerifyPacketAcknowledgement( - ctx sdk.Context, - store sdk.KVStore, - cdc codec.BinaryCodec, - height exported.Height, - _ uint64, - _ uint64, - prefix exported.Prefix, - proof []byte, - portID, - channelID string, - packetSequence uint64, - acknowledgement []byte, -) error { - publicKey, sigData, timestamp, sequence, err := produceVerificationArgs(cdc, cs, height, prefix, proof) - if err != nil { - return err - } - - ackPath := commitmenttypes.NewMerklePath(host.PacketAcknowledgementPath(portID, channelID, packetSequence)) - path, err := commitmenttypes.ApplyPrefix(prefix, ackPath) - if err != nil { - return err - } - - signBz, err := PacketAcknowledgementSignBytes(cdc, sequence, timestamp, cs.ConsensusState.Diversifier, path, acknowledgement) - if err != nil { - return err - } - - if err := VerifySignature(publicKey, signBz, sigData); err != nil { - return err - } - - cs.Sequence++ - cs.ConsensusState.Timestamp = timestamp - setClientState(store, cdc, cs) - return nil -} - -// VerifyPacketReceiptAbsence verifies a proof of the absence of an -// incoming packet receipt at the specified port, specified channel, and -// specified sequence. -func (cs *ClientState) VerifyPacketReceiptAbsence( - ctx sdk.Context, - store sdk.KVStore, - cdc codec.BinaryCodec, - height exported.Height, - _ uint64, - _ uint64, - prefix exported.Prefix, - proof []byte, - portID, - channelID string, - packetSequence uint64, -) error { - publicKey, sigData, timestamp, sequence, err := produceVerificationArgs(cdc, cs, height, prefix, proof) - if err != nil { - return err - } - - receiptPath := commitmenttypes.NewMerklePath(host.PacketReceiptPath(portID, channelID, packetSequence)) - path, err := commitmenttypes.ApplyPrefix(prefix, receiptPath) - if err != nil { - return err - } - - signBz, err := PacketReceiptAbsenceSignBytes(cdc, sequence, timestamp, cs.ConsensusState.Diversifier, path) - if err != nil { - return err - } - - if err := VerifySignature(publicKey, signBz, sigData); err != nil { - return err - } - - cs.Sequence++ - cs.ConsensusState.Timestamp = timestamp - setClientState(store, cdc, cs) - return nil -} - -// VerifyNextSequenceRecv verifies a proof of the next sequence number to be -// received of the specified channel at the specified port. -func (cs *ClientState) VerifyNextSequenceRecv( - ctx sdk.Context, - store sdk.KVStore, - cdc codec.BinaryCodec, - height exported.Height, - _ uint64, - _ uint64, - prefix exported.Prefix, - proof []byte, - portID, - channelID string, - nextSequenceRecv uint64, -) error { - publicKey, sigData, timestamp, sequence, err := produceVerificationArgs(cdc, cs, height, prefix, proof) - if err != nil { - return err - } - - nextSequenceRecvPath := commitmenttypes.NewMerklePath(host.NextSequenceRecvPath(portID, channelID)) - path, err := commitmenttypes.ApplyPrefix(prefix, nextSequenceRecvPath) - if err != nil { - return err - } - - signBz, err := NextSequenceRecvSignBytes(cdc, sequence, timestamp, cs.ConsensusState.Diversifier, path, nextSequenceRecv) - if err != nil { - return err - } - - if err := VerifySignature(publicKey, signBz, sigData); err != nil { - return err - } - - cs.Sequence++ - cs.ConsensusState.Timestamp = timestamp - setClientState(store, cdc, cs) - return nil -} - -// produceVerificationArgs perfoms the basic checks on the arguments that are -// shared between the verification functions and returns the public key of the -// consensus state, the unmarshalled proof representing the signature and timestamp -// along with the solo-machine sequence encoded in the proofHeight. -func produceVerificationArgs( - cdc codec.BinaryCodec, - cs *ClientState, - height exported.Height, - prefix exported.Prefix, - proof []byte, -) (cryptotypes.PubKey, signing.SignatureData, uint64, uint64, error) { - if revision := height.GetRevisionNumber(); revision != 0 { - return nil, nil, 0, 0, sdkerrors.Wrapf(sdkerrors.ErrInvalidHeight, "revision must be 0 for solomachine, got revision-number: %d", revision) - } - // sequence is encoded in the revision height of height struct - sequence := height.GetRevisionHeight() - if prefix == nil { - return nil, nil, 0, 0, sdkerrors.Wrap(commitmenttypes.ErrInvalidPrefix, "prefix cannot be empty") - } - - _, ok := prefix.(*commitmenttypes.MerklePrefix) - if !ok { - return nil, nil, 0, 0, sdkerrors.Wrapf(commitmenttypes.ErrInvalidPrefix, "invalid prefix type %T, expected MerklePrefix", prefix) - } - - if proof == nil { - return nil, nil, 0, 0, sdkerrors.Wrap(ErrInvalidProof, "proof cannot be empty") - } - - timestampedSigData := &TimestampedSignatureData{} - if err := cdc.Unmarshal(proof, timestampedSigData); err != nil { - return nil, nil, 0, 0, sdkerrors.Wrapf(err, "failed to unmarshal proof into type %T", timestampedSigData) - } - - timestamp := timestampedSigData.Timestamp - - if len(timestampedSigData.SignatureData) == 0 { - return nil, nil, 0, 0, sdkerrors.Wrap(ErrInvalidProof, "signature data cannot be empty") - } - - sigData, err := UnmarshalSignatureData(cdc, timestampedSigData.SignatureData) - if err != nil { - return nil, nil, 0, 0, err - } - - if cs.ConsensusState == nil { - return nil, nil, 0, 0, sdkerrors.Wrap(clienttypes.ErrInvalidConsensus, "consensus state cannot be empty") - } - - latestSequence := cs.GetLatestHeight().GetRevisionHeight() - if latestSequence != sequence { - return nil, nil, 0, 0, sdkerrors.Wrapf( - sdkerrors.ErrInvalidHeight, - "client state sequence != proof sequence (%d != %d)", latestSequence, sequence, - ) - } - - if cs.ConsensusState.GetTimestamp() > timestamp { - return nil, nil, 0, 0, sdkerrors.Wrapf(ErrInvalidProof, "the consensus state timestamp is greater than the signature timestamp (%d >= %d)", cs.ConsensusState.GetTimestamp(), timestamp) - } - - publicKey, err := cs.ConsensusState.GetPubKey() - if err != nil { - return nil, nil, 0, 0, err - } - - return publicKey, sigData, timestamp, sequence, nil -} - -// sets the client state to the store -func setClientState(store sdk.KVStore, cdc codec.BinaryCodec, clientState exported.ClientState) { - bz := clienttypes.MustMarshalClientState(cdc, clientState) - store.Set([]byte(host.KeyClientState), bz) -} diff --git a/modules/light-clients/06-solomachine/types/client_state_test.go b/modules/light-clients/06-solomachine/types/client_state_test.go deleted file mode 100644 index 7ebd9bec925..00000000000 --- a/modules/light-clients/06-solomachine/types/client_state_test.go +++ /dev/null @@ -1,844 +0,0 @@ -package types_test - -import ( - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - connectiontypes "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types" - channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" - commitmenttypes "github.com/cosmos/ibc-go/v4/modules/core/23-commitment/types" - "github.com/cosmos/ibc-go/v4/modules/core/exported" - "github.com/cosmos/ibc-go/v4/modules/light-clients/06-solomachine/types" - ibctmtypes "github.com/cosmos/ibc-go/v4/modules/light-clients/07-tendermint/types" - ibctesting "github.com/cosmos/ibc-go/v4/testing" -) - -const ( - counterpartyClientIdentifier = "chainA" - testConnectionID = "connectionid" - testChannelID = "testchannelid" - testPortID = "testportid" -) - -var ( - prefix = &commitmenttypes.MerklePrefix{ - KeyPrefix: []byte("ibc"), - } - consensusHeight = clienttypes.ZeroHeight() -) - -func (suite *SoloMachineTestSuite) TestStatus() { - clientState := suite.solomachine.ClientState() - // solo machine discards arguements - status := clientState.Status(suite.chainA.GetContext(), nil, nil) - suite.Require().Equal(exported.Active, status) - - // freeze solo machine - clientState.IsFrozen = true - status = clientState.Status(suite.chainA.GetContext(), nil, nil) - suite.Require().Equal(exported.Frozen, status) -} - -func (suite *SoloMachineTestSuite) TestClientStateValidateBasic() { - // test singlesig and multisig public keys - for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { - - testCases := []struct { - name string - clientState *types.ClientState - expPass bool - }{ - { - "valid client state", - solomachine.ClientState(), - true, - }, - { - "empty ClientState", - &types.ClientState{}, - false, - }, - { - "sequence is zero", - types.NewClientState(0, &types.ConsensusState{solomachine.ConsensusState().PublicKey, solomachine.Diversifier, solomachine.Time}, false), - false, - }, - { - "timestamp is zero", - types.NewClientState(1, &types.ConsensusState{solomachine.ConsensusState().PublicKey, solomachine.Diversifier, 0}, false), - false, - }, - { - "diversifier is blank", - types.NewClientState(1, &types.ConsensusState{solomachine.ConsensusState().PublicKey, " ", 1}, false), - false, - }, - { - "pubkey is empty", - types.NewClientState(1, &types.ConsensusState{nil, solomachine.Diversifier, solomachine.Time}, false), - false, - }, - } - - for _, tc := range testCases { - tc := tc - - suite.Run(tc.name, func() { - err := tc.clientState.Validate() - - if tc.expPass { - suite.Require().NoError(err) - } else { - suite.Require().Error(err) - } - }) - } - } -} - -func (suite *SoloMachineTestSuite) TestInitialize() { - // test singlesig and multisig public keys - for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { - malleatedConsensus := solomachine.ClientState().ConsensusState - malleatedConsensus.Timestamp = malleatedConsensus.Timestamp + 10 - - testCases := []struct { - name string - consState exported.ConsensusState - expPass bool - }{ - { - "valid consensus state", - solomachine.ConsensusState(), - true, - }, - { - "nil consensus state", - nil, - false, - }, - { - "invalid consensus state: Tendermint consensus state", - &ibctmtypes.ConsensusState{}, - false, - }, - { - "invalid consensus state: consensus state does not match consensus state in client", - malleatedConsensus, - false, - }, - } - - for _, tc := range testCases { - err := solomachine.ClientState().Initialize( - suite.chainA.GetContext(), suite.chainA.Codec, - suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), "solomachine"), - tc.consState, - ) - - if tc.expPass { - suite.Require().NoError(err, "valid testcase: %s failed", tc.name) - } else { - suite.Require().Error(err, "invalid testcase: %s passed", tc.name) - } - } - } -} - -func (suite *SoloMachineTestSuite) TestVerifyClientState() { - // create client for tendermint so we can use client state for verification - tmPath := ibctesting.NewPath(suite.chainA, suite.chainB) - suite.coordinator.SetupClients(tmPath) - clientState := suite.chainA.GetClientState(tmPath.EndpointA.ClientID) - path := suite.solomachine.GetClientStatePath(counterpartyClientIdentifier) - - // test singlesig and multisig public keys - for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { - - value, err := types.ClientStateSignBytes(suite.chainA.Codec, solomachine.Sequence, solomachine.Time, solomachine.Diversifier, path, clientState) - suite.Require().NoError(err) - - sig := solomachine.GenerateSignature(value) - - signatureDoc := &types.TimestampedSignatureData{ - SignatureData: sig, - Timestamp: solomachine.Time, - } - - proof, err := suite.chainA.Codec.Marshal(signatureDoc) - suite.Require().NoError(err) - - testCases := []struct { - name string - clientState *types.ClientState - prefix exported.Prefix - proof []byte - expPass bool - }{ - { - "successful verification", - solomachine.ClientState(), - prefix, - proof, - true, - }, - { - "ApplyPrefix failed", - solomachine.ClientState(), - nil, - proof, - false, - }, - { - "consensus state in client state is nil", - types.NewClientState(1, nil, false), - prefix, - proof, - false, - }, - { - "client state latest height is less than sequence", - types.NewClientState(solomachine.Sequence-1, - &types.ConsensusState{ - Timestamp: solomachine.Time, - PublicKey: solomachine.ConsensusState().PublicKey, - }, false), - prefix, - proof, - false, - }, - { - "consensus state timestamp is greater than signature", - types.NewClientState(solomachine.Sequence, - &types.ConsensusState{ - Timestamp: solomachine.Time + 1, - PublicKey: solomachine.ConsensusState().PublicKey, - }, false), - prefix, - proof, - false, - }, - - { - "proof is nil", - solomachine.ClientState(), - prefix, - nil, - false, - }, - { - "proof verification failed", - solomachine.ClientState(), - prefix, - suite.GetInvalidProof(), - false, - }, - } - - for _, tc := range testCases { - tc := tc - - suite.Run(tc.name, func() { - var expSeq uint64 - if tc.clientState.ConsensusState != nil { - expSeq = tc.clientState.Sequence + 1 - } - - // NOTE: to replicate the ordering of connection handshake, we must decrement proof height by 1 - height := clienttypes.NewHeight(solomachine.GetHeight().GetRevisionNumber(), solomachine.GetHeight().GetRevisionHeight()-1) - - err := tc.clientState.VerifyClientState( - suite.store, suite.chainA.Codec, height, tc.prefix, counterpartyClientIdentifier, tc.proof, clientState, - ) - - if tc.expPass { - suite.Require().NoError(err) - suite.Require().Equal(expSeq, tc.clientState.Sequence) - suite.Require().Equal(expSeq, suite.GetSequenceFromStore(), "sequence not updated in the store (%d) on valid test case %s", suite.GetSequenceFromStore(), tc.name) - } else { - suite.Require().Error(err) - } - }) - } - } -} - -func (suite *SoloMachineTestSuite) TestVerifyClientConsensusState() { - // create client for tendermint so we can use consensus state for verification - tmPath := ibctesting.NewPath(suite.chainA, suite.chainB) - suite.coordinator.SetupClients(tmPath) - clientState := suite.chainA.GetClientState(tmPath.EndpointA.ClientID) - consensusState, found := suite.chainA.GetConsensusState(tmPath.EndpointA.ClientID, clientState.GetLatestHeight()) - suite.Require().True(found) - - path := suite.solomachine.GetConsensusStatePath(counterpartyClientIdentifier, consensusHeight) - - // test singlesig and multisig public keys - for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { - - value, err := types.ConsensusStateSignBytes(suite.chainA.Codec, solomachine.Sequence, solomachine.Time, solomachine.Diversifier, path, consensusState) - suite.Require().NoError(err) - - sig := solomachine.GenerateSignature(value) - signatureDoc := &types.TimestampedSignatureData{ - SignatureData: sig, - Timestamp: solomachine.Time, - } - - proof, err := suite.chainA.Codec.Marshal(signatureDoc) - suite.Require().NoError(err) - - testCases := []struct { - name string - clientState *types.ClientState - prefix exported.Prefix - proof []byte - expPass bool - }{ - { - "successful verification", - solomachine.ClientState(), - prefix, - proof, - true, - }, - { - "ApplyPrefix failed", - solomachine.ClientState(), - nil, - proof, - false, - }, - { - "consensus state in client state is nil", - types.NewClientState(1, nil, false), - prefix, - proof, - false, - }, - { - "client state latest height is less than sequence", - types.NewClientState(solomachine.Sequence-1, - &types.ConsensusState{ - Timestamp: solomachine.Time, - PublicKey: solomachine.ConsensusState().PublicKey, - }, false), - prefix, - proof, - false, - }, - { - "consensus state timestamp is greater than signature", - types.NewClientState(solomachine.Sequence, - &types.ConsensusState{ - Timestamp: solomachine.Time + 1, - PublicKey: solomachine.ConsensusState().PublicKey, - }, false), - prefix, - proof, - false, - }, - - { - "proof is nil", - solomachine.ClientState(), - prefix, - nil, - false, - }, - { - "proof verification failed", - solomachine.ClientState(), - prefix, - suite.GetInvalidProof(), - false, - }, - } - - for _, tc := range testCases { - tc := tc - - suite.Run(tc.name, func() { - var expSeq uint64 - if tc.clientState.ConsensusState != nil { - expSeq = tc.clientState.Sequence + 1 - } - - // NOTE: to replicate the ordering of connection handshake, we must decrement proof height by 1 - height := clienttypes.NewHeight(solomachine.GetHeight().GetRevisionNumber(), solomachine.GetHeight().GetRevisionHeight()-2) - - err := tc.clientState.VerifyClientConsensusState( - suite.store, suite.chainA.Codec, height, counterpartyClientIdentifier, consensusHeight, tc.prefix, tc.proof, consensusState, - ) - - if tc.expPass { - suite.Require().NoError(err) - suite.Require().Equal(expSeq, tc.clientState.Sequence) - suite.Require().Equal(expSeq, suite.GetSequenceFromStore(), "sequence not updated in the store (%d) on valid test case %s", suite.GetSequenceFromStore(), tc.name) - } else { - suite.Require().Error(err) - } - }) - } - } -} - -func (suite *SoloMachineTestSuite) TestVerifyConnectionState() { - counterparty := connectiontypes.NewCounterparty("clientB", testConnectionID, *prefix) - conn := connectiontypes.NewConnectionEnd(connectiontypes.OPEN, "clientA", counterparty, connectiontypes.ExportedVersionsToProto(connectiontypes.GetCompatibleVersions()), 0) - - path := suite.solomachine.GetConnectionStatePath(testConnectionID) - - // test singlesig and multisig public keys - for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { - - value, err := types.ConnectionStateSignBytes(suite.chainA.Codec, solomachine.Sequence, solomachine.Time, solomachine.Diversifier, path, conn) - suite.Require().NoError(err) - - sig := solomachine.GenerateSignature(value) - signatureDoc := &types.TimestampedSignatureData{ - SignatureData: sig, - Timestamp: solomachine.Time, - } - - proof, err := suite.chainA.Codec.Marshal(signatureDoc) - suite.Require().NoError(err) - - testCases := []struct { - name string - clientState *types.ClientState - prefix exported.Prefix - proof []byte - expPass bool - }{ - { - "successful verification", - solomachine.ClientState(), - prefix, - proof, - true, - }, - { - "ApplyPrefix failed", - solomachine.ClientState(), - commitmenttypes.NewMerklePrefix([]byte{}), - proof, - false, - }, - { - "proof is nil", - solomachine.ClientState(), - prefix, - nil, - false, - }, - { - "proof verification failed", - solomachine.ClientState(), - prefix, - suite.GetInvalidProof(), - false, - }, - } - - for i, tc := range testCases { - tc := tc - - expSeq := tc.clientState.Sequence + 1 - - err := tc.clientState.VerifyConnectionState( - suite.store, suite.chainA.Codec, solomachine.GetHeight(), tc.prefix, tc.proof, testConnectionID, conn, - ) - - if tc.expPass { - suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.name) - suite.Require().Equal(expSeq, tc.clientState.Sequence) - suite.Require().Equal(expSeq, suite.GetSequenceFromStore(), "sequence not updated in the store (%d) on valid test case %d: %s", suite.GetSequenceFromStore(), i, tc.name) - } else { - suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.name) - } - } - } -} - -func (suite *SoloMachineTestSuite) TestVerifyChannelState() { - counterparty := channeltypes.NewCounterparty(testPortID, testChannelID) - ch := channeltypes.NewChannel(channeltypes.OPEN, channeltypes.ORDERED, counterparty, []string{testConnectionID}, "1.0.0") - - path := suite.solomachine.GetChannelStatePath(testPortID, testChannelID) - - // test singlesig and multisig public keys - for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { - - value, err := types.ChannelStateSignBytes(suite.chainA.Codec, solomachine.Sequence, solomachine.Time, solomachine.Diversifier, path, ch) - suite.Require().NoError(err) - - sig := solomachine.GenerateSignature(value) - signatureDoc := &types.TimestampedSignatureData{ - SignatureData: sig, - Timestamp: solomachine.Time, - } - - proof, err := suite.chainA.Codec.Marshal(signatureDoc) - suite.Require().NoError(err) - - testCases := []struct { - name string - clientState *types.ClientState - prefix exported.Prefix - proof []byte - expPass bool - }{ - { - "successful verification", - solomachine.ClientState(), - prefix, - proof, - true, - }, - { - "ApplyPrefix failed", - solomachine.ClientState(), - nil, - proof, - false, - }, - { - "proof is nil", - solomachine.ClientState(), - prefix, - nil, - false, - }, - { - "proof verification failed", - solomachine.ClientState(), - prefix, - suite.GetInvalidProof(), - false, - }, - } - - for i, tc := range testCases { - tc := tc - - expSeq := tc.clientState.Sequence + 1 - - err := tc.clientState.VerifyChannelState( - suite.store, suite.chainA.Codec, solomachine.GetHeight(), tc.prefix, tc.proof, testPortID, testChannelID, ch, - ) - - if tc.expPass { - suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.name) - suite.Require().Equal(expSeq, tc.clientState.Sequence) - suite.Require().Equal(expSeq, suite.GetSequenceFromStore(), "sequence not updated in the store (%d) on valid test case %d: %s", suite.GetSequenceFromStore(), i, tc.name) - } else { - suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.name) - } - } - } -} - -func (suite *SoloMachineTestSuite) TestVerifyPacketCommitment() { - commitmentBytes := []byte("COMMITMENT BYTES") - - // test singlesig and multisig public keys - for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { - - path := solomachine.GetPacketCommitmentPath(testPortID, testChannelID) - - value, err := types.PacketCommitmentSignBytes(suite.chainA.Codec, solomachine.Sequence, solomachine.Time, solomachine.Diversifier, path, commitmentBytes) - suite.Require().NoError(err) - - sig := solomachine.GenerateSignature(value) - signatureDoc := &types.TimestampedSignatureData{ - SignatureData: sig, - Timestamp: solomachine.Time, - } - - proof, err := suite.chainA.Codec.Marshal(signatureDoc) - suite.Require().NoError(err) - - testCases := []struct { - name string - clientState *types.ClientState - prefix exported.Prefix - proof []byte - expPass bool - }{ - { - "successful verification", - solomachine.ClientState(), - prefix, - proof, - true, - }, - { - "ApplyPrefix failed", - solomachine.ClientState(), - commitmenttypes.NewMerklePrefix([]byte{}), - proof, - false, - }, - { - "proof is nil", - solomachine.ClientState(), - prefix, - nil, - false, - }, - { - "proof verification failed", - solomachine.ClientState(), - prefix, - suite.GetInvalidProof(), - false, - }, - } - - for i, tc := range testCases { - tc := tc - - expSeq := tc.clientState.Sequence + 1 - ctx := suite.chainA.GetContext() - - err := tc.clientState.VerifyPacketCommitment( - ctx, suite.store, suite.chainA.Codec, solomachine.GetHeight(), 0, 0, tc.prefix, tc.proof, testPortID, testChannelID, solomachine.Sequence, commitmentBytes, - ) - - if tc.expPass { - suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.name) - suite.Require().Equal(expSeq, suite.GetSequenceFromStore(), "sequence not updated in the store (%d) on valid test case %d: %s", suite.GetSequenceFromStore(), i, tc.name) - } else { - suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.name) - } - } - } -} - -func (suite *SoloMachineTestSuite) TestVerifyPacketAcknowledgement() { - ack := []byte("ACK") - // test singlesig and multisig public keys - for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { - - path := solomachine.GetPacketAcknowledgementPath(testPortID, testChannelID) - - value, err := types.PacketAcknowledgementSignBytes(suite.chainA.Codec, solomachine.Sequence, solomachine.Time, solomachine.Diversifier, path, ack) - suite.Require().NoError(err) - - sig := solomachine.GenerateSignature(value) - signatureDoc := &types.TimestampedSignatureData{ - SignatureData: sig, - Timestamp: solomachine.Time, - } - - proof, err := suite.chainA.Codec.Marshal(signatureDoc) - suite.Require().NoError(err) - - testCases := []struct { - name string - clientState *types.ClientState - prefix exported.Prefix - proof []byte - expPass bool - }{ - { - "successful verification", - solomachine.ClientState(), - prefix, - proof, - true, - }, - { - "ApplyPrefix failed", - solomachine.ClientState(), - commitmenttypes.NewMerklePrefix([]byte{}), - proof, - false, - }, - { - "proof is nil", - solomachine.ClientState(), - prefix, - nil, - false, - }, - { - "proof verification failed", - solomachine.ClientState(), - prefix, - suite.GetInvalidProof(), - false, - }, - } - - for i, tc := range testCases { - tc := tc - - expSeq := tc.clientState.Sequence + 1 - ctx := suite.chainA.GetContext() - - err := tc.clientState.VerifyPacketAcknowledgement( - ctx, suite.store, suite.chainA.Codec, solomachine.GetHeight(), 0, 0, tc.prefix, tc.proof, testPortID, testChannelID, solomachine.Sequence, ack, - ) - - if tc.expPass { - suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.name) - suite.Require().Equal(expSeq, suite.GetSequenceFromStore(), "sequence not updated in the store (%d) on valid test case %d: %s", suite.GetSequenceFromStore(), i, tc.name) - } else { - suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.name) - } - } - } -} - -func (suite *SoloMachineTestSuite) TestVerifyPacketReceiptAbsence() { - // test singlesig and multisig public keys - for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { - - // absence uses receipt path as well - path := solomachine.GetPacketReceiptPath(testPortID, testChannelID) - - value, err := types.PacketReceiptAbsenceSignBytes(suite.chainA.Codec, solomachine.Sequence, solomachine.Time, solomachine.Diversifier, path) - suite.Require().NoError(err) - - sig := solomachine.GenerateSignature(value) - signatureDoc := &types.TimestampedSignatureData{ - SignatureData: sig, - Timestamp: solomachine.Time, - } - - proof, err := suite.chainA.Codec.Marshal(signatureDoc) - suite.Require().NoError(err) - - testCases := []struct { - name string - clientState *types.ClientState - prefix exported.Prefix - proof []byte - expPass bool - }{ - { - "successful verification", - solomachine.ClientState(), - prefix, - proof, - true, - }, - { - "ApplyPrefix failed", - solomachine.ClientState(), - commitmenttypes.NewMerklePrefix([]byte{}), - proof, - false, - }, - { - "proof is nil", - solomachine.ClientState(), - prefix, - nil, - false, - }, - { - "proof verification failed", - solomachine.ClientState(), - prefix, - suite.GetInvalidProof(), - false, - }, - } - - for i, tc := range testCases { - tc := tc - - expSeq := tc.clientState.Sequence + 1 - ctx := suite.chainA.GetContext() - - err := tc.clientState.VerifyPacketReceiptAbsence( - ctx, suite.store, suite.chainA.Codec, solomachine.GetHeight(), 0, 0, tc.prefix, tc.proof, testPortID, testChannelID, solomachine.Sequence, - ) - - if tc.expPass { - suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.name) - suite.Require().Equal(expSeq, suite.GetSequenceFromStore(), "sequence not updated in the store (%d) on valid test case %d: %s", suite.GetSequenceFromStore(), i, tc.name) - } else { - suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.name) - } - } - } -} - -func (suite *SoloMachineTestSuite) TestVerifyNextSeqRecv() { - // test singlesig and multisig public keys - for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { - - nextSeqRecv := solomachine.Sequence + 1 - path := solomachine.GetNextSequenceRecvPath(testPortID, testChannelID) - - value, err := types.NextSequenceRecvSignBytes(suite.chainA.Codec, solomachine.Sequence, solomachine.Time, solomachine.Diversifier, path, nextSeqRecv) - suite.Require().NoError(err) - - sig := solomachine.GenerateSignature(value) - signatureDoc := &types.TimestampedSignatureData{ - SignatureData: sig, - Timestamp: solomachine.Time, - } - - proof, err := suite.chainA.Codec.Marshal(signatureDoc) - suite.Require().NoError(err) - - testCases := []struct { - name string - clientState *types.ClientState - prefix exported.Prefix - proof []byte - expPass bool - }{ - { - "successful verification", - solomachine.ClientState(), - prefix, - proof, - true, - }, - { - "ApplyPrefix failed", - solomachine.ClientState(), - commitmenttypes.NewMerklePrefix([]byte{}), - proof, - false, - }, - { - "proof is nil", - solomachine.ClientState(), - prefix, - nil, - false, - }, - { - "proof verification failed", - solomachine.ClientState(), - prefix, - suite.GetInvalidProof(), - false, - }, - } - - for i, tc := range testCases { - tc := tc - - expSeq := tc.clientState.Sequence + 1 - ctx := suite.chainA.GetContext() - - err := tc.clientState.VerifyNextSequenceRecv( - ctx, suite.store, suite.chainA.Codec, solomachine.GetHeight(), 0, 0, tc.prefix, tc.proof, testPortID, testChannelID, nextSeqRecv, - ) - - if tc.expPass { - suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.name) - suite.Require().Equal(expSeq, tc.clientState.Sequence) - suite.Require().Equal(expSeq, suite.GetSequenceFromStore(), "sequence not updated in the store (%d) on valid test case %d: %s", suite.GetSequenceFromStore(), i, tc.name) - } else { - suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.name) - } - } - } -} diff --git a/modules/light-clients/06-solomachine/types/codec.go b/modules/light-clients/06-solomachine/types/codec.go deleted file mode 100644 index 2fc3c930f5b..00000000000 --- a/modules/light-clients/06-solomachine/types/codec.go +++ /dev/null @@ -1,131 +0,0 @@ -package types - -import ( - "github.com/cosmos/cosmos-sdk/codec" - codectypes "github.com/cosmos/cosmos-sdk/codec/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/cosmos/cosmos-sdk/types/tx/signing" - - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v4/modules/core/exported" -) - -// RegisterInterfaces register the ibc channel submodule interfaces to protobuf -// Any. -func RegisterInterfaces(registry codectypes.InterfaceRegistry) { - registry.RegisterImplementations( - (*exported.ClientState)(nil), - &ClientState{}, - ) - registry.RegisterImplementations( - (*exported.ConsensusState)(nil), - &ConsensusState{}, - ) - registry.RegisterImplementations( - (*exported.Header)(nil), - &Header{}, - ) - registry.RegisterImplementations( - (*exported.Misbehaviour)(nil), - &Misbehaviour{}, - ) -} - -func UnmarshalSignatureData(cdc codec.BinaryCodec, data []byte) (signing.SignatureData, error) { - protoSigData := &signing.SignatureDescriptor_Data{} - if err := cdc.Unmarshal(data, protoSigData); err != nil { - return nil, sdkerrors.Wrapf(err, "failed to unmarshal proof into type %T", protoSigData) - } - - sigData := signing.SignatureDataFromProto(protoSigData) - - return sigData, nil -} - -// UnmarshalDataByType attempts to unmarshal the data to the specified type. An error is -// return if it fails. -func UnmarshalDataByType(cdc codec.BinaryCodec, dataType DataType, data []byte) (Data, error) { - if len(data) == 0 { - return nil, sdkerrors.Wrap(ErrInvalidSignatureAndData, "data cannot be empty") - } - - switch dataType { - case UNSPECIFIED: - return nil, sdkerrors.Wrap(ErrInvalidDataType, "data type cannot be UNSPECIFIED") - - case CLIENT: - clientData := &ClientStateData{} - if err := cdc.Unmarshal(data, clientData); err != nil { - return nil, err - } - - // unpack any - if _, err := clienttypes.UnpackClientState(clientData.ClientState); err != nil { - return nil, err - } - return clientData, nil - - case CONSENSUS: - consensusData := &ConsensusStateData{} - if err := cdc.Unmarshal(data, consensusData); err != nil { - return nil, err - } - - // unpack any - if _, err := clienttypes.UnpackConsensusState(consensusData.ConsensusState); err != nil { - return nil, err - } - return consensusData, nil - - case CONNECTION: - connectionData := &ConnectionStateData{} - if err := cdc.Unmarshal(data, connectionData); err != nil { - return nil, err - } - - return connectionData, nil - - case CHANNEL: - channelData := &ChannelStateData{} - if err := cdc.Unmarshal(data, channelData); err != nil { - return nil, err - } - - return channelData, nil - - case PACKETCOMMITMENT: - commitmentData := &PacketCommitmentData{} - if err := cdc.Unmarshal(data, commitmentData); err != nil { - return nil, err - } - - return commitmentData, nil - - case PACKETACKNOWLEDGEMENT: - ackData := &PacketAcknowledgementData{} - if err := cdc.Unmarshal(data, ackData); err != nil { - return nil, err - } - - return ackData, nil - - case PACKETRECEIPTABSENCE: - receiptAbsenceData := &PacketReceiptAbsenceData{} - if err := cdc.Unmarshal(data, receiptAbsenceData); err != nil { - return nil, err - } - - return receiptAbsenceData, nil - - case NEXTSEQUENCERECV: - nextSeqRecvData := &NextSequenceRecvData{} - if err := cdc.Unmarshal(data, nextSeqRecvData); err != nil { - return nil, err - } - - return nextSeqRecvData, nil - - default: - return nil, sdkerrors.Wrapf(ErrInvalidDataType, "unsupported data type %T", dataType) - } -} diff --git a/modules/light-clients/06-solomachine/types/codec_test.go b/modules/light-clients/06-solomachine/types/codec_test.go deleted file mode 100644 index ed2afdafc41..00000000000 --- a/modules/light-clients/06-solomachine/types/codec_test.go +++ /dev/null @@ -1,186 +0,0 @@ -package types_test - -import ( - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - connectiontypes "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types" - channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" - "github.com/cosmos/ibc-go/v4/modules/light-clients/06-solomachine/types" - ibctesting "github.com/cosmos/ibc-go/v4/testing" -) - -func (suite SoloMachineTestSuite) TestUnmarshalDataByType() { - var ( - data []byte - err error - ) - - // test singlesig and multisig public keys - for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { - - cdc := suite.chainA.App.AppCodec() - cases := []struct { - name string - dataType types.DataType - malleate func() - expPass bool - }{ - { - "empty data", types.CLIENT, func() { - data = []byte{} - }, false, - }, - { - "unspecified", types.UNSPECIFIED, func() { - path := solomachine.GetClientStatePath(counterpartyClientIdentifier) - data, err = types.ClientStateDataBytes(cdc, path, solomachine.ClientState()) - suite.Require().NoError(err) - }, false, - }, - { - "client", types.CLIENT, func() { - path := solomachine.GetClientStatePath(counterpartyClientIdentifier) - data, err = types.ClientStateDataBytes(cdc, path, solomachine.ClientState()) - suite.Require().NoError(err) - }, true, - }, - { - "bad client (provides consensus state data)", types.CLIENT, func() { - path := solomachine.GetConsensusStatePath(counterpartyClientIdentifier, clienttypes.NewHeight(0, 5)) - data, err = types.ConsensusStateDataBytes(cdc, path, solomachine.ConsensusState()) - suite.Require().NoError(err) - }, false, - }, - { - "consensus", types.CONSENSUS, func() { - path := solomachine.GetConsensusStatePath(counterpartyClientIdentifier, clienttypes.NewHeight(0, 5)) - data, err = types.ConsensusStateDataBytes(cdc, path, solomachine.ConsensusState()) - suite.Require().NoError(err) - }, true, - }, - { - "bad consensus (provides client state data)", types.CONSENSUS, func() { - path := solomachine.GetClientStatePath(counterpartyClientIdentifier) - data, err = types.ClientStateDataBytes(cdc, path, solomachine.ClientState()) - suite.Require().NoError(err) - }, false, - }, - { - "connection", types.CONNECTION, func() { - counterparty := connectiontypes.NewCounterparty("clientB", testConnectionID, *prefix) - conn := connectiontypes.NewConnectionEnd(connectiontypes.OPEN, "clientA", counterparty, connectiontypes.ExportedVersionsToProto(connectiontypes.GetCompatibleVersions()), 0) - path := solomachine.GetConnectionStatePath("connectionID") - - data, err = types.ConnectionStateDataBytes(cdc, path, conn) - suite.Require().NoError(err) - }, true, - }, - { - "bad connection (uses channel data)", types.CONNECTION, func() { - counterparty := channeltypes.NewCounterparty(testPortID, testChannelID) - ch := channeltypes.NewChannel(channeltypes.OPEN, channeltypes.ORDERED, counterparty, []string{testConnectionID}, "1.0.0") - path := solomachine.GetChannelStatePath("portID", "channelID") - - data, err = types.ChannelStateDataBytes(cdc, path, ch) - suite.Require().NoError(err) - }, false, - }, - { - "channel", types.CHANNEL, func() { - counterparty := channeltypes.NewCounterparty(testPortID, testChannelID) - ch := channeltypes.NewChannel(channeltypes.OPEN, channeltypes.ORDERED, counterparty, []string{testConnectionID}, "1.0.0") - path := solomachine.GetChannelStatePath("portID", "channelID") - - data, err = types.ChannelStateDataBytes(cdc, path, ch) - suite.Require().NoError(err) - }, true, - }, - { - "bad channel (uses connection data)", types.CHANNEL, func() { - counterparty := connectiontypes.NewCounterparty("clientB", testConnectionID, *prefix) - conn := connectiontypes.NewConnectionEnd(connectiontypes.OPEN, "clientA", counterparty, connectiontypes.ExportedVersionsToProto(connectiontypes.GetCompatibleVersions()), 0) - path := solomachine.GetConnectionStatePath("connectionID") - - data, err = types.ConnectionStateDataBytes(cdc, path, conn) - suite.Require().NoError(err) - }, false, - }, - { - "packet commitment", types.PACKETCOMMITMENT, func() { - commitment := []byte("packet commitment") - path := solomachine.GetPacketCommitmentPath("portID", "channelID") - - data, err = types.PacketCommitmentDataBytes(cdc, path, commitment) - suite.Require().NoError(err) - }, true, - }, - { - "bad packet commitment (uses next seq recv)", types.PACKETCOMMITMENT, func() { - path := solomachine.GetNextSequenceRecvPath("portID", "channelID") - - data, err = types.NextSequenceRecvDataBytes(cdc, path, 10) - suite.Require().NoError(err) - }, false, - }, - { - "packet acknowledgement", types.PACKETACKNOWLEDGEMENT, func() { - commitment := []byte("packet acknowledgement") - path := solomachine.GetPacketAcknowledgementPath("portID", "channelID") - - data, err = types.PacketAcknowledgementDataBytes(cdc, path, commitment) - suite.Require().NoError(err) - }, true, - }, - { - "bad packet acknowledgement (uses next sequence recv)", types.PACKETACKNOWLEDGEMENT, func() { - path := solomachine.GetNextSequenceRecvPath("portID", "channelID") - - data, err = types.NextSequenceRecvDataBytes(cdc, path, 10) - suite.Require().NoError(err) - }, false, - }, - { - "packet acknowledgement absence", types.PACKETRECEIPTABSENCE, func() { - path := solomachine.GetPacketReceiptPath("portID", "channelID") - - data, err = types.PacketReceiptAbsenceDataBytes(cdc, path) - suite.Require().NoError(err) - }, true, - }, - { - "next sequence recv", types.NEXTSEQUENCERECV, func() { - path := solomachine.GetNextSequenceRecvPath("portID", "channelID") - - data, err = types.NextSequenceRecvDataBytes(cdc, path, 10) - suite.Require().NoError(err) - }, true, - }, - { - "bad next sequence recv (uses packet commitment)", types.NEXTSEQUENCERECV, func() { - commitment := []byte("packet commitment") - path := solomachine.GetPacketCommitmentPath("portID", "channelID") - - data, err = types.PacketCommitmentDataBytes(cdc, path, commitment) - suite.Require().NoError(err) - }, false, - }, - } - - for _, tc := range cases { - tc := tc - - suite.Run(tc.name, func() { - tc.malleate() - - data, err := types.UnmarshalDataByType(cdc, tc.dataType, data) - - if tc.expPass { - suite.Require().NoError(err) - suite.Require().NotNil(data) - } else { - suite.Require().Error(err) - suite.Require().Nil(data) - } - }) - } - } -} diff --git a/modules/light-clients/06-solomachine/types/errors.go b/modules/light-clients/06-solomachine/types/errors.go deleted file mode 100644 index 3e27f60732a..00000000000 --- a/modules/light-clients/06-solomachine/types/errors.go +++ /dev/null @@ -1,18 +0,0 @@ -package types - -import ( - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" -) - -const ( - SubModuleName = "solo machine" -) - -var ( - ErrInvalidHeader = sdkerrors.Register(SubModuleName, 2, "invalid header") - ErrInvalidSequence = sdkerrors.Register(SubModuleName, 3, "invalid sequence") - ErrInvalidSignatureAndData = sdkerrors.Register(SubModuleName, 4, "invalid signature and data") - ErrSignatureVerificationFailed = sdkerrors.Register(SubModuleName, 5, "signature verification failed") - ErrInvalidProof = sdkerrors.Register(SubModuleName, 6, "invalid solo machine proof") - ErrInvalidDataType = sdkerrors.Register(SubModuleName, 7, "invalid data type") -) diff --git a/modules/light-clients/06-solomachine/types/misbehaviour.go b/modules/light-clients/06-solomachine/types/misbehaviour.go deleted file mode 100644 index 6c002c45046..00000000000 --- a/modules/light-clients/06-solomachine/types/misbehaviour.go +++ /dev/null @@ -1,77 +0,0 @@ -package types - -import ( - "bytes" - - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" - "github.com/cosmos/ibc-go/v4/modules/core/exported" -) - -var _ exported.Misbehaviour = &Misbehaviour{} - -// ClientType is a Solo Machine light client. -func (misbehaviour Misbehaviour) ClientType() string { - return exported.Solomachine -} - -// GetClientID returns the ID of the client that committed a misbehaviour. -func (misbehaviour Misbehaviour) GetClientID() string { - return misbehaviour.ClientId -} - -// Type implements Evidence interface. -func (misbehaviour Misbehaviour) Type() string { - return exported.TypeClientMisbehaviour -} - -// ValidateBasic implements Evidence interface. -func (misbehaviour Misbehaviour) ValidateBasic() error { - if err := host.ClientIdentifierValidator(misbehaviour.ClientId); err != nil { - return sdkerrors.Wrap(err, "invalid client identifier for solo machine") - } - - if misbehaviour.Sequence == 0 { - return sdkerrors.Wrap(clienttypes.ErrInvalidMisbehaviour, "sequence cannot be 0") - } - - if err := misbehaviour.SignatureOne.ValidateBasic(); err != nil { - return sdkerrors.Wrap(err, "signature one failed basic validation") - } - - if err := misbehaviour.SignatureTwo.ValidateBasic(); err != nil { - return sdkerrors.Wrap(err, "signature two failed basic validation") - } - - // misbehaviour signatures cannot be identical - if bytes.Equal(misbehaviour.SignatureOne.Signature, misbehaviour.SignatureTwo.Signature) { - return sdkerrors.Wrap(clienttypes.ErrInvalidMisbehaviour, "misbehaviour signatures cannot be equal") - } - - // message data signed cannot be identical - if bytes.Equal(misbehaviour.SignatureOne.Data, misbehaviour.SignatureTwo.Data) { - return sdkerrors.Wrap(clienttypes.ErrInvalidMisbehaviour, "misbehaviour signature data must be signed over different messages") - } - - return nil -} - -// ValidateBasic ensures that the signature and data fields are non-empty. -func (sd SignatureAndData) ValidateBasic() error { - if len(sd.Signature) == 0 { - return sdkerrors.Wrap(ErrInvalidSignatureAndData, "signature cannot be empty") - } - if len(sd.Data) == 0 { - return sdkerrors.Wrap(ErrInvalidSignatureAndData, "data for signature cannot be empty") - } - if sd.DataType == UNSPECIFIED { - return sdkerrors.Wrap(ErrInvalidSignatureAndData, "data type cannot be UNSPECIFIED") - } - if sd.Timestamp == 0 { - return sdkerrors.Wrap(ErrInvalidSignatureAndData, "timestamp cannot be 0") - } - - return nil -} diff --git a/modules/light-clients/06-solomachine/types/misbehaviour_handle.go b/modules/light-clients/06-solomachine/types/misbehaviour_handle.go deleted file mode 100644 index 339ec884f1b..00000000000 --- a/modules/light-clients/06-solomachine/types/misbehaviour_handle.go +++ /dev/null @@ -1,86 +0,0 @@ -package types - -import ( - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v4/modules/core/exported" -) - -// CheckMisbehaviourAndUpdateState determines whether or not the currently registered -// public key signed over two different messages with the same sequence. If this is true -// the client state is updated to a frozen status. -// NOTE: Misbehaviour is not tracked for previous public keys, a solo machine may update to -// a new public key before the misbehaviour is processed. Therefore, misbehaviour is data -// order processing dependent. -func (cs ClientState) CheckMisbehaviourAndUpdateState( - ctx sdk.Context, - cdc codec.BinaryCodec, - clientStore sdk.KVStore, - misbehaviour exported.Misbehaviour, -) (exported.ClientState, error) { - soloMisbehaviour, ok := misbehaviour.(*Misbehaviour) - if !ok { - return nil, sdkerrors.Wrapf( - clienttypes.ErrInvalidClientType, - "misbehaviour type %T, expected %T", misbehaviour, &Misbehaviour{}, - ) - } - - // NOTE: a check that the misbehaviour message data are not equal is done by - // misbehaviour.ValidateBasic which is called by the 02-client keeper. - - // verify first signature - if err := verifySignatureAndData(cdc, cs, soloMisbehaviour, soloMisbehaviour.SignatureOne); err != nil { - return nil, sdkerrors.Wrap(err, "failed to verify signature one") - } - - // verify second signature - if err := verifySignatureAndData(cdc, cs, soloMisbehaviour, soloMisbehaviour.SignatureTwo); err != nil { - return nil, sdkerrors.Wrap(err, "failed to verify signature two") - } - - cs.IsFrozen = true - return &cs, nil -} - -// verifySignatureAndData verifies that the currently registered public key has signed -// over the provided data and that the data is valid. The data is valid if it can be -// unmarshaled into the specified data type. -func verifySignatureAndData(cdc codec.BinaryCodec, clientState ClientState, misbehaviour *Misbehaviour, sigAndData *SignatureAndData) error { - // do not check misbehaviour timestamp since we want to allow processing of past misbehaviour - - // ensure data can be unmarshaled to the specified data type - if _, err := UnmarshalDataByType(cdc, sigAndData.DataType, sigAndData.Data); err != nil { - return err - } - - data, err := MisbehaviourSignBytes( - cdc, - misbehaviour.Sequence, sigAndData.Timestamp, - clientState.ConsensusState.Diversifier, - sigAndData.DataType, - sigAndData.Data, - ) - if err != nil { - return err - } - - sigData, err := UnmarshalSignatureData(cdc, sigAndData.Signature) - if err != nil { - return err - } - - publicKey, err := clientState.ConsensusState.GetPubKey() - if err != nil { - return err - } - - if err := VerifySignature(publicKey, data, sigData); err != nil { - return err - } - - return nil -} diff --git a/modules/light-clients/06-solomachine/types/misbehaviour_handle_test.go b/modules/light-clients/06-solomachine/types/misbehaviour_handle_test.go deleted file mode 100644 index d2718f75689..00000000000 --- a/modules/light-clients/06-solomachine/types/misbehaviour_handle_test.go +++ /dev/null @@ -1,264 +0,0 @@ -package types_test - -import ( - "github.com/cosmos/ibc-go/v4/modules/core/exported" - "github.com/cosmos/ibc-go/v4/modules/light-clients/06-solomachine/types" - ibctmtypes "github.com/cosmos/ibc-go/v4/modules/light-clients/07-tendermint/types" - ibctesting "github.com/cosmos/ibc-go/v4/testing" -) - -func (suite *SoloMachineTestSuite) TestCheckMisbehaviourAndUpdateState() { - var ( - clientState exported.ClientState - misbehaviour exported.Misbehaviour - ) - - // test singlesig and multisig public keys - for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { - - testCases := []struct { - name string - setup func() - expPass bool - }{ - { - "valid misbehaviour", - func() { - clientState = solomachine.ClientState() - misbehaviour = solomachine.CreateMisbehaviour() - }, - true, - }, - { - "old misbehaviour is successful (timestamp is less than current consensus state)", - func() { - clientState = solomachine.ClientState() - solomachine.Time = solomachine.Time - 5 - misbehaviour = solomachine.CreateMisbehaviour() - }, true, - }, - { - "wrong client state type", - func() { - clientState = &ibctmtypes.ClientState{} - misbehaviour = solomachine.CreateMisbehaviour() - }, - false, - }, - { - "invalid misbehaviour type", - func() { - clientState = solomachine.ClientState() - misbehaviour = &ibctmtypes.Misbehaviour{} - }, - false, - }, - { - "invalid SignatureOne SignatureData", - func() { - clientState = solomachine.ClientState() - m := solomachine.CreateMisbehaviour() - - m.SignatureOne.Signature = suite.GetInvalidProof() - misbehaviour = m - }, false, - }, - { - "invalid SignatureTwo SignatureData", - func() { - clientState = solomachine.ClientState() - m := solomachine.CreateMisbehaviour() - - m.SignatureTwo.Signature = suite.GetInvalidProof() - misbehaviour = m - }, false, - }, - { - "invalid SignatureOne timestamp", - func() { - clientState = solomachine.ClientState() - m := solomachine.CreateMisbehaviour() - - m.SignatureOne.Timestamp = 1000000000000 - misbehaviour = m - }, false, - }, - { - "invalid SignatureTwo timestamp", - func() { - clientState = solomachine.ClientState() - m := solomachine.CreateMisbehaviour() - - m.SignatureTwo.Timestamp = 1000000000000 - misbehaviour = m - }, false, - }, - { - "invalid first signature data", - func() { - clientState = solomachine.ClientState() - - // store in temp before assigning to interface type - m := solomachine.CreateMisbehaviour() - - msg := []byte("DATA ONE") - signBytes := &types.SignBytes{ - Sequence: solomachine.Sequence + 1, - Timestamp: solomachine.Time, - Diversifier: solomachine.Diversifier, - DataType: types.CLIENT, - Data: msg, - } - - data, err := suite.chainA.Codec.Marshal(signBytes) - suite.Require().NoError(err) - - sig := solomachine.GenerateSignature(data) - - m.SignatureOne.Signature = sig - m.SignatureOne.Data = msg - misbehaviour = m - }, - false, - }, - { - "invalid second signature data", - func() { - clientState = solomachine.ClientState() - - // store in temp before assigning to interface type - m := solomachine.CreateMisbehaviour() - - msg := []byte("DATA TWO") - signBytes := &types.SignBytes{ - Sequence: solomachine.Sequence + 1, - Timestamp: solomachine.Time, - Diversifier: solomachine.Diversifier, - DataType: types.CLIENT, - Data: msg, - } - - data, err := suite.chainA.Codec.Marshal(signBytes) - suite.Require().NoError(err) - - sig := solomachine.GenerateSignature(data) - - m.SignatureTwo.Signature = sig - m.SignatureTwo.Data = msg - misbehaviour = m - }, - false, - }, - { - "wrong pubkey generates first signature", - func() { - clientState = solomachine.ClientState() - badMisbehaviour := solomachine.CreateMisbehaviour() - - // update public key to a new one - solomachine.CreateHeader() - m := solomachine.CreateMisbehaviour() - - // set SignatureOne to use the wrong signature - m.SignatureOne = badMisbehaviour.SignatureOne - misbehaviour = m - }, false, - }, - { - "wrong pubkey generates second signature", - func() { - clientState = solomachine.ClientState() - badMisbehaviour := solomachine.CreateMisbehaviour() - - // update public key to a new one - solomachine.CreateHeader() - m := solomachine.CreateMisbehaviour() - - // set SignatureTwo to use the wrong signature - m.SignatureTwo = badMisbehaviour.SignatureTwo - misbehaviour = m - }, false, - }, - - { - "signatures sign over different sequence", - func() { - clientState = solomachine.ClientState() - - // store in temp before assigning to interface type - m := solomachine.CreateMisbehaviour() - - // Signature One - msg := []byte("DATA ONE") - // sequence used is plus 1 - signBytes := &types.SignBytes{ - Sequence: solomachine.Sequence + 1, - Timestamp: solomachine.Time, - Diversifier: solomachine.Diversifier, - DataType: types.CLIENT, - Data: msg, - } - - data, err := suite.chainA.Codec.Marshal(signBytes) - suite.Require().NoError(err) - - sig := solomachine.GenerateSignature(data) - - m.SignatureOne.Signature = sig - m.SignatureOne.Data = msg - - // Signature Two - msg = []byte("DATA TWO") - // sequence used is minus 1 - - signBytes = &types.SignBytes{ - Sequence: solomachine.Sequence - 1, - Timestamp: solomachine.Time, - Diversifier: solomachine.Diversifier, - DataType: types.CLIENT, - Data: msg, - } - data, err = suite.chainA.Codec.Marshal(signBytes) - suite.Require().NoError(err) - - sig = solomachine.GenerateSignature(data) - - m.SignatureTwo.Signature = sig - m.SignatureTwo.Data = msg - - misbehaviour = m - }, - false, - }, - { - "consensus state pubkey is nil", - func() { - cs := solomachine.ClientState() - cs.ConsensusState.PublicKey = nil - clientState = cs - misbehaviour = solomachine.CreateMisbehaviour() - }, - false, - }, - } - - for _, tc := range testCases { - tc := tc - - suite.Run(tc.name, func() { - // setup test - tc.setup() - - clientState, err := clientState.CheckMisbehaviourAndUpdateState(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), suite.store, misbehaviour) - - if tc.expPass { - suite.Require().NoError(err) - suite.Require().True(clientState.(*types.ClientState).IsFrozen, "client not frozen") - } else { - suite.Require().Error(err) - suite.Require().Nil(clientState) - } - }) - } - } -} diff --git a/modules/light-clients/06-solomachine/types/proof.go b/modules/light-clients/06-solomachine/types/proof.go deleted file mode 100644 index 7389121488f..00000000000 --- a/modules/light-clients/06-solomachine/types/proof.go +++ /dev/null @@ -1,477 +0,0 @@ -package types - -import ( - "github.com/cosmos/cosmos-sdk/codec" - cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" - "github.com/cosmos/cosmos-sdk/crypto/types/multisig" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/cosmos/cosmos-sdk/types/tx/signing" - - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - connectiontypes "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types" - channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" - commitmenttypes "github.com/cosmos/ibc-go/v4/modules/core/23-commitment/types" - "github.com/cosmos/ibc-go/v4/modules/core/exported" -) - -// VerifySignature verifies if the the provided public key generated the signature -// over the given data. Single and Multi signature public keys are supported. -// The signature data type must correspond to the public key type. An error is -// returned if signature verification fails or an invalid SignatureData type is -// provided. -func VerifySignature(pubKey cryptotypes.PubKey, signBytes []byte, sigData signing.SignatureData) error { - switch pubKey := pubKey.(type) { - case multisig.PubKey: - data, ok := sigData.(*signing.MultiSignatureData) - if !ok { - return sdkerrors.Wrapf(ErrSignatureVerificationFailed, "invalid signature data type, expected %T, got %T", (*signing.MultiSignatureData)(nil), data) - } - - // The function supplied fulfills the VerifyMultisignature interface. No special - // adjustments need to be made to the sign bytes based on the sign mode. - if err := pubKey.VerifyMultisignature(func(signing.SignMode) ([]byte, error) { - return signBytes, nil - }, data); err != nil { - return err - } - - default: - data, ok := sigData.(*signing.SingleSignatureData) - if !ok { - return sdkerrors.Wrapf(ErrSignatureVerificationFailed, "invalid signature data type, expected %T, got %T", (*signing.SingleSignatureData)(nil), data) - } - - if !pubKey.VerifySignature(signBytes, data.Signature) { - return ErrSignatureVerificationFailed - } - } - - return nil -} - -// MisbehaviourSignBytes returns the sign bytes for verification of misbehaviour. -func MisbehaviourSignBytes( - cdc codec.BinaryCodec, - sequence, timestamp uint64, - diversifier string, - dataType DataType, - data []byte, -) ([]byte, error) { - signBytes := &SignBytes{ - Sequence: sequence, - Timestamp: timestamp, - Diversifier: diversifier, - DataType: dataType, - Data: data, - } - - return cdc.Marshal(signBytes) -} - -// HeaderSignBytes returns the sign bytes for verification of misbehaviour. -func HeaderSignBytes( - cdc codec.BinaryCodec, - header *Header, -) ([]byte, error) { - data := &HeaderData{ - NewPubKey: header.NewPublicKey, - NewDiversifier: header.NewDiversifier, - } - - dataBz, err := cdc.Marshal(data) - if err != nil { - return nil, err - } - - signBytes := &SignBytes{ - Sequence: header.Sequence, - Timestamp: header.Timestamp, - Diversifier: header.NewDiversifier, - DataType: HEADER, - Data: dataBz, - } - - return cdc.Marshal(signBytes) -} - -// ClientStateSignBytes returns the sign bytes for verification of the -// client state. -func ClientStateSignBytes( - cdc codec.BinaryCodec, - sequence, timestamp uint64, - diversifier string, - path commitmenttypes.MerklePath, - clientState exported.ClientState, -) ([]byte, error) { - dataBz, err := ClientStateDataBytes(cdc, path, clientState) - if err != nil { - return nil, err - } - - signBytes := &SignBytes{ - Sequence: sequence, - Timestamp: timestamp, - Diversifier: diversifier, - DataType: CLIENT, - Data: dataBz, - } - - return cdc.Marshal(signBytes) -} - -// ClientStateDataBytes returns the client state data bytes used in constructing -// SignBytes. -func ClientStateDataBytes( - cdc codec.BinaryCodec, - path commitmenttypes.MerklePath, // nolint: interfacer - clientState exported.ClientState, -) ([]byte, error) { - any, err := clienttypes.PackClientState(clientState) - if err != nil { - return nil, err - } - - data := &ClientStateData{ - Path: []byte(path.String()), - ClientState: any, - } - - dataBz, err := cdc.Marshal(data) - if err != nil { - return nil, err - } - - return dataBz, nil -} - -// ConsensusStateSignBytes returns the sign bytes for verification of the -// consensus state. -func ConsensusStateSignBytes( - cdc codec.BinaryCodec, - sequence, timestamp uint64, - diversifier string, - path commitmenttypes.MerklePath, - consensusState exported.ConsensusState, -) ([]byte, error) { - dataBz, err := ConsensusStateDataBytes(cdc, path, consensusState) - if err != nil { - return nil, err - } - - signBytes := &SignBytes{ - Sequence: sequence, - Timestamp: timestamp, - Diversifier: diversifier, - DataType: CONSENSUS, - Data: dataBz, - } - - return cdc.Marshal(signBytes) -} - -// ConsensusStateDataBytes returns the consensus state data bytes used in constructing -// SignBytes. -func ConsensusStateDataBytes( - cdc codec.BinaryCodec, - path commitmenttypes.MerklePath, // nolint: interfacer - consensusState exported.ConsensusState, -) ([]byte, error) { - any, err := clienttypes.PackConsensusState(consensusState) - if err != nil { - return nil, err - } - - data := &ConsensusStateData{ - Path: []byte(path.String()), - ConsensusState: any, - } - - dataBz, err := cdc.Marshal(data) - if err != nil { - return nil, err - } - - return dataBz, nil -} - -// ConnectionStateSignBytes returns the sign bytes for verification of the -// connection state. -func ConnectionStateSignBytes( - cdc codec.BinaryCodec, - sequence, timestamp uint64, - diversifier string, - path commitmenttypes.MerklePath, - connectionEnd exported.ConnectionI, -) ([]byte, error) { - dataBz, err := ConnectionStateDataBytes(cdc, path, connectionEnd) - if err != nil { - return nil, err - } - - signBytes := &SignBytes{ - Sequence: sequence, - Timestamp: timestamp, - Diversifier: diversifier, - DataType: CONNECTION, - Data: dataBz, - } - - return cdc.Marshal(signBytes) -} - -// ConnectionStateDataBytes returns the connection state data bytes used in constructing -// SignBytes. -func ConnectionStateDataBytes( - cdc codec.BinaryCodec, - path commitmenttypes.MerklePath, // nolint: interfacer - connectionEnd exported.ConnectionI, -) ([]byte, error) { - connection, ok := connectionEnd.(connectiontypes.ConnectionEnd) - if !ok { - return nil, sdkerrors.Wrapf( - connectiontypes.ErrInvalidConnection, - "expected type %T, got %T", connectiontypes.ConnectionEnd{}, connectionEnd, - ) - } - - data := &ConnectionStateData{ - Path: []byte(path.String()), - Connection: &connection, - } - - dataBz, err := cdc.Marshal(data) - if err != nil { - return nil, err - } - - return dataBz, nil -} - -// ChannelStateSignBytes returns the sign bytes for verification of the -// channel state. -func ChannelStateSignBytes( - cdc codec.BinaryCodec, - sequence, timestamp uint64, - diversifier string, - path commitmenttypes.MerklePath, - channelEnd exported.ChannelI, -) ([]byte, error) { - dataBz, err := ChannelStateDataBytes(cdc, path, channelEnd) - if err != nil { - return nil, err - } - - signBytes := &SignBytes{ - Sequence: sequence, - Timestamp: timestamp, - Diversifier: diversifier, - DataType: CHANNEL, - Data: dataBz, - } - - return cdc.Marshal(signBytes) -} - -// ChannelStateDataBytes returns the channel state data bytes used in constructing -// SignBytes. -func ChannelStateDataBytes( - cdc codec.BinaryCodec, - path commitmenttypes.MerklePath, // nolint: interfacer - channelEnd exported.ChannelI, -) ([]byte, error) { - channel, ok := channelEnd.(channeltypes.Channel) - if !ok { - return nil, sdkerrors.Wrapf( - channeltypes.ErrInvalidChannel, - "expected channel type %T, got %T", channeltypes.Channel{}, channelEnd) - } - - data := &ChannelStateData{ - Path: []byte(path.String()), - Channel: &channel, - } - - dataBz, err := cdc.Marshal(data) - if err != nil { - return nil, err - } - - return dataBz, nil -} - -// PacketCommitmentSignBytes returns the sign bytes for verification of the -// packet commitment. -func PacketCommitmentSignBytes( - cdc codec.BinaryCodec, - sequence, timestamp uint64, - diversifier string, - path commitmenttypes.MerklePath, - commitmentBytes []byte, -) ([]byte, error) { - dataBz, err := PacketCommitmentDataBytes(cdc, path, commitmentBytes) - if err != nil { - return nil, err - } - - signBytes := &SignBytes{ - Sequence: sequence, - Timestamp: timestamp, - Diversifier: diversifier, - DataType: PACKETCOMMITMENT, - Data: dataBz, - } - - return cdc.Marshal(signBytes) -} - -// PacketCommitmentDataBytes returns the packet commitment data bytes used in constructing -// SignBytes. -func PacketCommitmentDataBytes( - cdc codec.BinaryCodec, - path commitmenttypes.MerklePath, // nolint: interfacer - commitmentBytes []byte, -) ([]byte, error) { - data := &PacketCommitmentData{ - Path: []byte(path.String()), - Commitment: commitmentBytes, - } - - dataBz, err := cdc.Marshal(data) - if err != nil { - return nil, err - } - - return dataBz, nil -} - -// PacketAcknowledgementSignBytes returns the sign bytes for verification of -// the acknowledgement. -func PacketAcknowledgementSignBytes( - cdc codec.BinaryCodec, - sequence, timestamp uint64, - diversifier string, - path commitmenttypes.MerklePath, - acknowledgement []byte, -) ([]byte, error) { - dataBz, err := PacketAcknowledgementDataBytes(cdc, path, acknowledgement) - if err != nil { - return nil, err - } - - signBytes := &SignBytes{ - Sequence: sequence, - Timestamp: timestamp, - Diversifier: diversifier, - DataType: PACKETACKNOWLEDGEMENT, - Data: dataBz, - } - - return cdc.Marshal(signBytes) -} - -// PacketAcknowledgementDataBytes returns the packet acknowledgement data bytes used in constructing -// SignBytes. -func PacketAcknowledgementDataBytes( - cdc codec.BinaryCodec, - path commitmenttypes.MerklePath, // nolint: interfacer - acknowledgement []byte, -) ([]byte, error) { - data := &PacketAcknowledgementData{ - Path: []byte(path.String()), - Acknowledgement: acknowledgement, - } - - dataBz, err := cdc.Marshal(data) - if err != nil { - return nil, err - } - - return dataBz, nil -} - -// PacketReceiptAbsenceSignBytes returns the sign bytes for verification -// of the absence of an receipt. -func PacketReceiptAbsenceSignBytes( - cdc codec.BinaryCodec, - sequence, timestamp uint64, - diversifier string, - path commitmenttypes.MerklePath, -) ([]byte, error) { - dataBz, err := PacketReceiptAbsenceDataBytes(cdc, path) - if err != nil { - return nil, err - } - - signBytes := &SignBytes{ - Sequence: sequence, - Timestamp: timestamp, - Diversifier: diversifier, - DataType: PACKETRECEIPTABSENCE, - Data: dataBz, - } - - return cdc.Marshal(signBytes) -} - -// PacketReceiptAbsenceDataBytes returns the packet receipt absence data bytes -// used in constructing SignBytes. -func PacketReceiptAbsenceDataBytes( - cdc codec.BinaryCodec, - path commitmenttypes.MerklePath, // nolint: interfacer -) ([]byte, error) { - data := &PacketReceiptAbsenceData{ - Path: []byte(path.String()), - } - - dataBz, err := cdc.Marshal(data) - if err != nil { - return nil, err - } - - return dataBz, nil -} - -// NextSequenceRecvSignBytes returns the sign bytes for verification of the next -// sequence to be received. -func NextSequenceRecvSignBytes( - cdc codec.BinaryCodec, - sequence, timestamp uint64, - diversifier string, - path commitmenttypes.MerklePath, - nextSequenceRecv uint64, -) ([]byte, error) { - dataBz, err := NextSequenceRecvDataBytes(cdc, path, nextSequenceRecv) - if err != nil { - return nil, err - } - - signBytes := &SignBytes{ - Sequence: sequence, - Timestamp: timestamp, - Diversifier: diversifier, - DataType: NEXTSEQUENCERECV, - Data: dataBz, - } - - return cdc.Marshal(signBytes) -} - -// NextSequenceRecvDataBytes returns the next sequence recv data bytes used in constructing -// SignBytes. -func NextSequenceRecvDataBytes( - cdc codec.BinaryCodec, - path commitmenttypes.MerklePath, // nolint: interfacer - nextSequenceRecv uint64, -) ([]byte, error) { - data := &NextSequenceRecvData{ - Path: []byte(path.String()), - NextSeqRecv: nextSequenceRecv, - } - - dataBz, err := cdc.Marshal(data) - if err != nil { - return nil, err - } - - return dataBz, nil -} diff --git a/modules/light-clients/06-solomachine/types/proof_test.go b/modules/light-clients/06-solomachine/types/proof_test.go deleted file mode 100644 index f7f386e6339..00000000000 --- a/modules/light-clients/06-solomachine/types/proof_test.go +++ /dev/null @@ -1,103 +0,0 @@ -package types_test - -import ( - cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" - "github.com/cosmos/cosmos-sdk/types/tx/signing" - - "github.com/cosmos/ibc-go/v4/modules/light-clients/06-solomachine/types" - solomachinetypes "github.com/cosmos/ibc-go/v4/modules/light-clients/06-solomachine/types" - ibctesting "github.com/cosmos/ibc-go/v4/testing" -) - -func (suite *SoloMachineTestSuite) TestVerifySignature() { - cdc := suite.chainA.App.AppCodec() - signBytes := []byte("sign bytes") - - singleSignature := suite.solomachine.GenerateSignature(signBytes) - singleSigData, err := solomachinetypes.UnmarshalSignatureData(cdc, singleSignature) - suite.Require().NoError(err) - - multiSignature := suite.solomachineMulti.GenerateSignature(signBytes) - multiSigData, err := solomachinetypes.UnmarshalSignatureData(cdc, multiSignature) - suite.Require().NoError(err) - - testCases := []struct { - name string - publicKey cryptotypes.PubKey - sigData signing.SignatureData - expPass bool - }{ - { - "single signature with regular public key", - suite.solomachine.PublicKey, - singleSigData, - true, - }, - { - "multi signature with multisig public key", - suite.solomachineMulti.PublicKey, - multiSigData, - true, - }, - { - "single signature with multisig public key", - suite.solomachineMulti.PublicKey, - singleSigData, - false, - }, - { - "multi signature with regular public key", - suite.solomachine.PublicKey, - multiSigData, - false, - }, - } - - for _, tc := range testCases { - tc := tc - - suite.Run(tc.name, func() { - err := solomachinetypes.VerifySignature(tc.publicKey, signBytes, tc.sigData) - - if tc.expPass { - suite.Require().NoError(err) - } else { - suite.Require().Error(err) - } - }) - } -} - -func (suite *SoloMachineTestSuite) TestClientStateSignBytes() { - cdc := suite.chainA.App.AppCodec() - - for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { - // success - path := solomachine.GetClientStatePath(counterpartyClientIdentifier) - bz, err := types.ClientStateSignBytes(cdc, solomachine.Sequence, solomachine.Time, solomachine.Diversifier, path, solomachine.ClientState()) - suite.Require().NoError(err) - suite.Require().NotNil(bz) - - // nil client state - bz, err = types.ClientStateSignBytes(cdc, solomachine.Sequence, solomachine.Time, solomachine.Diversifier, path, nil) - suite.Require().Error(err) - suite.Require().Nil(bz) - } -} - -func (suite *SoloMachineTestSuite) TestConsensusStateSignBytes() { - cdc := suite.chainA.App.AppCodec() - - for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { - // success - path := solomachine.GetConsensusStatePath(counterpartyClientIdentifier, consensusHeight) - bz, err := types.ConsensusStateSignBytes(cdc, solomachine.Sequence, solomachine.Time, solomachine.Diversifier, path, solomachine.ConsensusState()) - suite.Require().NoError(err) - suite.Require().NotNil(bz) - - // nil consensus state - bz, err = types.ConsensusStateSignBytes(cdc, solomachine.Sequence, solomachine.Time, solomachine.Diversifier, path, nil) - suite.Require().Error(err) - suite.Require().Nil(bz) - } -} diff --git a/modules/light-clients/06-solomachine/types/proposal_handle_test.go b/modules/light-clients/06-solomachine/types/proposal_handle_test.go deleted file mode 100644 index b283cdbfdd9..00000000000 --- a/modules/light-clients/06-solomachine/types/proposal_handle_test.go +++ /dev/null @@ -1,88 +0,0 @@ -package types_test - -import ( - "github.com/cosmos/ibc-go/v4/modules/core/exported" - "github.com/cosmos/ibc-go/v4/modules/light-clients/06-solomachine/types" - ibctmtypes "github.com/cosmos/ibc-go/v4/modules/light-clients/07-tendermint/types" - ibctesting "github.com/cosmos/ibc-go/v4/testing" -) - -func (suite *SoloMachineTestSuite) TestCheckSubstituteAndUpdateState() { - var ( - subjectClientState *types.ClientState - substituteClientState exported.ClientState - ) - - // test singlesig and multisig public keys - for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { - - testCases := []struct { - name string - malleate func() - expPass bool - }{ - { - "valid substitute", func() { - subjectClientState.AllowUpdateAfterProposal = true - }, true, - }, - { - "subject not allowed to be updated", func() { - subjectClientState.AllowUpdateAfterProposal = false - }, false, - }, - { - "substitute is not the solo machine", func() { - substituteClientState = &ibctmtypes.ClientState{} - }, false, - }, - { - "subject public key is nil", func() { - subjectClientState.ConsensusState.PublicKey = nil - }, false, - }, - - { - "substitute public key is nil", func() { - substituteClientState.(*types.ClientState).ConsensusState.PublicKey = nil - }, false, - }, - { - "subject and substitute use the same public key", func() { - substituteClientState.(*types.ClientState).ConsensusState.PublicKey = subjectClientState.ConsensusState.PublicKey - }, false, - }, - } - - for _, tc := range testCases { - tc := tc - - suite.Run(tc.name, func() { - suite.SetupTest() - - subjectClientState = solomachine.ClientState() - subjectClientState.AllowUpdateAfterProposal = true - substitute := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "substitute", "testing", 5) - substituteClientState = substitute.ClientState() - - tc.malleate() - - subjectClientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), solomachine.ClientID) - substituteClientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), substitute.ClientID) - - updatedClient, err := subjectClientState.CheckSubstituteAndUpdateState(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), subjectClientStore, substituteClientStore, substituteClientState) - - if tc.expPass { - suite.Require().NoError(err) - - suite.Require().Equal(substituteClientState.(*types.ClientState).ConsensusState, updatedClient.(*types.ClientState).ConsensusState) - suite.Require().Equal(substituteClientState.(*types.ClientState).Sequence, updatedClient.(*types.ClientState).Sequence) - suite.Require().Equal(false, updatedClient.(*types.ClientState).IsFrozen) - } else { - suite.Require().Error(err) - suite.Require().Nil(updatedClient) - } - }) - } - } -} diff --git a/modules/light-clients/06-solomachine/types/solomachine_test.go b/modules/light-clients/06-solomachine/types/solomachine_test.go deleted file mode 100644 index b286afc4ef7..00000000000 --- a/modules/light-clients/06-solomachine/types/solomachine_test.go +++ /dev/null @@ -1,113 +0,0 @@ -package types_test - -import ( - "testing" - - codectypes "github.com/cosmos/cosmos-sdk/codec/types" - cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" - "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" - cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" - "github.com/cosmos/cosmos-sdk/testutil/testdata" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/stretchr/testify/require" - "github.com/stretchr/testify/suite" - - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" - "github.com/cosmos/ibc-go/v4/modules/core/exported" - "github.com/cosmos/ibc-go/v4/modules/light-clients/06-solomachine/types" - ibctesting "github.com/cosmos/ibc-go/v4/testing" -) - -type SoloMachineTestSuite struct { - suite.Suite - - solomachine *ibctesting.Solomachine // singlesig public key - solomachineMulti *ibctesting.Solomachine // multisig public key - coordinator *ibctesting.Coordinator - - // testing chain used for convenience and readability - chainA *ibctesting.TestChain - chainB *ibctesting.TestChain - - store sdk.KVStore -} - -func (suite *SoloMachineTestSuite) SetupTest() { - suite.coordinator = ibctesting.NewCoordinator(suite.T(), 2) - suite.chainA = suite.coordinator.GetChain(ibctesting.GetChainID(1)) - suite.chainB = suite.coordinator.GetChain(ibctesting.GetChainID(2)) - - suite.solomachine = ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachinesingle", "testing", 1) - suite.solomachineMulti = ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachinemulti", "testing", 4) - - suite.store = suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), exported.Solomachine) -} - -func TestSoloMachineTestSuite(t *testing.T) { - suite.Run(t, new(SoloMachineTestSuite)) -} - -func (suite *SoloMachineTestSuite) GetSequenceFromStore() uint64 { - bz := suite.store.Get(host.ClientStateKey()) - suite.Require().NotNil(bz) - - var clientState exported.ClientState - err := suite.chainA.Codec.UnmarshalInterface(bz, &clientState) - suite.Require().NoError(err) - return clientState.GetLatestHeight().GetRevisionHeight() -} - -func (suite *SoloMachineTestSuite) GetInvalidProof() []byte { - invalidProof, err := suite.chainA.Codec.Marshal(&types.TimestampedSignatureData{Timestamp: suite.solomachine.Time}) - suite.Require().NoError(err) - - return invalidProof -} - -func TestUnpackInterfaces_Header(t *testing.T) { - registry := testdata.NewTestInterfaceRegistry() - cryptocodec.RegisterInterfaces(registry) - - pk := secp256k1.GenPrivKey().PubKey().(cryptotypes.PubKey) - any, err := codectypes.NewAnyWithValue(pk) - require.NoError(t, err) - - header := types.Header{ - NewPublicKey: any, - } - bz, err := header.Marshal() - require.NoError(t, err) - - var header2 types.Header - err = header2.Unmarshal(bz) - require.NoError(t, err) - - err = codectypes.UnpackInterfaces(header2, registry) - require.NoError(t, err) - - require.Equal(t, pk, header2.NewPublicKey.GetCachedValue()) -} - -func TestUnpackInterfaces_HeaderData(t *testing.T) { - registry := testdata.NewTestInterfaceRegistry() - cryptocodec.RegisterInterfaces(registry) - - pk := secp256k1.GenPrivKey().PubKey().(cryptotypes.PubKey) - any, err := codectypes.NewAnyWithValue(pk) - require.NoError(t, err) - - hd := types.HeaderData{ - NewPubKey: any, - } - bz, err := hd.Marshal() - require.NoError(t, err) - - var hd2 types.HeaderData - err = hd2.Unmarshal(bz) - require.NoError(t, err) - - err = codectypes.UnpackInterfaces(hd2, registry) - require.NoError(t, err) - - require.Equal(t, pk, hd2.NewPubKey.GetCachedValue()) -} diff --git a/modules/light-clients/06-solomachine/types/update.go b/modules/light-clients/06-solomachine/types/update.go deleted file mode 100644 index a810548692d..00000000000 --- a/modules/light-clients/06-solomachine/types/update.go +++ /dev/null @@ -1,90 +0,0 @@ -package types - -import ( - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v4/modules/core/exported" -) - -// CheckHeaderAndUpdateState checks if the provided header is valid and updates -// the consensus state if appropriate. It returns an error if: -// - the header provided is not parseable to a solo machine header -// - the header sequence does not match the current sequence -// - the header timestamp is less than the consensus state timestamp -// - the currently registered public key did not provide the update signature -func (cs ClientState) CheckHeaderAndUpdateState( - ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, - header exported.Header, -) (exported.ClientState, exported.ConsensusState, error) { - smHeader, ok := header.(*Header) - if !ok { - return nil, nil, sdkerrors.Wrapf( - clienttypes.ErrInvalidHeader, "header type %T, expected %T", header, &Header{}, - ) - } - - if err := checkHeader(cdc, &cs, smHeader); err != nil { - return nil, nil, err - } - - clientState, consensusState := update(&cs, smHeader) - return clientState, consensusState, nil -} - -// checkHeader checks if the Solo Machine update signature is valid. -func checkHeader(cdc codec.BinaryCodec, clientState *ClientState, header *Header) error { - // assert update sequence is current sequence - if header.Sequence != clientState.Sequence { - return sdkerrors.Wrapf( - clienttypes.ErrInvalidHeader, - "header sequence does not match the client state sequence (%d != %d)", header.Sequence, clientState.Sequence, - ) - } - - // assert update timestamp is not less than current consensus state timestamp - if header.Timestamp < clientState.ConsensusState.Timestamp { - return sdkerrors.Wrapf( - clienttypes.ErrInvalidHeader, - "header timestamp is less than to the consensus state timestamp (%d < %d)", header.Timestamp, clientState.ConsensusState.Timestamp, - ) - } - - // assert currently registered public key signed over the new public key with correct sequence - data, err := HeaderSignBytes(cdc, header) - if err != nil { - return err - } - - sigData, err := UnmarshalSignatureData(cdc, header.Signature) - if err != nil { - return err - } - - publicKey, err := clientState.ConsensusState.GetPubKey() - if err != nil { - return err - } - - if err := VerifySignature(publicKey, data, sigData); err != nil { - return sdkerrors.Wrap(ErrInvalidHeader, err.Error()) - } - - return nil -} - -// update the consensus state to the new public key and an incremented sequence -func update(clientState *ClientState, header *Header) (*ClientState, *ConsensusState) { - consensusState := &ConsensusState{ - PublicKey: header.NewPublicKey, - Diversifier: header.NewDiversifier, - Timestamp: header.Timestamp, - } - - // increment sequence number - clientState.Sequence++ - clientState.ConsensusState = consensusState - return clientState, consensusState -} diff --git a/modules/light-clients/06-solomachine/types/update_test.go b/modules/light-clients/06-solomachine/types/update_test.go deleted file mode 100644 index 459c72d0d76..00000000000 --- a/modules/light-clients/06-solomachine/types/update_test.go +++ /dev/null @@ -1,181 +0,0 @@ -package types_test - -import ( - codectypes "github.com/cosmos/cosmos-sdk/codec/types" - sdk "github.com/cosmos/cosmos-sdk/types" - - "github.com/cosmos/ibc-go/v4/modules/core/exported" - "github.com/cosmos/ibc-go/v4/modules/light-clients/06-solomachine/types" - ibctmtypes "github.com/cosmos/ibc-go/v4/modules/light-clients/07-tendermint/types" - ibctesting "github.com/cosmos/ibc-go/v4/testing" -) - -func (suite *SoloMachineTestSuite) TestCheckHeaderAndUpdateState() { - var ( - clientState exported.ClientState - header exported.Header - ) - - // test singlesig and multisig public keys - for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { - - testCases := []struct { - name string - setup func() - expPass bool - }{ - { - "successful update", - func() { - clientState = solomachine.ClientState() - header = solomachine.CreateHeader() - }, - true, - }, - { - "wrong client state type", - func() { - clientState = &ibctmtypes.ClientState{} - header = solomachine.CreateHeader() - }, - false, - }, - { - "invalid header type", - func() { - clientState = solomachine.ClientState() - header = &ibctmtypes.Header{} - }, - false, - }, - { - "wrong sequence in header", - func() { - clientState = solomachine.ClientState() - // store in temp before assigning to interface type - h := solomachine.CreateHeader() - h.Sequence++ - header = h - }, - false, - }, - { - "invalid header Signature", - func() { - clientState = solomachine.ClientState() - h := solomachine.CreateHeader() - h.Signature = suite.GetInvalidProof() - header = h - }, false, - }, - { - "invalid timestamp in header", - func() { - clientState = solomachine.ClientState() - h := solomachine.CreateHeader() - h.Timestamp-- - header = h - }, false, - }, - { - "signature uses wrong sequence", - func() { - clientState = solomachine.ClientState() - solomachine.Sequence++ - header = solomachine.CreateHeader() - }, - false, - }, - { - "signature uses new pubkey to sign", - func() { - // store in temp before assinging to interface type - cs := solomachine.ClientState() - h := solomachine.CreateHeader() - - publicKey, err := codectypes.NewAnyWithValue(solomachine.PublicKey) - suite.NoError(err) - - data := &types.HeaderData{ - NewPubKey: publicKey, - NewDiversifier: h.NewDiversifier, - } - - dataBz, err := suite.chainA.Codec.Marshal(data) - suite.Require().NoError(err) - - // generate invalid signature - signBytes := &types.SignBytes{ - Sequence: cs.Sequence, - Timestamp: solomachine.Time, - Diversifier: solomachine.Diversifier, - DataType: types.CLIENT, - Data: dataBz, - } - - signBz, err := suite.chainA.Codec.Marshal(signBytes) - suite.Require().NoError(err) - - sig := solomachine.GenerateSignature(signBz) - suite.Require().NoError(err) - h.Signature = sig - - clientState = cs - header = h - }, - false, - }, - { - "signature signs over old pubkey", - func() { - // store in temp before assinging to interface type - cs := solomachine.ClientState() - oldPubKey := solomachine.PublicKey - h := solomachine.CreateHeader() - - // generate invalid signature - data := append(sdk.Uint64ToBigEndian(cs.Sequence), oldPubKey.Bytes()...) - sig := solomachine.GenerateSignature(data) - h.Signature = sig - - clientState = cs - header = h - }, - false, - }, - { - "consensus state public key is nil", - func() { - cs := solomachine.ClientState() - cs.ConsensusState.PublicKey = nil - clientState = cs - header = solomachine.CreateHeader() - }, - false, - }, - } - - for _, tc := range testCases { - tc := tc - - suite.Run(tc.name, func() { - // setup test - tc.setup() - - clientState, consensusState, err := clientState.CheckHeaderAndUpdateState(suite.chainA.GetContext(), suite.chainA.Codec, suite.store, header) - - if tc.expPass { - suite.Require().NoError(err) - suite.Require().Equal(header.(*types.Header).NewPublicKey, clientState.(*types.ClientState).ConsensusState.PublicKey) - suite.Require().Equal(false, clientState.(*types.ClientState).IsFrozen) - suite.Require().Equal(header.(*types.Header).Sequence+1, clientState.(*types.ClientState).Sequence) - suite.Require().Equal(consensusState, clientState.(*types.ClientState).ConsensusState) - } else { - suite.Require().Error(err) - suite.Require().Nil(clientState) - suite.Require().Nil(consensusState) - } - }) - } - } -} diff --git a/modules/light-clients/06-solomachine/update.go b/modules/light-clients/06-solomachine/update.go new file mode 100644 index 00000000000..8d044e0a2e9 --- /dev/null +++ b/modules/light-clients/06-solomachine/update.go @@ -0,0 +1,107 @@ +package solomachine + +import ( + "fmt" + + errorsmod "cosmossdk.io/errors" + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v7/modules/core/exported" +) + +// VerifyClientMessage introspects the provided ClientMessage and checks its validity +// A Solomachine Header is considered valid if the currently registered public key has signed over the new public key with the correct sequence +// A Solomachine Misbehaviour is considered valid if duplicate signatures of the current public key are found on two different messages at a given sequence +func (cs ClientState) VerifyClientMessage(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, clientMsg exported.ClientMessage) error { + switch msg := clientMsg.(type) { + case *Header: + return cs.verifyHeader(ctx, cdc, clientStore, msg) + case *Misbehaviour: + return cs.verifyMisbehaviour(ctx, cdc, clientStore, msg) + default: + return errorsmod.Wrapf(clienttypes.ErrInvalidClientType, "expected type of %T or %T, got type %T", Header{}, Misbehaviour{}, msg) + } +} + +func (cs ClientState) verifyHeader(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, header *Header) error { + // assert update timestamp is not less than current consensus state timestamp + if header.Timestamp < cs.ConsensusState.Timestamp { + return errorsmod.Wrapf( + clienttypes.ErrInvalidHeader, + "header timestamp is less than to the consensus state timestamp (%d < %d)", header.Timestamp, cs.ConsensusState.Timestamp, + ) + } + + // assert currently registered public key signed over the new public key with correct sequence + headerData := &HeaderData{ + NewPubKey: header.NewPublicKey, + NewDiversifier: header.NewDiversifier, + } + + dataBz, err := cdc.Marshal(headerData) + if err != nil { + return err + } + + signBytes := &SignBytes{ + Sequence: cs.Sequence, + Timestamp: header.Timestamp, + Diversifier: cs.ConsensusState.Diversifier, + Path: []byte(SentinelHeaderPath), + Data: dataBz, + } + + data, err := cdc.Marshal(signBytes) + if err != nil { + return err + } + + sigData, err := UnmarshalSignatureData(cdc, header.Signature) + if err != nil { + return err + } + + publicKey, err := cs.ConsensusState.GetPubKey() + if err != nil { + return err + } + + if err := VerifySignature(publicKey, data, sigData); err != nil { + return errorsmod.Wrap(ErrInvalidHeader, err.Error()) + } + + return nil +} + +// UpdateState updates the consensus state to the new public key and an incremented sequence. +// A list containing the updated consensus height is returned. +func (cs ClientState) UpdateState(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, clientMsg exported.ClientMessage) []exported.Height { + smHeader, ok := clientMsg.(*Header) + if !ok { + panic(fmt.Errorf("unsupported ClientMessage: %T", clientMsg)) + } + + // create new solomachine ConsensusState + consensusState := &ConsensusState{ + PublicKey: smHeader.NewPublicKey, + Diversifier: smHeader.NewDiversifier, + Timestamp: smHeader.Timestamp, + } + + cs.Sequence++ + cs.ConsensusState = consensusState + + setClientState(clientStore, cdc, &cs) + + return []exported.Height{clienttypes.NewHeight(0, cs.Sequence)} +} + +// UpdateStateOnMisbehaviour updates state upon misbehaviour. This method should only be called on misbehaviour +// as it does not perform any misbehaviour checks. +func (cs ClientState) UpdateStateOnMisbehaviour(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, _ exported.ClientMessage) { + cs.IsFrozen = true + + setClientState(clientStore, cdc, &cs) +} diff --git a/modules/light-clients/06-solomachine/update_test.go b/modules/light-clients/06-solomachine/update_test.go new file mode 100644 index 00000000000..923237b0d06 --- /dev/null +++ b/modules/light-clients/06-solomachine/update_test.go @@ -0,0 +1,546 @@ +package solomachine_test + +import ( + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + "github.com/cosmos/ibc-go/v7/modules/core/exported" + solomachine "github.com/cosmos/ibc-go/v7/modules/light-clients/06-solomachine" + ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" + ibctesting "github.com/cosmos/ibc-go/v7/testing" +) + +func (suite *SoloMachineTestSuite) TestVerifyClientMessageHeader() { + var ( + clientMsg exported.ClientMessage + clientState *solomachine.ClientState + ) + + // test singlesig and multisig public keys + for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + + testCases := []struct { + name string + setup func() + expPass bool + }{ + { + "successful header", + func() { + clientMsg = sm.CreateHeader(sm.Diversifier) + }, + true, + }, + { + "successful header with new diversifier", + func() { + clientMsg = sm.CreateHeader(sm.Diversifier + "0") + }, + true, + }, + { + "successful misbehaviour", + func() { + clientMsg = sm.CreateMisbehaviour() + }, + true, + }, + { + "invalid client message type", + func() { + clientMsg = &ibctm.Header{} + }, + false, + }, + { + "invalid header Signature", + func() { + h := sm.CreateHeader(sm.Diversifier) + h.Signature = suite.GetInvalidProof() + clientMsg = h + }, false, + }, + { + "invalid timestamp in header", + func() { + h := sm.CreateHeader(sm.Diversifier) + h.Timestamp-- + clientMsg = h + }, false, + }, + { + "signature uses wrong sequence", + func() { + sm.Sequence++ + clientMsg = sm.CreateHeader(sm.Diversifier) + }, + false, + }, + { + "signature uses new pubkey to sign", + func() { + // store in temp before assinging to interface type + cs := sm.ClientState() + h := sm.CreateHeader(sm.Diversifier) + + publicKey, err := codectypes.NewAnyWithValue(sm.PublicKey) + suite.NoError(err) + + data := &solomachine.HeaderData{ + NewPubKey: publicKey, + NewDiversifier: h.NewDiversifier, + } + + dataBz, err := suite.chainA.Codec.Marshal(data) + suite.Require().NoError(err) + + // generate invalid signature + signBytes := &solomachine.SignBytes{ + Sequence: cs.Sequence, + Timestamp: sm.Time, + Diversifier: sm.Diversifier, + Path: []byte("invalid signature data"), + Data: dataBz, + } + + signBz, err := suite.chainA.Codec.Marshal(signBytes) + suite.Require().NoError(err) + + sig := sm.GenerateSignature(signBz) + suite.Require().NoError(err) + h.Signature = sig + + clientState = cs + clientMsg = h + }, + false, + }, + { + "signature signs over old pubkey", + func() { + // store in temp before assinging to interface type + cs := sm.ClientState() + oldPubKey := sm.PublicKey + h := sm.CreateHeader(sm.Diversifier) + + // generate invalid signature + data := append(sdk.Uint64ToBigEndian(cs.Sequence), oldPubKey.Bytes()...) + sig := sm.GenerateSignature(data) + h.Signature = sig + + clientState = cs + clientMsg = h + }, + false, + }, + { + "consensus state public key is nil - header", + func() { + clientState.ConsensusState.PublicKey = nil + clientMsg = sm.CreateHeader(sm.Diversifier) + }, + false, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + clientState = sm.ClientState() + + // setup test + tc.setup() + + err := clientState.VerifyClientMessage(suite.chainA.GetContext(), suite.chainA.Codec, suite.store, clientMsg) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } + } +} + +func (suite *SoloMachineTestSuite) TestVerifyClientMessageMisbehaviour() { + var ( + clientMsg exported.ClientMessage + clientState *solomachine.ClientState + ) + + // test singlesig and multisig public keys + for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + + testCases := []struct { + name string + setup func() + expPass bool + }{ + { + "successful misbehaviour", + func() { + clientMsg = sm.CreateMisbehaviour() + }, + true, + }, + { + "old misbehaviour is successful (timestamp is less than current consensus state)", + func() { + clientState = sm.ClientState() + sm.Time -= 5 + clientMsg = sm.CreateMisbehaviour() + }, true, + }, + { + "invalid client message type", + func() { + clientMsg = &ibctm.Header{} + }, + false, + }, + { + "consensus state pubkey is nil", + func() { + clientState.ConsensusState.PublicKey = nil + clientMsg = sm.CreateMisbehaviour() + }, + false, + }, + { + "invalid SignatureOne SignatureData", + func() { + m := sm.CreateMisbehaviour() + + m.SignatureOne.Signature = suite.GetInvalidProof() + clientMsg = m + }, false, + }, + { + "invalid SignatureTwo SignatureData", + func() { + m := sm.CreateMisbehaviour() + + m.SignatureTwo.Signature = suite.GetInvalidProof() + clientMsg = m + }, false, + }, + { + "invalid SignatureOne timestamp", + func() { + m := sm.CreateMisbehaviour() + + m.SignatureOne.Timestamp = 1000000000000 + clientMsg = m + }, false, + }, + { + "invalid SignatureTwo timestamp", + func() { + m := sm.CreateMisbehaviour() + + m.SignatureTwo.Timestamp = 1000000000000 + clientMsg = m + }, false, + }, + { + "invalid first signature data", + func() { + // store in temp before assigning to interface type + m := sm.CreateMisbehaviour() + + msg := []byte("DATA ONE") + signBytes := &solomachine.SignBytes{ + Sequence: sm.Sequence + 1, + Timestamp: sm.Time, + Diversifier: sm.Diversifier, + Path: []byte("invalid signature data"), + Data: msg, + } + + data, err := suite.chainA.Codec.Marshal(signBytes) + suite.Require().NoError(err) + + sig := sm.GenerateSignature(data) + + m.SignatureOne.Signature = sig + m.SignatureOne.Data = msg + clientMsg = m + }, + false, + }, + { + "invalid second signature data", + func() { + // store in temp before assigning to interface type + m := sm.CreateMisbehaviour() + + msg := []byte("DATA TWO") + signBytes := &solomachine.SignBytes{ + Sequence: sm.Sequence + 1, + Timestamp: sm.Time, + Diversifier: sm.Diversifier, + Path: []byte("invalid signature data"), + Data: msg, + } + + data, err := suite.chainA.Codec.Marshal(signBytes) + suite.Require().NoError(err) + + sig := sm.GenerateSignature(data) + + m.SignatureTwo.Signature = sig + m.SignatureTwo.Data = msg + clientMsg = m + }, + false, + }, + { + "wrong pubkey generates first signature", + func() { + badMisbehaviour := sm.CreateMisbehaviour() + + // update public key to a new one + sm.CreateHeader(sm.Diversifier) + m := sm.CreateMisbehaviour() + + // set SignatureOne to use the wrong signature + m.SignatureOne = badMisbehaviour.SignatureOne + clientMsg = m + }, false, + }, + { + "wrong pubkey generates second signature", + func() { + badMisbehaviour := sm.CreateMisbehaviour() + + // update public key to a new one + sm.CreateHeader(sm.Diversifier) + m := sm.CreateMisbehaviour() + + // set SignatureTwo to use the wrong signature + m.SignatureTwo = badMisbehaviour.SignatureTwo + clientMsg = m + }, false, + }, + { + "signatures sign over different sequence", + func() { + // store in temp before assigning to interface type + m := sm.CreateMisbehaviour() + + // Signature One + msg := []byte("DATA ONE") + // sequence used is plus 1 + signBytes := &solomachine.SignBytes{ + Sequence: sm.Sequence + 1, + Timestamp: sm.Time, + Diversifier: sm.Diversifier, + Path: []byte("invalid signature data"), + Data: msg, + } + + data, err := suite.chainA.Codec.Marshal(signBytes) + suite.Require().NoError(err) + + sig := sm.GenerateSignature(data) + + m.SignatureOne.Signature = sig + m.SignatureOne.Data = msg + + // Signature Two + msg = []byte("DATA TWO") + // sequence used is minus 1 + + signBytes = &solomachine.SignBytes{ + Sequence: sm.Sequence - 1, + Timestamp: sm.Time, + Diversifier: sm.Diversifier, + Path: []byte("invalid signature data"), + Data: msg, + } + data, err = suite.chainA.Codec.Marshal(signBytes) + suite.Require().NoError(err) + + sig = sm.GenerateSignature(data) + + m.SignatureTwo.Signature = sig + m.SignatureTwo.Data = msg + + clientMsg = m + }, + false, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + clientState = sm.ClientState() + + // setup test + tc.setup() + + err := clientState.VerifyClientMessage(suite.chainA.GetContext(), suite.chainA.Codec, suite.store, clientMsg) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } + } +} + +func (suite *SoloMachineTestSuite) TestUpdateState() { + var ( + clientState exported.ClientState + clientMsg exported.ClientMessage + ) + + // test singlesig and multisig public keys + for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + + testCases := []struct { + name string + setup func() + expPass bool + }{ + { + "successful update", + func() { + clientState = sm.ClientState() + clientMsg = sm.CreateHeader(sm.Diversifier) + }, + true, + }, + { + "invalid type misbehaviour", + func() { + clientState = sm.ClientState() + clientMsg = sm.CreateMisbehaviour() + }, + false, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + tc.setup() // setup test + + if tc.expPass { + consensusHeights := clientState.UpdateState(suite.chainA.GetContext(), suite.chainA.Codec, suite.store, clientMsg) + + clientStateBz := suite.store.Get(host.ClientStateKey()) + suite.Require().NotEmpty(clientStateBz) + + newClientState := clienttypes.MustUnmarshalClientState(suite.chainA.Codec, clientStateBz) + + suite.Require().Len(consensusHeights, 1) + suite.Require().Equal(uint64(0), consensusHeights[0].GetRevisionNumber()) + suite.Require().Equal(newClientState.(*solomachine.ClientState).Sequence, consensusHeights[0].GetRevisionHeight()) + + suite.Require().False(newClientState.(*solomachine.ClientState).IsFrozen) + suite.Require().Equal(clientMsg.(*solomachine.Header).NewPublicKey, newClientState.(*solomachine.ClientState).ConsensusState.PublicKey) + suite.Require().Equal(clientMsg.(*solomachine.Header).NewDiversifier, newClientState.(*solomachine.ClientState).ConsensusState.Diversifier) + suite.Require().Equal(clientMsg.(*solomachine.Header).Timestamp, newClientState.(*solomachine.ClientState).ConsensusState.Timestamp) + } else { + suite.Require().Panics(func() { + clientState.UpdateState(suite.chainA.GetContext(), suite.chainA.Codec, suite.store, clientMsg) + }) + } + }) + } + } +} + +func (suite *SoloMachineTestSuite) TestCheckForMisbehaviour() { + var clientMsg exported.ClientMessage + + // test singlesig and multisig public keys + for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + testCases := []struct { + name string + malleate func() + expPass bool + }{ + { + "success", + func() { + clientMsg = sm.CreateMisbehaviour() + }, + true, + }, + { + "normal header returns false", + func() { + clientMsg = sm.CreateHeader(sm.Diversifier) + }, + false, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + clientState := sm.ClientState() + + tc.malleate() + + foundMisbehaviour := clientState.CheckForMisbehaviour(suite.chainA.GetContext(), suite.chainA.Codec, suite.store, clientMsg) + + if tc.expPass { + suite.Require().True(foundMisbehaviour) + } else { + suite.Require().False(foundMisbehaviour) + } + }) + } + } +} + +func (suite *SoloMachineTestSuite) TestUpdateStateOnMisbehaviour() { + // test singlesig and multisig public keys + for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + testCases := []struct { + name string + malleate func() + expPass bool + }{ + { + "success", + func() {}, + true, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + clientState := sm.ClientState() + + tc.malleate() + + clientState.UpdateStateOnMisbehaviour(suite.chainA.GetContext(), suite.chainA.Codec, suite.store, nil) + + if tc.expPass { + clientStateBz := suite.store.Get(host.ClientStateKey()) + suite.Require().NotEmpty(clientStateBz) + + newClientState := clienttypes.MustUnmarshalClientState(suite.chainA.Codec, clientStateBz) + + suite.Require().True(newClientState.(*solomachine.ClientState).IsFrozen) + } + }) + } + } +} diff --git a/modules/light-clients/07-tendermint/client_state.go b/modules/light-clients/07-tendermint/client_state.go new file mode 100644 index 00000000000..53daf119393 --- /dev/null +++ b/modules/light-clients/07-tendermint/client_state.go @@ -0,0 +1,340 @@ +package tendermint + +import ( + "strings" + "time" + + errorsmod "cosmossdk.io/errors" + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + ics23 "github.com/cosmos/ics23/go" + "github.com/tendermint/tendermint/light" + tmtypes "github.com/tendermint/tendermint/types" + + ibcerrors "github.com/cosmos/ibc-go/v7/internal/errors" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + commitmenttypes "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/types" + "github.com/cosmos/ibc-go/v7/modules/core/exported" +) + +var _ exported.ClientState = (*ClientState)(nil) + +// NewClientState creates a new ClientState instance +func NewClientState( + chainID string, trustLevel Fraction, + trustingPeriod, ubdPeriod, maxClockDrift time.Duration, + latestHeight clienttypes.Height, specs []*ics23.ProofSpec, + upgradePath []string, +) *ClientState { + return &ClientState{ + ChainId: chainID, + TrustLevel: trustLevel, + TrustingPeriod: trustingPeriod, + UnbondingPeriod: ubdPeriod, + MaxClockDrift: maxClockDrift, + LatestHeight: latestHeight, + FrozenHeight: clienttypes.ZeroHeight(), + ProofSpecs: specs, + UpgradePath: upgradePath, + } +} + +// GetChainID returns the chain-id +func (cs ClientState) GetChainID() string { + return cs.ChainId +} + +// ClientType is tendermint. +func (cs ClientState) ClientType() string { + return exported.Tendermint +} + +// GetLatestHeight returns latest block height. +func (cs ClientState) GetLatestHeight() exported.Height { + return cs.LatestHeight +} + +// GetTimestampAtHeight returns the timestamp in nanoseconds of the consensus state at the given height. +func (cs ClientState) GetTimestampAtHeight( + ctx sdk.Context, + clientStore sdk.KVStore, + cdc codec.BinaryCodec, + height exported.Height, +) (uint64, error) { + // get consensus state at height from clientStore to check for expiry + consState, found := GetConsensusState(clientStore, cdc, height) + if !found { + return 0, errorsmod.Wrapf(clienttypes.ErrConsensusStateNotFound, "height (%s)", height) + } + return consState.GetTimestamp(), nil +} + +// Status returns the status of the tendermint client. +// The client may be: +// - Active: FrozenHeight is zero and client is not expired +// - Frozen: Frozen Height is not zero +// - Expired: the latest consensus state timestamp + trusting period <= current time +// +// A frozen client will become expired, so the Frozen status +// has higher precedence. +func (cs ClientState) Status( + ctx sdk.Context, + clientStore sdk.KVStore, + cdc codec.BinaryCodec, +) exported.Status { + if !cs.FrozenHeight.IsZero() { + return exported.Frozen + } + + // get latest consensus state from clientStore to check for expiry + consState, found := GetConsensusState(clientStore, cdc, cs.GetLatestHeight()) + if !found { + // if the client state does not have an associated consensus state for its latest height + // then it must be expired + return exported.Expired + } + + if cs.IsExpired(consState.Timestamp, ctx.BlockTime()) { + return exported.Expired + } + + return exported.Active +} + +// IsExpired returns whether or not the client has passed the trusting period since the last +// update (in which case no headers are considered valid). +func (cs ClientState) IsExpired(latestTimestamp, now time.Time) bool { + expirationTime := latestTimestamp.Add(cs.TrustingPeriod) + return !expirationTime.After(now) +} + +// Validate performs a basic validation of the client state fields. +func (cs ClientState) Validate() error { + if strings.TrimSpace(cs.ChainId) == "" { + return errorsmod.Wrap(ErrInvalidChainID, "chain id cannot be empty string") + } + + // NOTE: the value of tmtypes.MaxChainIDLen may change in the future. + // If this occurs, the code here must account for potential difference + // between the tendermint version being run by the counterparty chain + // and the tendermint version used by this light client. + // https://github.com/cosmos/ibc-go/issues/177 + if len(cs.ChainId) > tmtypes.MaxChainIDLen { + return errorsmod.Wrapf(ErrInvalidChainID, "chainID is too long; got: %d, max: %d", len(cs.ChainId), tmtypes.MaxChainIDLen) + } + + if err := light.ValidateTrustLevel(cs.TrustLevel.ToTendermint()); err != nil { + return err + } + if cs.TrustingPeriod <= 0 { + return errorsmod.Wrap(ErrInvalidTrustingPeriod, "trusting period must be greater than zero") + } + if cs.UnbondingPeriod <= 0 { + return errorsmod.Wrap(ErrInvalidUnbondingPeriod, "unbonding period must be greater than zero") + } + if cs.MaxClockDrift <= 0 { + return errorsmod.Wrap(ErrInvalidMaxClockDrift, "max clock drift must be greater than zero") + } + + // the latest height revision number must match the chain id revision number + if cs.LatestHeight.RevisionNumber != clienttypes.ParseChainID(cs.ChainId) { + return errorsmod.Wrapf(ErrInvalidHeaderHeight, + "latest height revision number must match chain id revision number (%d != %d)", cs.LatestHeight.RevisionNumber, clienttypes.ParseChainID(cs.ChainId)) + } + if cs.LatestHeight.RevisionHeight == 0 { + return errorsmod.Wrapf(ErrInvalidHeaderHeight, "tendermint client's latest height revision height cannot be zero") + } + if cs.TrustingPeriod >= cs.UnbondingPeriod { + return errorsmod.Wrapf( + ErrInvalidTrustingPeriod, + "trusting period (%s) should be < unbonding period (%s)", cs.TrustingPeriod, cs.UnbondingPeriod, + ) + } + + if cs.ProofSpecs == nil { + return errorsmod.Wrap(ErrInvalidProofSpecs, "proof specs cannot be nil for tm client") + } + for i, spec := range cs.ProofSpecs { + if spec == nil { + return errorsmod.Wrapf(ErrInvalidProofSpecs, "proof spec cannot be nil at index: %d", i) + } + } + // UpgradePath may be empty, but if it isn't, each key must be non-empty + for i, k := range cs.UpgradePath { + if strings.TrimSpace(k) == "" { + return errorsmod.Wrapf(clienttypes.ErrInvalidClient, "key in upgrade path at index %d cannot be empty", i) + } + } + + return nil +} + +// GetProofSpecs returns the format the client expects for proof verification +// as a string array specifying the proof type for each position in chained proof +func (cs ClientState) GetProofSpecs() []*ics23.ProofSpec { + return cs.ProofSpecs +} + +// ZeroCustomFields returns a ClientState that is a copy of the current ClientState +// with all client customizable fields zeroed out +func (cs ClientState) ZeroCustomFields() exported.ClientState { + // copy over all chain-specified fields + // and leave custom fields empty + return &ClientState{ + ChainId: cs.ChainId, + UnbondingPeriod: cs.UnbondingPeriod, + LatestHeight: cs.LatestHeight, + ProofSpecs: cs.ProofSpecs, + UpgradePath: cs.UpgradePath, + } +} + +// Initialize checks that the initial consensus state is an 07-tendermint consensus state and +// sets the client state, consensus state and associated metadata in the provided client store. +func (cs ClientState) Initialize(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, consState exported.ConsensusState) error { + consensusState, ok := consState.(*ConsensusState) + if !ok { + return errorsmod.Wrapf(clienttypes.ErrInvalidConsensus, "invalid initial consensus state. expected type: %T, got: %T", + &ConsensusState{}, consState) + } + + setClientState(clientStore, cdc, &cs) + setConsensusState(clientStore, cdc, consensusState, cs.GetLatestHeight()) + setConsensusMetadata(ctx, clientStore, cs.GetLatestHeight()) + + return nil +} + +// VerifyMembership is a generic proof verification method which verifies a proof of the existence of a value at a given CommitmentPath at the specified height. +// The caller is expected to construct the full CommitmentPath from a CommitmentPrefix and a standardized path (as defined in ICS 24). +// If a zero proof height is passed in, it will fail to retrieve the associated consensus state. +func (cs ClientState) VerifyMembership( + ctx sdk.Context, + clientStore sdk.KVStore, + cdc codec.BinaryCodec, + height exported.Height, + delayTimePeriod uint64, + delayBlockPeriod uint64, + proof []byte, + path exported.Path, + value []byte, +) error { + if cs.GetLatestHeight().LT(height) { + return errorsmod.Wrapf( + ibcerrors.ErrInvalidHeight, + "client state height < proof height (%d < %d), please ensure the client has been updated", cs.GetLatestHeight(), height, + ) + } + + if err := verifyDelayPeriodPassed(ctx, clientStore, height, delayTimePeriod, delayBlockPeriod); err != nil { + return err + } + + var merkleProof commitmenttypes.MerkleProof + if err := cdc.Unmarshal(proof, &merkleProof); err != nil { + return errorsmod.Wrap(commitmenttypes.ErrInvalidProof, "failed to unmarshal proof into ICS 23 commitment merkle proof") + } + + merklePath, ok := path.(commitmenttypes.MerklePath) + if !ok { + return errorsmod.Wrapf(ibcerrors.ErrInvalidType, "expected %T, got %T", commitmenttypes.MerklePath{}, path) + } + + consensusState, found := GetConsensusState(clientStore, cdc, height) + if !found { + return errorsmod.Wrap(clienttypes.ErrConsensusStateNotFound, "please ensure the proof was constructed against a height that exists on the client") + } + + if err := merkleProof.VerifyMembership(cs.ProofSpecs, consensusState.GetRoot(), merklePath, value); err != nil { + return err + } + + return nil +} + +// VerifyNonMembership is a generic proof verification method which verifies the absence of a given CommitmentPath at a specified height. +// The caller is expected to construct the full CommitmentPath from a CommitmentPrefix and a standardized path (as defined in ICS 24). +// If a zero proof height is passed in, it will fail to retrieve the associated consensus state. +func (cs ClientState) VerifyNonMembership( + ctx sdk.Context, + clientStore sdk.KVStore, + cdc codec.BinaryCodec, + height exported.Height, + delayTimePeriod uint64, + delayBlockPeriod uint64, + proof []byte, + path exported.Path, +) error { + if cs.GetLatestHeight().LT(height) { + return errorsmod.Wrapf( + ibcerrors.ErrInvalidHeight, + "client state height < proof height (%d < %d), please ensure the client has been updated", cs.GetLatestHeight(), height, + ) + } + + if err := verifyDelayPeriodPassed(ctx, clientStore, height, delayTimePeriod, delayBlockPeriod); err != nil { + return err + } + + var merkleProof commitmenttypes.MerkleProof + if err := cdc.Unmarshal(proof, &merkleProof); err != nil { + return errorsmod.Wrap(commitmenttypes.ErrInvalidProof, "failed to unmarshal proof into ICS 23 commitment merkle proof") + } + + merklePath, ok := path.(commitmenttypes.MerklePath) + if !ok { + return errorsmod.Wrapf(ibcerrors.ErrInvalidType, "expected %T, got %T", commitmenttypes.MerklePath{}, path) + } + + consensusState, found := GetConsensusState(clientStore, cdc, height) + if !found { + return errorsmod.Wrap(clienttypes.ErrConsensusStateNotFound, "please ensure the proof was constructed against a height that exists on the client") + } + + if err := merkleProof.VerifyNonMembership(cs.ProofSpecs, consensusState.GetRoot(), merklePath); err != nil { + return err + } + + return nil +} + +// verifyDelayPeriodPassed will ensure that at least delayTimePeriod amount of time and delayBlockPeriod number of blocks have passed +// since consensus state was submitted before allowing verification to continue. +func verifyDelayPeriodPassed(ctx sdk.Context, store sdk.KVStore, proofHeight exported.Height, delayTimePeriod, delayBlockPeriod uint64) error { + if delayTimePeriod != 0 { + // check that executing chain's timestamp has passed consensusState's processed time + delay time period + processedTime, ok := GetProcessedTime(store, proofHeight) + if !ok { + return errorsmod.Wrapf(ErrProcessedTimeNotFound, "processed time not found for height: %s", proofHeight) + } + + currentTimestamp := uint64(ctx.BlockTime().UnixNano()) + validTime := processedTime + delayTimePeriod + + // NOTE: delay time period is inclusive, so if currentTimestamp is validTime, then we return no error + if currentTimestamp < validTime { + return errorsmod.Wrapf(ErrDelayPeriodNotPassed, "cannot verify packet until time: %d, current time: %d", + validTime, currentTimestamp) + } + + } + + if delayBlockPeriod != 0 { + // check that executing chain's height has passed consensusState's processed height + delay block period + processedHeight, ok := GetProcessedHeight(store, proofHeight) + if !ok { + return errorsmod.Wrapf(ErrProcessedHeightNotFound, "processed height not found for height: %s", proofHeight) + } + + currentHeight := clienttypes.GetSelfHeight(ctx) + validHeight := clienttypes.NewHeight(processedHeight.GetRevisionNumber(), processedHeight.GetRevisionHeight()+delayBlockPeriod) + + // NOTE: delay block period is inclusive, so if currentHeight is validHeight, then we return no error + if currentHeight.LT(validHeight) { + return errorsmod.Wrapf(ErrDelayPeriodNotPassed, "cannot verify packet until height: %s, current height: %s", + validHeight, currentHeight) + } + } + + return nil +} diff --git a/modules/light-clients/07-tendermint/client_state_test.go b/modules/light-clients/07-tendermint/client_state_test.go new file mode 100644 index 00000000000..2a70d4199a6 --- /dev/null +++ b/modules/light-clients/07-tendermint/client_state_test.go @@ -0,0 +1,678 @@ +package tendermint_test + +import ( + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" + ics23 "github.com/cosmos/ics23/go" + + transfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + commitmenttypes "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + "github.com/cosmos/ibc-go/v7/modules/core/exported" + ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" + ibctesting "github.com/cosmos/ibc-go/v7/testing" + ibcmock "github.com/cosmos/ibc-go/v7/testing/mock" +) + +const ( + // Do not change the length of these variables + fiftyCharChainID = "12345678901234567890123456789012345678901234567890" + fiftyOneCharChainID = "123456789012345678901234567890123456789012345678901" +) + +var invalidProof = []byte("invalid proof") + +func (suite *TendermintTestSuite) TestStatus() { + var ( + path *ibctesting.Path + clientState *ibctm.ClientState + ) + + testCases := []struct { + name string + malleate func() + expStatus exported.Status + }{ + {"client is active", func() {}, exported.Active}, + {"client is frozen", func() { + clientState.FrozenHeight = clienttypes.NewHeight(0, 1) + path.EndpointA.SetClientState(clientState) + }, exported.Frozen}, + {"client status without consensus state", func() { + clientState.LatestHeight = clientState.LatestHeight.Increment().(clienttypes.Height) + path.EndpointA.SetClientState(clientState) + }, exported.Expired}, + {"client status is expired", func() { + suite.coordinator.IncrementTimeBy(clientState.TrustingPeriod) + }, exported.Expired}, + } + + for _, tc := range testCases { + path = ibctesting.NewPath(suite.chainA, suite.chainB) + suite.coordinator.SetupClients(path) + + clientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) + clientState = path.EndpointA.GetClientState().(*ibctm.ClientState) + + tc.malleate() + + status := clientState.Status(suite.chainA.GetContext(), clientStore, suite.chainA.App.AppCodec()) + suite.Require().Equal(tc.expStatus, status) + + } +} + +func (suite *TendermintTestSuite) TestValidate() { + testCases := []struct { + name string + clientState *ibctm.ClientState + expPass bool + }{ + { + name: "valid client", + clientState: ibctm.NewClientState(chainID, ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath), + expPass: true, + }, + { + name: "valid client with nil upgrade path", + clientState: ibctm.NewClientState(chainID, ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), nil), + expPass: true, + }, + { + name: "invalid chainID", + clientState: ibctm.NewClientState(" ", ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath), + expPass: false, + }, + { + // NOTE: if this test fails, the code must account for the change in chainID length across tendermint versions! + // Do not only fix the test, fix the code! + // https://github.com/cosmos/ibc-go/issues/177 + name: "valid chainID - chainID validation failed for chainID of length 50! ", + clientState: ibctm.NewClientState(fiftyCharChainID, ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath), + expPass: true, + }, + { + // NOTE: if this test fails, the code must account for the change in chainID length across tendermint versions! + // Do not only fix the test, fix the code! + // https://github.com/cosmos/ibc-go/issues/177 + name: "invalid chainID - chainID validation did not fail for chainID of length 51! ", + clientState: ibctm.NewClientState(fiftyOneCharChainID, ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath), + expPass: false, + }, + { + name: "invalid trust level", + clientState: ibctm.NewClientState(chainID, ibctm.Fraction{Numerator: 0, Denominator: 1}, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath), + expPass: false, + }, + { + name: "invalid zero trusting period", + clientState: ibctm.NewClientState(chainID, ibctm.DefaultTrustLevel, 0, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath), + expPass: false, + }, + { + name: "invalid negative trusting period", + clientState: ibctm.NewClientState(chainID, ibctm.DefaultTrustLevel, -1, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath), + expPass: false, + }, + { + name: "invalid zero unbonding period", + clientState: ibctm.NewClientState(chainID, ibctm.DefaultTrustLevel, trustingPeriod, 0, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath), + expPass: false, + }, + { + name: "invalid negative unbonding period", + clientState: ibctm.NewClientState(chainID, ibctm.DefaultTrustLevel, trustingPeriod, -1, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath), + expPass: false, + }, + { + name: "invalid zero max clock drift", + clientState: ibctm.NewClientState(chainID, ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod, 0, height, commitmenttypes.GetSDKSpecs(), upgradePath), + expPass: false, + }, + { + name: "invalid negative max clock drift", + clientState: ibctm.NewClientState(chainID, ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod, -1, height, commitmenttypes.GetSDKSpecs(), upgradePath), + expPass: false, + }, + { + name: "invalid revision number", + clientState: ibctm.NewClientState(chainID, ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, clienttypes.NewHeight(1, 1), commitmenttypes.GetSDKSpecs(), upgradePath), + expPass: false, + }, + { + name: "invalid revision height", + clientState: ibctm.NewClientState(chainID, ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, clienttypes.ZeroHeight(), commitmenttypes.GetSDKSpecs(), upgradePath), + expPass: false, + }, + { + name: "trusting period not less than unbonding period", + clientState: ibctm.NewClientState(chainID, ibctm.DefaultTrustLevel, ubdPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath), + expPass: false, + }, + { + name: "proof specs is nil", + clientState: ibctm.NewClientState(chainID, ibctm.DefaultTrustLevel, ubdPeriod, ubdPeriod, maxClockDrift, height, nil, upgradePath), + expPass: false, + }, + { + name: "proof specs contains nil", + clientState: ibctm.NewClientState(chainID, ibctm.DefaultTrustLevel, ubdPeriod, ubdPeriod, maxClockDrift, height, []*ics23.ProofSpec{ics23.TendermintSpec, nil}, upgradePath), + expPass: false, + }, + } + + for _, tc := range testCases { + err := tc.clientState.Validate() + if tc.expPass { + suite.Require().NoError(err, tc.name) + } else { + suite.Require().Error(err, tc.name) + } + } +} + +func (suite *TendermintTestSuite) TestInitialize() { + testCases := []struct { + name string + consensusState exported.ConsensusState + expPass bool + }{ + { + name: "valid consensus", + consensusState: &ibctm.ConsensusState{}, + expPass: true, + }, + { + name: "invalid consensus: consensus state is solomachine consensus", + consensusState: ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachine", "", 2).ConsensusState(), + expPass: false, + }, + } + + for _, tc := range testCases { + suite.SetupTest() + path := ibctesting.NewPath(suite.chainA, suite.chainB) + + tmConfig, ok := path.EndpointB.ClientConfig.(*ibctesting.TendermintConfig) + suite.Require().True(ok) + + clientState := ibctm.NewClientState( + path.EndpointB.Chain.ChainID, + tmConfig.TrustLevel, tmConfig.TrustingPeriod, tmConfig.UnbondingPeriod, tmConfig.MaxClockDrift, + suite.chainB.LastHeader.GetTrustedHeight(), commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, + ) + + store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) + err := clientState.Initialize(suite.chainA.GetContext(), suite.chainA.Codec, store, tc.consensusState) + + if tc.expPass { + suite.Require().NoError(err, "valid case returned an error") + suite.Require().True(store.Has(host.ClientStateKey())) + suite.Require().True(store.Has(host.ConsensusStateKey(suite.chainB.LastHeader.GetTrustedHeight()))) + } else { + suite.Require().Error(err, "invalid case didn't return an error") + suite.Require().False(store.Has(host.ClientStateKey())) + suite.Require().False(store.Has(host.ConsensusStateKey(suite.chainB.LastHeader.GetTrustedHeight()))) + } + } +} + +func (suite *TendermintTestSuite) TestVerifyMembership() { + var ( + testingpath *ibctesting.Path + delayTimePeriod uint64 + delayBlockPeriod uint64 + err error + proofHeight exported.Height + proof []byte + path exported.Path + value []byte + ) + + testCases := []struct { + name string + malleate func() + expPass bool + }{ + { + "successful ClientState verification", + func() { + // default proof construction uses ClientState + }, + true, + }, + { + "successful ConsensusState verification", func() { + key := host.FullConsensusStateKey(testingpath.EndpointB.ClientID, testingpath.EndpointB.GetClientState().GetLatestHeight()) + merklePath := commitmenttypes.NewMerklePath(string(key)) + path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) + suite.Require().NoError(err) + + proof, proofHeight = suite.chainB.QueryProof(key) + + consensusState := testingpath.EndpointB.GetConsensusState(testingpath.EndpointB.GetClientState().GetLatestHeight()).(*ibctm.ConsensusState) + value, err = suite.chainB.Codec.MarshalInterface(consensusState) + suite.Require().NoError(err) + }, + true, + }, + { + "successful Connection verification", func() { + key := host.ConnectionKey(testingpath.EndpointB.ConnectionID) + merklePath := commitmenttypes.NewMerklePath(string(key)) + path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) + suite.Require().NoError(err) + + proof, proofHeight = suite.chainB.QueryProof(key) + + connection := testingpath.EndpointB.GetConnection() + value, err = suite.chainB.Codec.Marshal(&connection) + suite.Require().NoError(err) + }, + true, + }, + { + "successful Channel verification", func() { + key := host.ChannelKey(testingpath.EndpointB.ChannelConfig.PortID, testingpath.EndpointB.ChannelID) + merklePath := commitmenttypes.NewMerklePath(string(key)) + path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) + suite.Require().NoError(err) + + proof, proofHeight = suite.chainB.QueryProof(key) + + channel := testingpath.EndpointB.GetChannel() + value, err = suite.chainB.Codec.Marshal(&channel) + suite.Require().NoError(err) + }, + true, + }, + { + "successful PacketCommitment verification", func() { + // send from chainB to chainA since we are proving chainB sent a packet + sequence, err := testingpath.EndpointB.SendPacket(clienttypes.NewHeight(1, 100), 0, ibctesting.MockPacketData) + suite.Require().NoError(err) + + // make packet commitment proof + packet := channeltypes.NewPacket(ibctesting.MockPacketData, sequence, testingpath.EndpointB.ChannelConfig.PortID, testingpath.EndpointB.ChannelID, testingpath.EndpointA.ChannelConfig.PortID, testingpath.EndpointA.ChannelID, clienttypes.NewHeight(1, 100), 0) + key := host.PacketCommitmentKey(packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) + merklePath := commitmenttypes.NewMerklePath(string(key)) + path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) + suite.Require().NoError(err) + + proof, proofHeight = testingpath.EndpointB.QueryProof(key) + + value = channeltypes.CommitPacket(suite.chainA.App.GetIBCKeeper().Codec(), packet) + }, true, + }, + { + "successful Acknowledgement verification", func() { + // send from chainA to chainB since we are proving chainB wrote an acknowledgement + sequence, err := testingpath.EndpointA.SendPacket(clienttypes.NewHeight(1, 100), 0, ibctesting.MockPacketData) + suite.Require().NoError(err) + + // write receipt and ack + packet := channeltypes.NewPacket(ibctesting.MockPacketData, sequence, testingpath.EndpointA.ChannelConfig.PortID, testingpath.EndpointA.ChannelID, testingpath.EndpointB.ChannelConfig.PortID, testingpath.EndpointB.ChannelID, clienttypes.NewHeight(1, 100), 0) + err = testingpath.EndpointB.RecvPacket(packet) + suite.Require().NoError(err) + + key := host.PacketAcknowledgementKey(packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) + merklePath := commitmenttypes.NewMerklePath(string(key)) + path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) + suite.Require().NoError(err) + + proof, proofHeight = testingpath.EndpointB.QueryProof(key) + + value = channeltypes.CommitAcknowledgement(ibcmock.MockAcknowledgement.Acknowledgement()) + }, + true, + }, + { + "successful NextSequenceRecv verification", func() { + // send from chainA to chainB since we are proving chainB incremented the sequence recv + + // send packet + sequence, err := testingpath.EndpointA.SendPacket(clienttypes.NewHeight(1, 100), 0, ibctesting.MockPacketData) + suite.Require().NoError(err) + + // next seq recv incremented + packet := channeltypes.NewPacket(ibctesting.MockPacketData, sequence, testingpath.EndpointA.ChannelConfig.PortID, testingpath.EndpointA.ChannelID, testingpath.EndpointB.ChannelConfig.PortID, testingpath.EndpointB.ChannelID, clienttypes.NewHeight(1, 100), 0) + err = testingpath.EndpointB.RecvPacket(packet) + suite.Require().NoError(err) + + key := host.NextSequenceRecvKey(packet.GetSourcePort(), packet.GetSourceChannel()) + merklePath := commitmenttypes.NewMerklePath(string(key)) + path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) + suite.Require().NoError(err) + + proof, proofHeight = testingpath.EndpointB.QueryProof(key) + + value = sdk.Uint64ToBigEndian(packet.GetSequence() + 1) + }, + true, + }, + { + "successful verification outside IBC store", func() { + key := transfertypes.PortKey + merklePath := commitmenttypes.NewMerklePath(string(key)) + path, err = commitmenttypes.ApplyPrefix(commitmenttypes.NewMerklePrefix([]byte(transfertypes.StoreKey)), merklePath) + suite.Require().NoError(err) + + clientState := testingpath.EndpointA.GetClientState() + proof, proofHeight = suite.chainB.QueryProofForStore(transfertypes.StoreKey, key, int64(clientState.GetLatestHeight().GetRevisionHeight())) + + value = []byte(suite.chainB.GetSimApp().TransferKeeper.GetPort(suite.chainB.GetContext())) + suite.Require().NoError(err) + }, + true, + }, + { + "delay time period has passed", func() { + delayTimePeriod = uint64(time.Second.Nanoseconds()) + }, + true, + }, + { + "delay time period has not passed", func() { + delayTimePeriod = uint64(time.Hour.Nanoseconds()) + }, + false, + }, + { + "delay block period has passed", func() { + delayBlockPeriod = 1 + }, + true, + }, + { + "delay block period has not passed", func() { + delayBlockPeriod = 1000 + }, + false, + }, + { + "latest client height < height", func() { + proofHeight = testingpath.EndpointA.GetClientState().GetLatestHeight().Increment() + }, false, + }, + { + "invalid path type", + func() { + path = ibcmock.KeyPath{} + }, + false, + }, + { + "failed to unmarshal merkle proof", func() { + proof = invalidProof + }, false, + }, + { + "consensus state not found", func() { + proofHeight = clienttypes.ZeroHeight() + }, false, + }, + { + "proof verification failed", func() { + // change the value being proved + value = []byte("invalid value") + }, false, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + suite.SetupTest() // reset + testingpath = ibctesting.NewPath(suite.chainA, suite.chainB) + testingpath.SetChannelOrdered() + suite.coordinator.Setup(testingpath) + + // reset time and block delays to 0, malleate may change to a specific non-zero value. + delayTimePeriod = 0 + delayBlockPeriod = 0 + + // create default proof, merklePath, and value which passes + // may be overwritten by malleate() + key := host.FullClientStateKey(testingpath.EndpointB.ClientID) + merklePath := commitmenttypes.NewMerklePath(string(key)) + path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) + suite.Require().NoError(err) + + proof, proofHeight = suite.chainB.QueryProof(key) + + clientState := testingpath.EndpointB.GetClientState().(*ibctm.ClientState) + value, err = suite.chainB.Codec.MarshalInterface(clientState) + suite.Require().NoError(err) + + tc.malleate() // make changes as necessary + + clientState = testingpath.EndpointA.GetClientState().(*ibctm.ClientState) + + ctx := suite.chainA.GetContext() + store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(ctx, testingpath.EndpointA.ClientID) + + err = clientState.VerifyMembership( + ctx, store, suite.chainA.Codec, proofHeight, delayTimePeriod, delayBlockPeriod, + proof, path, value, + ) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *TendermintTestSuite) TestVerifyNonMembership() { + var ( + testingpath *ibctesting.Path + delayTimePeriod uint64 + delayBlockPeriod uint64 + err error + proofHeight exported.Height + path exported.Path + proof []byte + invalidClientID = "09-tendermint" + invalidConnectionID = "connection-100" + invalidChannelID = "channel-800" + invalidPortID = "invalid-port" + ) + + testCases := []struct { + name string + malleate func() + expPass bool + }{ + { + "successful ClientState verification of non membership", + func() { + // default proof construction uses ClientState + }, + true, + }, + { + "successful ConsensusState verification of non membership", func() { + key := host.FullConsensusStateKey(invalidClientID, testingpath.EndpointB.GetClientState().GetLatestHeight()) + merklePath := commitmenttypes.NewMerklePath(string(key)) + path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) + suite.Require().NoError(err) + + proof, proofHeight = suite.chainB.QueryProof(key) + }, + true, + }, + { + "successful Connection verification of non membership", func() { + key := host.ConnectionKey(invalidConnectionID) + merklePath := commitmenttypes.NewMerklePath(string(key)) + path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) + suite.Require().NoError(err) + + proof, proofHeight = suite.chainB.QueryProof(key) + }, + true, + }, + { + "successful Channel verification of non membership", func() { + key := host.ChannelKey(testingpath.EndpointB.ChannelConfig.PortID, invalidChannelID) + merklePath := commitmenttypes.NewMerklePath(string(key)) + path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) + suite.Require().NoError(err) + + proof, proofHeight = suite.chainB.QueryProof(key) + }, + true, + }, + { + "successful PacketCommitment verification of non membership", func() { + // make packet commitment proof + key := host.PacketCommitmentKey(invalidPortID, invalidChannelID, 1) + merklePath := commitmenttypes.NewMerklePath(string(key)) + path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) + suite.Require().NoError(err) + + proof, proofHeight = testingpath.EndpointB.QueryProof(key) + }, true, + }, + { + "successful Acknowledgement verification of non membership", func() { + key := host.PacketAcknowledgementKey(invalidPortID, invalidChannelID, 1) + merklePath := commitmenttypes.NewMerklePath(string(key)) + path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) + suite.Require().NoError(err) + + proof, proofHeight = testingpath.EndpointB.QueryProof(key) + }, + true, + }, + { + "successful NextSequenceRecv verification of non membership", func() { + key := host.NextSequenceRecvKey(invalidPortID, invalidChannelID) + merklePath := commitmenttypes.NewMerklePath(string(key)) + path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) + suite.Require().NoError(err) + + proof, proofHeight = testingpath.EndpointB.QueryProof(key) + }, + true, + }, + { + "successful verification of non membership outside IBC store", func() { + key := []byte{0x08} + merklePath := commitmenttypes.NewMerklePath(string(key)) + path, err = commitmenttypes.ApplyPrefix(commitmenttypes.NewMerklePrefix([]byte(transfertypes.StoreKey)), merklePath) + suite.Require().NoError(err) + + clientState := testingpath.EndpointA.GetClientState() + proof, proofHeight = suite.chainB.QueryProofForStore(transfertypes.StoreKey, key, int64(clientState.GetLatestHeight().GetRevisionHeight())) + }, + true, + }, + { + "delay time period has passed", func() { + delayTimePeriod = uint64(time.Second.Nanoseconds()) + }, + true, + }, + { + "delay time period has not passed", func() { + delayTimePeriod = uint64(time.Hour.Nanoseconds()) + }, + false, + }, + { + "delay block period has passed", func() { + delayBlockPeriod = 1 + }, + true, + }, + { + "delay block period has not passed", func() { + delayBlockPeriod = 1000 + }, + false, + }, + { + "latest client height < height", func() { + proofHeight = testingpath.EndpointA.GetClientState().GetLatestHeight().Increment() + }, false, + }, + { + "invalid path type", + func() { + path = ibcmock.KeyPath{} + }, + false, + }, + { + "failed to unmarshal merkle proof", func() { + proof = invalidProof + }, false, + }, + { + "consensus state not found", func() { + proofHeight = clienttypes.ZeroHeight() + }, false, + }, + { + "verify non membership fails as path exists", func() { + // change the value being proved + key := host.FullClientStateKey(testingpath.EndpointB.ClientID) + merklePath := commitmenttypes.NewMerklePath(string(key)) + path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) + suite.Require().NoError(err) + + proof, proofHeight = suite.chainB.QueryProof(key) + }, false, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + suite.SetupTest() // reset + testingpath = ibctesting.NewPath(suite.chainA, suite.chainB) + testingpath.SetChannelOrdered() + suite.coordinator.Setup(testingpath) + + // reset time and block delays to 0, malleate may change to a specific non-zero value. + delayTimePeriod = 0 + delayBlockPeriod = 0 + + // create default proof, merklePath, and value which passes + // may be overwritten by malleate() + key := host.FullClientStateKey("invalid-client-id") + + merklePath := commitmenttypes.NewMerklePath(string(key)) + path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) + suite.Require().NoError(err) + + proof, proofHeight = suite.chainB.QueryProof(key) + + tc.malleate() // make changes as necessary + + clientState := testingpath.EndpointA.GetClientState().(*ibctm.ClientState) + + ctx := suite.chainA.GetContext() + store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(ctx, testingpath.EndpointA.ClientID) + + err = clientState.VerifyNonMembership( + ctx, store, suite.chainA.Codec, proofHeight, delayTimePeriod, delayBlockPeriod, + proof, path, + ) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } +} diff --git a/modules/light-clients/07-tendermint/types/codec.go b/modules/light-clients/07-tendermint/codec.go similarity index 79% rename from modules/light-clients/07-tendermint/types/codec.go rename to modules/light-clients/07-tendermint/codec.go index 28e68a2b7b4..09f093ab9d7 100644 --- a/modules/light-clients/07-tendermint/types/codec.go +++ b/modules/light-clients/07-tendermint/codec.go @@ -1,9 +1,9 @@ -package types +package tendermint import ( codectypes "github.com/cosmos/cosmos-sdk/codec/types" - "github.com/cosmos/ibc-go/v4/modules/core/exported" + "github.com/cosmos/ibc-go/v7/modules/core/exported" ) // RegisterInterfaces registers the tendermint concrete client-related @@ -18,11 +18,11 @@ func RegisterInterfaces(registry codectypes.InterfaceRegistry) { &ConsensusState{}, ) registry.RegisterImplementations( - (*exported.Header)(nil), + (*exported.ClientMessage)(nil), &Header{}, ) registry.RegisterImplementations( - (*exported.Misbehaviour)(nil), + (*exported.ClientMessage)(nil), &Misbehaviour{}, ) } diff --git a/modules/light-clients/07-tendermint/types/consensus_state.go b/modules/light-clients/07-tendermint/consensus_state.go similarity index 77% rename from modules/light-clients/07-tendermint/types/consensus_state.go rename to modules/light-clients/07-tendermint/consensus_state.go index 7f563285898..065c2dfa250 100644 --- a/modules/light-clients/07-tendermint/types/consensus_state.go +++ b/modules/light-clients/07-tendermint/consensus_state.go @@ -1,15 +1,15 @@ -package types +package tendermint import ( "time" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + errorsmod "cosmossdk.io/errors" tmbytes "github.com/tendermint/tendermint/libs/bytes" tmtypes "github.com/tendermint/tendermint/types" - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - commitmenttypes "github.com/cosmos/ibc-go/v4/modules/core/23-commitment/types" - "github.com/cosmos/ibc-go/v4/modules/core/exported" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + commitmenttypes "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/types" + "github.com/cosmos/ibc-go/v7/modules/core/exported" ) // SentinelRoot is used as a stand-in root value for the consensus state set at the upgrade height @@ -46,13 +46,13 @@ func (cs ConsensusState) GetTimestamp() uint64 { // as opposed to a consensus state constructed by the chain. func (cs ConsensusState) ValidateBasic() error { if cs.Root.Empty() { - return sdkerrors.Wrap(clienttypes.ErrInvalidConsensus, "root cannot be empty") + return errorsmod.Wrap(clienttypes.ErrInvalidConsensus, "root cannot be empty") } if err := tmtypes.ValidateHash(cs.NextValidatorsHash); err != nil { - return sdkerrors.Wrap(err, "next validators hash is invalid") + return errorsmod.Wrap(err, "next validators hash is invalid") } if cs.Timestamp.Unix() <= 0 { - return sdkerrors.Wrap(clienttypes.ErrInvalidConsensus, "timestamp must be a positive Unix time") + return errorsmod.Wrap(clienttypes.ErrInvalidConsensus, "timestamp must be a positive Unix time") } return nil } diff --git a/modules/light-clients/07-tendermint/types/consensus_state_test.go b/modules/light-clients/07-tendermint/consensus_state_test.go similarity index 77% rename from modules/light-clients/07-tendermint/types/consensus_state_test.go rename to modules/light-clients/07-tendermint/consensus_state_test.go index 86337eb5258..853217dd9cc 100644 --- a/modules/light-clients/07-tendermint/types/consensus_state_test.go +++ b/modules/light-clients/07-tendermint/consensus_state_test.go @@ -1,22 +1,22 @@ -package types_test +package tendermint_test import ( "time" - commitmenttypes "github.com/cosmos/ibc-go/v4/modules/core/23-commitment/types" - "github.com/cosmos/ibc-go/v4/modules/core/exported" - "github.com/cosmos/ibc-go/v4/modules/light-clients/07-tendermint/types" + commitmenttypes "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/types" + "github.com/cosmos/ibc-go/v7/modules/core/exported" + ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" ) func (suite *TendermintTestSuite) TestConsensusStateValidateBasic() { testCases := []struct { msg string - consensusState *types.ConsensusState + consensusState *ibctm.ConsensusState expectPass bool }{ { "success", - &types.ConsensusState{ + &ibctm.ConsensusState{ Timestamp: suite.now, Root: commitmenttypes.NewMerkleRoot([]byte("app_hash")), NextValidatorsHash: suite.valsHash, @@ -25,16 +25,16 @@ func (suite *TendermintTestSuite) TestConsensusStateValidateBasic() { }, { "success with sentinel", - &types.ConsensusState{ + &ibctm.ConsensusState{ Timestamp: suite.now, - Root: commitmenttypes.NewMerkleRoot([]byte(types.SentinelRoot)), + Root: commitmenttypes.NewMerkleRoot([]byte(ibctm.SentinelRoot)), NextValidatorsHash: suite.valsHash, }, true, }, { "root is nil", - &types.ConsensusState{ + &ibctm.ConsensusState{ Timestamp: suite.now, Root: commitmenttypes.MerkleRoot{}, NextValidatorsHash: suite.valsHash, @@ -43,7 +43,7 @@ func (suite *TendermintTestSuite) TestConsensusStateValidateBasic() { }, { "root is empty", - &types.ConsensusState{ + &ibctm.ConsensusState{ Timestamp: suite.now, Root: commitmenttypes.MerkleRoot{}, NextValidatorsHash: suite.valsHash, @@ -52,7 +52,7 @@ func (suite *TendermintTestSuite) TestConsensusStateValidateBasic() { }, { "nextvalshash is invalid", - &types.ConsensusState{ + &ibctm.ConsensusState{ Timestamp: suite.now, Root: commitmenttypes.NewMerkleRoot([]byte("app_hash")), NextValidatorsHash: []byte("hi"), @@ -62,7 +62,7 @@ func (suite *TendermintTestSuite) TestConsensusStateValidateBasic() { { "timestamp is zero", - &types.ConsensusState{ + &ibctm.ConsensusState{ Timestamp: time.Time{}, Root: commitmenttypes.NewMerkleRoot([]byte("app_hash")), NextValidatorsHash: suite.valsHash, diff --git a/modules/light-clients/07-tendermint/doc.go b/modules/light-clients/07-tendermint/doc.go index 26aa430a82d..85f183b8d9d 100644 --- a/modules/light-clients/07-tendermint/doc.go +++ b/modules/light-clients/07-tendermint/doc.go @@ -1,5 +1,7 @@ /* -Package tendermint implements a concrete `ConsensusState`, `Header`, -`Misbehaviour` and `Equivocation` types for the Tendermint consensus light client. +Package tendermint implements a concrete ClientState, ConsensusState, +Header, Misbehaviour and types for the Tendermint consensus light client. +This implementation is based off the ICS 07 specification +(https://github.com/cosmos/ibc/tree/main/spec/client/ics-007-tendermint-client) */ package tendermint diff --git a/modules/light-clients/07-tendermint/errors.go b/modules/light-clients/07-tendermint/errors.go new file mode 100644 index 00000000000..8bbb58ec33a --- /dev/null +++ b/modules/light-clients/07-tendermint/errors.go @@ -0,0 +1,22 @@ +package tendermint + +import ( + errorsmod "cosmossdk.io/errors" +) + +// IBC tendermint client sentinel errors +var ( + ErrInvalidChainID = errorsmod.Register(ModuleName, 2, "invalid chain-id") + ErrInvalidTrustingPeriod = errorsmod.Register(ModuleName, 3, "invalid trusting period") + ErrInvalidUnbondingPeriod = errorsmod.Register(ModuleName, 4, "invalid unbonding period") + ErrInvalidHeaderHeight = errorsmod.Register(ModuleName, 5, "invalid header height") + ErrInvalidHeader = errorsmod.Register(ModuleName, 6, "invalid header") + ErrInvalidMaxClockDrift = errorsmod.Register(ModuleName, 7, "invalid max clock drift") + ErrProcessedTimeNotFound = errorsmod.Register(ModuleName, 8, "processed time not found") + ErrProcessedHeightNotFound = errorsmod.Register(ModuleName, 9, "processed height not found") + ErrDelayPeriodNotPassed = errorsmod.Register(ModuleName, 10, "packet-specified delay period has not been reached") + ErrTrustingPeriodExpired = errorsmod.Register(ModuleName, 11, "time since latest trusted state has passed the trusting period") + ErrUnbondingPeriodExpired = errorsmod.Register(ModuleName, 12, "time since latest trusted state has passed the unbonding period") + ErrInvalidProofSpecs = errorsmod.Register(ModuleName, 13, "invalid proof specs") + ErrInvalidValidatorSet = errorsmod.Register(ModuleName, 14, "invalid validator set") +) diff --git a/modules/light-clients/07-tendermint/types/fraction.go b/modules/light-clients/07-tendermint/fraction.go similarity index 97% rename from modules/light-clients/07-tendermint/types/fraction.go rename to modules/light-clients/07-tendermint/fraction.go index e445f19ba6f..772c6d9c171 100644 --- a/modules/light-clients/07-tendermint/types/fraction.go +++ b/modules/light-clients/07-tendermint/fraction.go @@ -1,4 +1,4 @@ -package types +package tendermint import ( tmmath "github.com/tendermint/tendermint/libs/math" diff --git a/modules/light-clients/07-tendermint/types/genesis.go b/modules/light-clients/07-tendermint/genesis.go similarity index 79% rename from modules/light-clients/07-tendermint/types/genesis.go rename to modules/light-clients/07-tendermint/genesis.go index 2a36e1653e4..f9c0e6c99a8 100644 --- a/modules/light-clients/07-tendermint/types/genesis.go +++ b/modules/light-clients/07-tendermint/genesis.go @@ -1,10 +1,10 @@ -package types +package tendermint import ( sdk "github.com/cosmos/cosmos-sdk/types" - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v4/modules/core/exported" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v7/modules/core/exported" ) // ExportMetadata exports all the consensus metadata in the client store so they can be included in clients genesis diff --git a/modules/light-clients/07-tendermint/types/genesis_test.go b/modules/light-clients/07-tendermint/genesis_test.go similarity index 74% rename from modules/light-clients/07-tendermint/types/genesis_test.go rename to modules/light-clients/07-tendermint/genesis_test.go index 03fe79a935c..26033f6bfa7 100644 --- a/modules/light-clients/07-tendermint/types/genesis_test.go +++ b/modules/light-clients/07-tendermint/genesis_test.go @@ -1,11 +1,11 @@ -package types_test +package tendermint_test import ( sdk "github.com/cosmos/cosmos-sdk/types" - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v4/modules/light-clients/07-tendermint/types" - ibctesting "github.com/cosmos/ibc-go/v4/testing" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" + ibctesting "github.com/cosmos/ibc-go/v7/testing" ) // expected export ordering: @@ -19,26 +19,26 @@ func (suite *TendermintTestSuite) TestExportMetadata() { clientState := path.EndpointA.GetClientState() height := clientState.GetLatestHeight() - initIteration := types.GetIterationKey(clientStore, height) + initIteration := ibctm.GetIterationKey(clientStore, height) suite.Require().NotEqual(0, len(initIteration)) - initProcessedTime, found := types.GetProcessedTime(clientStore, height) + initProcessedTime, found := ibctm.GetProcessedTime(clientStore, height) suite.Require().True(found) - initProcessedHeight, found := types.GetProcessedHeight(clientStore, height) + initProcessedHeight, found := ibctm.GetProcessedHeight(clientStore, height) suite.Require().True(found) gm := clientState.ExportMetadata(suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID)) suite.Require().NotNil(gm, "client with metadata returned nil exported metadata") suite.Require().Len(gm, 3, "exported metadata has unexpected length") - suite.Require().Equal(types.ProcessedHeightKey(height), gm[0].GetKey(), "metadata has unexpected key") + suite.Require().Equal(ibctm.ProcessedHeightKey(height), gm[0].GetKey(), "metadata has unexpected key") actualProcessedHeight, err := clienttypes.ParseHeight(string(gm[0].GetValue())) suite.Require().NoError(err) suite.Require().Equal(initProcessedHeight, actualProcessedHeight, "metadata has unexpected value") - suite.Require().Equal(types.ProcessedTimeKey(height), gm[1].GetKey(), "metadata has unexpected key") + suite.Require().Equal(ibctm.ProcessedTimeKey(height), gm[1].GetKey(), "metadata has unexpected key") suite.Require().Equal(initProcessedTime, sdk.BigEndianToUint64(gm[1].GetValue()), "metadata has unexpected value") - suite.Require().Equal(types.IterationKey(height), gm[2].GetKey(), "metadata has unexpected key") + suite.Require().Equal(ibctm.IterationKey(height), gm[2].GetKey(), "metadata has unexpected key") suite.Require().Equal(initIteration, gm[2].GetValue(), "metadata has unexpected value") // test updating client and exporting metadata @@ -48,11 +48,11 @@ func (suite *TendermintTestSuite) TestExportMetadata() { clientState = path.EndpointA.GetClientState() updateHeight := clientState.GetLatestHeight() - iteration := types.GetIterationKey(clientStore, updateHeight) + iteration := ibctm.GetIterationKey(clientStore, updateHeight) suite.Require().NotEqual(0, len(initIteration)) - processedTime, found := types.GetProcessedTime(clientStore, updateHeight) + processedTime, found := ibctm.GetProcessedTime(clientStore, updateHeight) suite.Require().True(found) - processedHeight, found := types.GetProcessedHeight(clientStore, updateHeight) + processedHeight, found := ibctm.GetProcessedHeight(clientStore, updateHeight) suite.Require().True(found) gm = clientState.ExportMetadata(suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID)) @@ -63,27 +63,27 @@ func (suite *TendermintTestSuite) TestExportMetadata() { // initProcessedHeight, initProcessedTime, processedHeight, processedTime, initIteration, iteration // check init processed height and time - suite.Require().Equal(types.ProcessedHeightKey(height), gm[0].GetKey(), "metadata has unexpected key") + suite.Require().Equal(ibctm.ProcessedHeightKey(height), gm[0].GetKey(), "metadata has unexpected key") actualProcessedHeight, err = clienttypes.ParseHeight(string(gm[0].GetValue())) suite.Require().NoError(err) suite.Require().Equal(initProcessedHeight, actualProcessedHeight, "metadata has unexpected value") - suite.Require().Equal(types.ProcessedTimeKey(height), gm[1].GetKey(), "metadata has unexpected key") + suite.Require().Equal(ibctm.ProcessedTimeKey(height), gm[1].GetKey(), "metadata has unexpected key") suite.Require().Equal(initProcessedTime, sdk.BigEndianToUint64(gm[1].GetValue()), "metadata has unexpected value") // check processed height and time after update - suite.Require().Equal(types.ProcessedHeightKey(updateHeight), gm[2].GetKey(), "metadata has unexpected key") + suite.Require().Equal(ibctm.ProcessedHeightKey(updateHeight), gm[2].GetKey(), "metadata has unexpected key") actualProcessedHeight, err = clienttypes.ParseHeight(string(gm[2].GetValue())) suite.Require().NoError(err) suite.Require().Equal(processedHeight, actualProcessedHeight, "metadata has unexpected value") - suite.Require().Equal(types.ProcessedTimeKey(updateHeight), gm[3].GetKey(), "metadata has unexpected key") + suite.Require().Equal(ibctm.ProcessedTimeKey(updateHeight), gm[3].GetKey(), "metadata has unexpected key") suite.Require().Equal(processedTime, sdk.BigEndianToUint64(gm[3].GetValue()), "metadata has unexpected value") // check iteration keys - suite.Require().Equal(types.IterationKey(height), gm[4].GetKey(), "metadata has unexpected key") + suite.Require().Equal(ibctm.IterationKey(height), gm[4].GetKey(), "metadata has unexpected key") suite.Require().Equal(initIteration, gm[4].GetValue(), "metadata has unexpected value") - suite.Require().Equal(types.IterationKey(updateHeight), gm[5].GetKey(), "metadata has unexpected key") + suite.Require().Equal(ibctm.IterationKey(updateHeight), gm[5].GetKey(), "metadata has unexpected key") suite.Require().Equal(iteration, gm[5].GetValue(), "metadata has unexpected value") } diff --git a/modules/light-clients/07-tendermint/types/header.go b/modules/light-clients/07-tendermint/header.go similarity index 72% rename from modules/light-clients/07-tendermint/types/header.go rename to modules/light-clients/07-tendermint/header.go index e3a420680b9..e158eaab5d1 100644 --- a/modules/light-clients/07-tendermint/types/header.go +++ b/modules/light-clients/07-tendermint/header.go @@ -1,18 +1,18 @@ -package types +package tendermint import ( "bytes" "time" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + errorsmod "cosmossdk.io/errors" tmtypes "github.com/tendermint/tendermint/types" - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - commitmenttypes "github.com/cosmos/ibc-go/v4/modules/core/23-commitment/types" - "github.com/cosmos/ibc-go/v4/modules/core/exported" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + commitmenttypes "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/types" + "github.com/cosmos/ibc-go/v7/modules/core/exported" ) -var _ exported.Header = &Header{} +var _ exported.ClientMessage = &Header{} // ConsensusState returns the updated consensus state associated with the header func (h Header) ConsensusState() *ConsensusState { @@ -49,34 +49,34 @@ func (h Header) GetTime() time.Time { // with MsgCreateClient func (h Header) ValidateBasic() error { if h.SignedHeader == nil { - return sdkerrors.Wrap(clienttypes.ErrInvalidHeader, "tendermint signed header cannot be nil") + return errorsmod.Wrap(clienttypes.ErrInvalidHeader, "tendermint signed header cannot be nil") } if h.Header == nil { - return sdkerrors.Wrap(clienttypes.ErrInvalidHeader, "tendermint header cannot be nil") + return errorsmod.Wrap(clienttypes.ErrInvalidHeader, "tendermint header cannot be nil") } tmSignedHeader, err := tmtypes.SignedHeaderFromProto(h.SignedHeader) if err != nil { - return sdkerrors.Wrap(err, "header is not a tendermint header") + return errorsmod.Wrap(err, "header is not a tendermint header") } if err := tmSignedHeader.ValidateBasic(h.Header.GetChainID()); err != nil { - return sdkerrors.Wrap(err, "header failed basic validation") + return errorsmod.Wrap(err, "header failed basic validation") } // TrustedHeight is less than Header for updates and misbehaviour if h.TrustedHeight.GTE(h.GetHeight()) { - return sdkerrors.Wrapf(ErrInvalidHeaderHeight, "TrustedHeight %d must be less than header height %d", + return errorsmod.Wrapf(ErrInvalidHeaderHeight, "TrustedHeight %d must be less than header height %d", h.TrustedHeight, h.GetHeight()) } if h.ValidatorSet == nil { - return sdkerrors.Wrap(clienttypes.ErrInvalidHeader, "validator set is nil") + return errorsmod.Wrap(clienttypes.ErrInvalidHeader, "validator set is nil") } tmValset, err := tmtypes.ValidatorSetFromProto(h.ValidatorSet) if err != nil { - return sdkerrors.Wrap(err, "validator set is not tendermint validator set") + return errorsmod.Wrap(err, "validator set is not tendermint validator set") } if !bytes.Equal(h.Header.ValidatorsHash, tmValset.Hash()) { - return sdkerrors.Wrap(clienttypes.ErrInvalidHeader, "validator set does not match hash") + return errorsmod.Wrap(clienttypes.ErrInvalidHeader, "validator set does not match hash") } return nil } diff --git a/modules/light-clients/07-tendermint/types/header_test.go b/modules/light-clients/07-tendermint/header_test.go similarity index 88% rename from modules/light-clients/07-tendermint/types/header_test.go rename to modules/light-clients/07-tendermint/header_test.go index 128882f8393..5325bdb7a62 100644 --- a/modules/light-clients/07-tendermint/types/header_test.go +++ b/modules/light-clients/07-tendermint/header_test.go @@ -1,13 +1,13 @@ -package types_test +package tendermint_test import ( "time" tmprotocrypto "github.com/tendermint/tendermint/proto/tendermint/crypto" - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v4/modules/core/exported" - "github.com/cosmos/ibc-go/v4/modules/light-clients/07-tendermint/types" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v7/modules/core/exported" + ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" ) func (suite *TendermintTestSuite) TestGetHeight() { @@ -21,7 +21,7 @@ func (suite *TendermintTestSuite) TestGetTime() { } func (suite *TendermintTestSuite) TestHeaderValidateBasic() { - var header *types.Header + var header *ibctm.Header testCases := []struct { name string malleate func() diff --git a/modules/light-clients/07-tendermint/keys.go b/modules/light-clients/07-tendermint/keys.go new file mode 100644 index 00000000000..bc2fd02dec1 --- /dev/null +++ b/modules/light-clients/07-tendermint/keys.go @@ -0,0 +1,5 @@ +package tendermint + +const ( + ModuleName = "07-tendermint" +) diff --git a/modules/light-clients/07-tendermint/migrations/expected_keepers.go b/modules/light-clients/07-tendermint/migrations/expected_keepers.go new file mode 100644 index 00000000000..e47a82e40c1 --- /dev/null +++ b/modules/light-clients/07-tendermint/migrations/expected_keepers.go @@ -0,0 +1,16 @@ +package migrations + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/tendermint/tendermint/libs/log" + + "github.com/cosmos/ibc-go/v7/modules/core/exported" +) + +// ClientKeeper expected account IBC client keeper +type ClientKeeper interface { + GetClientState(ctx sdk.Context, clientID string) (exported.ClientState, bool) + IterateClientStates(ctx sdk.Context, prefix []byte, cb func(string, exported.ClientState) bool) + ClientStore(ctx sdk.Context, clientID string) sdk.KVStore + Logger(ctx sdk.Context) log.Logger +} diff --git a/modules/light-clients/07-tendermint/migrations/migrations.go b/modules/light-clients/07-tendermint/migrations/migrations.go new file mode 100644 index 00000000000..f5d28fe468f --- /dev/null +++ b/modules/light-clients/07-tendermint/migrations/migrations.go @@ -0,0 +1,46 @@ +package migrations + +import ( + errorsmod "cosmossdk.io/errors" + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v7/modules/core/exported" + ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" +) + +// PruneExpiredConsensusStates prunes all expired tendermint consensus states. This function +// may optionally be called during in-place store migrations. The ibc store key must be provided. +func PruneExpiredConsensusStates(ctx sdk.Context, cdc codec.BinaryCodec, clientKeeper ClientKeeper) (int, error) { + var clientIDs []string + clientKeeper.IterateClientStates(ctx, []byte(exported.Tendermint), func(clientID string, _ exported.ClientState) bool { + clientIDs = append(clientIDs, clientID) + return false + }) + + // keep track of the total consensus states pruned so chains can + // understand how much space is saved when the migration is run + var totalPruned int + + for _, clientID := range clientIDs { + clientStore := clientKeeper.ClientStore(ctx, clientID) + + clientState, ok := clientKeeper.GetClientState(ctx, clientID) + if !ok { + return 0, errorsmod.Wrapf(clienttypes.ErrClientNotFound, "clientID %s", clientID) + } + + tmClientState, ok := clientState.(*ibctm.ClientState) + if !ok { + return 0, errorsmod.Wrap(clienttypes.ErrInvalidClient, "client state is not tendermint even though client id contains 07-tendermint") + } + + totalPruned += ibctm.PruneAllExpiredConsensusStates(ctx, clientStore, cdc, tmClientState) + } + + clientLogger := clientKeeper.Logger(ctx) + clientLogger.Info("pruned expired tendermint consensus states", "total", totalPruned) + + return totalPruned, nil +} diff --git a/modules/light-clients/07-tendermint/migrations/migrations_test.go b/modules/light-clients/07-tendermint/migrations/migrations_test.go new file mode 100644 index 00000000000..0bfc1031725 --- /dev/null +++ b/modules/light-clients/07-tendermint/migrations/migrations_test.go @@ -0,0 +1,178 @@ +package migrations_test + +import ( + "testing" + "time" + + "github.com/stretchr/testify/suite" + + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + "github.com/cosmos/ibc-go/v7/modules/core/exported" + ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" + ibctmmigrations "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint/migrations" + ibctesting "github.com/cosmos/ibc-go/v7/testing" +) + +type MigrationsTestSuite struct { + suite.Suite + + coordinator *ibctesting.Coordinator + + // testing chains used for convenience and readability + chainA *ibctesting.TestChain + chainB *ibctesting.TestChain +} + +func (suite *MigrationsTestSuite) 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 TestTendermintTestSuite(t *testing.T) { + suite.Run(t, new(MigrationsTestSuite)) +} + +// test pruning of multiple expired tendermint consensus states +func (suite *MigrationsTestSuite) TestPruneExpiredConsensusStates() { + // create multiple tendermint clients and a solo machine client + // the solo machine is used to verify this pruning function only modifies + // the tendermint store. + + numTMClients := 3 + paths := make([]*ibctesting.Path, numTMClients) + + for i := 0; i < numTMClients; i++ { + path := ibctesting.NewPath(suite.chainA, suite.chainB) + suite.coordinator.SetupClients(path) + + paths[i] = path + } + + solomachine := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "06-solomachine-0", "testing", 1) + smClientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), solomachine.ClientID) + + // set client state + bz, err := suite.chainA.App.AppCodec().MarshalInterface(solomachine.ClientState()) + suite.Require().NoError(err) + smClientStore.Set(host.ClientStateKey(), bz) + + bz, err = suite.chainA.App.AppCodec().MarshalInterface(solomachine.ConsensusState()) + suite.Require().NoError(err) + smHeight := clienttypes.NewHeight(0, 1) + smClientStore.Set(host.ConsensusStateKey(smHeight), bz) + + pruneHeightMap := make(map[*ibctesting.Path][]exported.Height) + unexpiredHeightMap := make(map[*ibctesting.Path][]exported.Height) + + for _, path := range paths { + // collect all heights expected to be pruned + var pruneHeights []exported.Height + pruneHeights = append(pruneHeights, path.EndpointA.GetClientState().GetLatestHeight()) + + // these heights will be expired and also pruned + for i := 0; i < 3; i++ { + err := path.EndpointA.UpdateClient() + suite.Require().NoError(err) + + pruneHeights = append(pruneHeights, path.EndpointA.GetClientState().GetLatestHeight()) + } + + // double chedck all information is currently stored + for _, pruneHeight := range pruneHeights { + consState, ok := suite.chainA.GetConsensusState(path.EndpointA.ClientID, pruneHeight) + suite.Require().True(ok) + suite.Require().NotNil(consState) + + ctx := suite.chainA.GetContext() + clientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(ctx, path.EndpointA.ClientID) + + processedTime, ok := ibctm.GetProcessedTime(clientStore, pruneHeight) + suite.Require().True(ok) + suite.Require().NotNil(processedTime) + + processedHeight, ok := ibctm.GetProcessedHeight(clientStore, pruneHeight) + suite.Require().True(ok) + suite.Require().NotNil(processedHeight) + + expectedConsKey := ibctm.GetIterationKey(clientStore, pruneHeight) + suite.Require().NotNil(expectedConsKey) + } + pruneHeightMap[path] = pruneHeights + } + + // Increment the time by a week + suite.coordinator.IncrementTimeBy(7 * 24 * time.Hour) + + for _, path := range paths { + // create the consensus state that can be used as trusted height for next update + var unexpiredHeights []exported.Height + err := path.EndpointA.UpdateClient() + suite.Require().NoError(err) + unexpiredHeights = append(unexpiredHeights, path.EndpointA.GetClientState().GetLatestHeight()) + + err = path.EndpointA.UpdateClient() + suite.Require().NoError(err) + unexpiredHeights = append(unexpiredHeights, path.EndpointA.GetClientState().GetLatestHeight()) + + unexpiredHeightMap[path] = unexpiredHeights + } + + // Increment the time by another week, then update the client. + // This will cause the consensus states created before the first time increment + // to be expired + suite.coordinator.IncrementTimeBy(7 * 24 * time.Hour) + totalPruned, err := ibctmmigrations.PruneExpiredConsensusStates(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), suite.chainA.GetSimApp().IBCKeeper.ClientKeeper) + suite.Require().NoError(err) + suite.Require().NotZero(totalPruned) + + for _, path := range paths { + ctx := suite.chainA.GetContext() + clientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(ctx, path.EndpointA.ClientID) + + // ensure everything has been pruned + for i, pruneHeight := range pruneHeightMap[path] { + consState, ok := suite.chainA.GetConsensusState(path.EndpointA.ClientID, pruneHeight) + suite.Require().False(ok, i) + suite.Require().Nil(consState, i) + + processedTime, ok := ibctm.GetProcessedTime(clientStore, pruneHeight) + suite.Require().False(ok, i) + suite.Require().Equal(uint64(0), processedTime, i) + + processedHeight, ok := ibctm.GetProcessedHeight(clientStore, pruneHeight) + suite.Require().False(ok, i) + suite.Require().Nil(processedHeight, i) + + expectedConsKey := ibctm.GetIterationKey(clientStore, pruneHeight) + suite.Require().Nil(expectedConsKey, i) + } + + // ensure metadata is set for unexpired consensus state + for _, height := range unexpiredHeightMap[path] { + consState, ok := suite.chainA.GetConsensusState(path.EndpointA.ClientID, height) + suite.Require().True(ok) + suite.Require().NotNil(consState) + + processedTime, ok := ibctm.GetProcessedTime(clientStore, height) + suite.Require().True(ok) + suite.Require().NotEqual(uint64(0), processedTime) + + processedHeight, ok := ibctm.GetProcessedHeight(clientStore, height) + suite.Require().True(ok) + suite.Require().NotEqual(clienttypes.ZeroHeight(), processedHeight) + + consKey := ibctm.GetIterationKey(clientStore, height) + suite.Require().Equal(host.ConsensusStateKey(height), consKey) + } + } + + // verify that solomachine client and consensus state were not removed + smClientStore = suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), solomachine.ClientID) + bz = smClientStore.Get(host.ClientStateKey()) + suite.Require().NotEmpty(bz) + + bz = smClientStore.Get(host.ConsensusStateKey(smHeight)) + suite.Require().NotEmpty(bz) +} diff --git a/modules/light-clients/07-tendermint/types/misbehaviour.go b/modules/light-clients/07-tendermint/misbehaviour.go similarity index 68% rename from modules/light-clients/07-tendermint/types/misbehaviour.go rename to modules/light-clients/07-tendermint/misbehaviour.go index f4355cf21f6..827518f6f74 100644 --- a/modules/light-clients/07-tendermint/types/misbehaviour.go +++ b/modules/light-clients/07-tendermint/misbehaviour.go @@ -1,20 +1,20 @@ -package types +package tendermint import ( "time" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + errorsmod "cosmossdk.io/errors" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" tmtypes "github.com/tendermint/tendermint/types" - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" - "github.com/cosmos/ibc-go/v4/modules/core/exported" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + "github.com/cosmos/ibc-go/v7/modules/core/exported" ) -var _ exported.Misbehaviour = &Misbehaviour{} +var _ exported.ClientMessage = &Misbehaviour{} -// Use the same FrozenHeight for all misbehaviour +// FrozenHeight is same for all misbehaviour var FrozenHeight = clienttypes.NewHeight(0, 1) // NewMisbehaviour creates a new Misbehaviour instance. @@ -31,11 +31,6 @@ func (misbehaviour Misbehaviour) ClientType() string { return exported.Tendermint } -// GetClientID returns the ID of the client that committed a misbehaviour. -func (misbehaviour Misbehaviour) GetClientID() string { - return misbehaviour.ClientId -} - // GetTime returns the timestamp at which misbehaviour occurred. It uses the // maximum value from both headers to prevent producing an invalid header outside // of the misbehaviour age range. @@ -50,56 +45,56 @@ func (misbehaviour Misbehaviour) GetTime() time.Time { // ValidateBasic implements Misbehaviour interface func (misbehaviour Misbehaviour) ValidateBasic() error { if misbehaviour.Header1 == nil { - return sdkerrors.Wrap(ErrInvalidHeader, "misbehaviour Header1 cannot be nil") + return errorsmod.Wrap(ErrInvalidHeader, "misbehaviour Header1 cannot be nil") } if misbehaviour.Header2 == nil { - return sdkerrors.Wrap(ErrInvalidHeader, "misbehaviour Header2 cannot be nil") + return errorsmod.Wrap(ErrInvalidHeader, "misbehaviour Header2 cannot be nil") } if misbehaviour.Header1.TrustedHeight.RevisionHeight == 0 { - return sdkerrors.Wrapf(ErrInvalidHeaderHeight, "misbehaviour Header1 cannot have zero revision height") + return errorsmod.Wrapf(ErrInvalidHeaderHeight, "misbehaviour Header1 cannot have zero revision height") } if misbehaviour.Header2.TrustedHeight.RevisionHeight == 0 { - return sdkerrors.Wrapf(ErrInvalidHeaderHeight, "misbehaviour Header2 cannot have zero revision height") + return errorsmod.Wrapf(ErrInvalidHeaderHeight, "misbehaviour Header2 cannot have zero revision height") } if misbehaviour.Header1.TrustedValidators == nil { - return sdkerrors.Wrap(ErrInvalidValidatorSet, "trusted validator set in Header1 cannot be empty") + return errorsmod.Wrap(ErrInvalidValidatorSet, "trusted validator set in Header1 cannot be empty") } if misbehaviour.Header2.TrustedValidators == nil { - return sdkerrors.Wrap(ErrInvalidValidatorSet, "trusted validator set in Header2 cannot be empty") + return errorsmod.Wrap(ErrInvalidValidatorSet, "trusted validator set in Header2 cannot be empty") } if misbehaviour.Header1.Header.ChainID != misbehaviour.Header2.Header.ChainID { - return sdkerrors.Wrap(clienttypes.ErrInvalidMisbehaviour, "headers must have identical chainIDs") + return errorsmod.Wrap(clienttypes.ErrInvalidMisbehaviour, "headers must have identical chainIDs") } if err := host.ClientIdentifierValidator(misbehaviour.ClientId); err != nil { - return sdkerrors.Wrap(err, "misbehaviour client ID is invalid") + return errorsmod.Wrap(err, "misbehaviour client ID is invalid") } // ValidateBasic on both validators if err := misbehaviour.Header1.ValidateBasic(); err != nil { - return sdkerrors.Wrap( + return errorsmod.Wrap( clienttypes.ErrInvalidMisbehaviour, - sdkerrors.Wrap(err, "header 1 failed validation").Error(), + errorsmod.Wrap(err, "header 1 failed validation").Error(), ) } if err := misbehaviour.Header2.ValidateBasic(); err != nil { - return sdkerrors.Wrap( + return errorsmod.Wrap( clienttypes.ErrInvalidMisbehaviour, - sdkerrors.Wrap(err, "header 2 failed validation").Error(), + errorsmod.Wrap(err, "header 2 failed validation").Error(), ) } // Ensure that Height1 is greater than or equal to Height2 if misbehaviour.Header1.GetHeight().LT(misbehaviour.Header2.GetHeight()) { - return sdkerrors.Wrapf(clienttypes.ErrInvalidMisbehaviour, "Header1 height is less than Header2 height (%s < %s)", misbehaviour.Header1.GetHeight(), misbehaviour.Header2.GetHeight()) + return errorsmod.Wrapf(clienttypes.ErrInvalidMisbehaviour, "Header1 height is less than Header2 height (%s < %s)", misbehaviour.Header1.GetHeight(), misbehaviour.Header2.GetHeight()) } blockID1, err := tmtypes.BlockIDFromProto(&misbehaviour.Header1.SignedHeader.Commit.BlockID) if err != nil { - return sdkerrors.Wrap(err, "invalid block ID from header 1 in misbehaviour") + return errorsmod.Wrap(err, "invalid block ID from header 1 in misbehaviour") } blockID2, err := tmtypes.BlockIDFromProto(&misbehaviour.Header2.SignedHeader.Commit.BlockID) if err != nil { - return sdkerrors.Wrap(err, "invalid block ID from header 2 in misbehaviour") + return errorsmod.Wrap(err, "invalid block ID from header 2 in misbehaviour") } if err := validCommit(misbehaviour.Header1.Header.ChainID, *blockID1, @@ -117,15 +112,15 @@ func (misbehaviour Misbehaviour) ValidateBasic() error { func validCommit(chainID string, blockID tmtypes.BlockID, commit *tmproto.Commit, valSet *tmproto.ValidatorSet) (err error) { tmCommit, err := tmtypes.CommitFromProto(commit) if err != nil { - return sdkerrors.Wrap(err, "commit is not tendermint commit type") + return errorsmod.Wrap(err, "commit is not tendermint commit type") } tmValset, err := tmtypes.ValidatorSetFromProto(valSet) if err != nil { - return sdkerrors.Wrap(err, "validator set is not tendermint validator set type") + return errorsmod.Wrap(err, "validator set is not tendermint validator set type") } if err := tmValset.VerifyCommitLight(chainID, blockID, tmCommit.Height, tmCommit); err != nil { - return sdkerrors.Wrap(clienttypes.ErrInvalidMisbehaviour, "validator set did not commit to header") + return errorsmod.Wrap(clienttypes.ErrInvalidMisbehaviour, "validator set did not commit to header") } return nil diff --git a/modules/light-clients/07-tendermint/misbehaviour_handle.go b/modules/light-clients/07-tendermint/misbehaviour_handle.go new file mode 100644 index 00000000000..ef3e654179d --- /dev/null +++ b/modules/light-clients/07-tendermint/misbehaviour_handle.go @@ -0,0 +1,170 @@ +package tendermint + +import ( + "bytes" + "reflect" + "time" + + errorsmod "cosmossdk.io/errors" + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + tmtypes "github.com/tendermint/tendermint/types" + + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v7/modules/core/exported" +) + +// CheckForMisbehaviour detects duplicate height misbehaviour and BFT time violation misbehaviour +// in a submitted Header message and verifies the correctness of a submitted Misbehaviour ClientMessage +func (cs ClientState) CheckForMisbehaviour(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, msg exported.ClientMessage) bool { + switch msg := msg.(type) { + case *Header: + tmHeader := msg + consState := tmHeader.ConsensusState() + + // Check if the Client store already has a consensus state for the header's height + // If the consensus state exists, and it matches the header then we return early + // since header has already been submitted in a previous UpdateClient. + existingConsState, _ := GetConsensusState(clientStore, cdc, tmHeader.GetHeight()) + if existingConsState != nil { + // This header has already been submitted and the necessary state is already stored + // in client store, thus we can return early without further validation. + if reflect.DeepEqual(existingConsState, tmHeader.ConsensusState()) { //nolint:gosimple + return false + } + + // A consensus state already exists for this height, but it does not match the provided header. + // The assumption is that Header has already been validated. Thus we can return true as misbehaviour is present + return true + } + + // Check that consensus state timestamps are monotonic + prevCons, prevOk := GetPreviousConsensusState(clientStore, cdc, tmHeader.GetHeight()) + nextCons, nextOk := GetNextConsensusState(clientStore, cdc, tmHeader.GetHeight()) + // if previous consensus state exists, check consensus state time is greater than previous consensus state time + // if previous consensus state is not before current consensus state return true + if prevOk && !prevCons.Timestamp.Before(consState.Timestamp) { + return true + } + // if next consensus state exists, check consensus state time is less than next consensus state time + // if next consensus state is not after current consensus state return true + if nextOk && !nextCons.Timestamp.After(consState.Timestamp) { + return true + } + case *Misbehaviour: + // if heights are equal check that this is valid misbehaviour of a fork + // otherwise if heights are unequal check that this is valid misbehavior of BFT time violation + if msg.Header1.GetHeight().EQ(msg.Header2.GetHeight()) { + blockID1, err := tmtypes.BlockIDFromProto(&msg.Header1.SignedHeader.Commit.BlockID) + if err != nil { + return false + } + + blockID2, err := tmtypes.BlockIDFromProto(&msg.Header2.SignedHeader.Commit.BlockID) + if err != nil { + return false + } + + // Ensure that Commit Hashes are different + if !bytes.Equal(blockID1.Hash, blockID2.Hash) { + return true + } + + } else if !msg.Header1.SignedHeader.Header.Time.After(msg.Header2.SignedHeader.Header.Time) { + // Header1 is at greater height than Header2, therefore Header1 time must be less than or equal to + // Header2 time in order to be valid misbehaviour (violation of monotonic time). + return true + } + } + + return false +} + +// verifyMisbehaviour determines whether or not two conflicting +// headers at the same height would have convinced the light client. +// +// NOTE: consensusState1 is the trusted consensus state that corresponds to the TrustedHeight +// of misbehaviour.Header1 +// Similarly, consensusState2 is the trusted consensus state that corresponds +// to misbehaviour.Header2 +// Misbehaviour sets frozen height to {0, 1} since it is only used as a boolean value (zero or non-zero). +func (cs *ClientState) verifyMisbehaviour(ctx sdk.Context, clientStore sdk.KVStore, cdc codec.BinaryCodec, misbehaviour *Misbehaviour) error { + // Regardless of the type of misbehaviour, ensure that both headers are valid and would have been accepted by light-client + + // Retrieve trusted consensus states for each Header in misbehaviour + tmConsensusState1, found := GetConsensusState(clientStore, cdc, misbehaviour.Header1.TrustedHeight) + if !found { + return errorsmod.Wrapf(clienttypes.ErrConsensusStateNotFound, "could not get trusted consensus state from clientStore for Header1 at TrustedHeight: %s", misbehaviour.Header1.TrustedHeight) + } + + tmConsensusState2, found := GetConsensusState(clientStore, cdc, misbehaviour.Header2.TrustedHeight) + if !found { + return errorsmod.Wrapf(clienttypes.ErrConsensusStateNotFound, "could not get trusted consensus state from clientStore for Header2 at TrustedHeight: %s", misbehaviour.Header2.TrustedHeight) + } + + // Check the validity of the two conflicting headers against their respective + // trusted consensus states + // NOTE: header height and commitment root assertions are checked in + // misbehaviour.ValidateBasic by the client keeper and msg.ValidateBasic + // by the base application. + if err := checkMisbehaviourHeader( + cs, tmConsensusState1, misbehaviour.Header1, ctx.BlockTime(), + ); err != nil { + return errorsmod.Wrap(err, "verifying Header1 in Misbehaviour failed") + } + if err := checkMisbehaviourHeader( + cs, tmConsensusState2, misbehaviour.Header2, ctx.BlockTime(), + ); err != nil { + return errorsmod.Wrap(err, "verifying Header2 in Misbehaviour failed") + } + + return nil +} + +// checkMisbehaviourHeader checks that a Header in Misbehaviour is valid misbehaviour given +// a trusted ConsensusState +func checkMisbehaviourHeader( + clientState *ClientState, consState *ConsensusState, header *Header, currentTimestamp time.Time, +) error { + tmTrustedValset, err := tmtypes.ValidatorSetFromProto(header.TrustedValidators) + if err != nil { + return errorsmod.Wrap(err, "trusted validator set is not tendermint validator set type") + } + + tmCommit, err := tmtypes.CommitFromProto(header.Commit) + if err != nil { + return errorsmod.Wrap(err, "commit is not tendermint commit type") + } + + // check the trusted fields for the header against ConsensusState + if err := checkTrustedHeader(header, consState); err != nil { + return err + } + + // assert that the age of the trusted consensus state is not older than the trusting period + if currentTimestamp.Sub(consState.Timestamp) >= clientState.TrustingPeriod { + return errorsmod.Wrapf( + ErrTrustingPeriodExpired, + "current timestamp minus the latest consensus state timestamp is greater than or equal to the trusting period (%d >= %d)", + currentTimestamp.Sub(consState.Timestamp), clientState.TrustingPeriod, + ) + } + + chainID := clientState.GetChainID() + // If chainID is in revision format, then set revision number of chainID with the revision number + // of the misbehaviour header + // NOTE: misbehaviour verification is not supported for chains which upgrade to a new chainID without + // strictly following the chainID revision format + if clienttypes.IsRevisionFormat(chainID) { + chainID, _ = clienttypes.SetRevisionNumber(chainID, header.GetHeight().GetRevisionNumber()) + } + + // - ValidatorSet must have TrustLevel similarity with trusted FromValidatorSet + // - ValidatorSets on both headers are valid given the last trusted ValidatorSet + if err := tmTrustedValset.VerifyCommitLightTrusting( + chainID, tmCommit, clientState.TrustLevel.ToTendermint(), + ); err != nil { + return errorsmod.Wrapf(clienttypes.ErrInvalidMisbehaviour, "validator set in header has too much change from trusted validator set: %v", err) + } + return nil +} diff --git a/modules/light-clients/07-tendermint/misbehaviour_handle_test.go b/modules/light-clients/07-tendermint/misbehaviour_handle_test.go new file mode 100644 index 00000000000..ae7a2dd126b --- /dev/null +++ b/modules/light-clients/07-tendermint/misbehaviour_handle_test.go @@ -0,0 +1,648 @@ +package tendermint_test + +import ( + "fmt" + "strings" + "time" + + tmtypes "github.com/tendermint/tendermint/types" + + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v7/modules/core/exported" + solomachine "github.com/cosmos/ibc-go/v7/modules/light-clients/06-solomachine" + ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" + ibctesting "github.com/cosmos/ibc-go/v7/testing" + ibctestingmock "github.com/cosmos/ibc-go/v7/testing/mock" +) + +func (suite *TendermintTestSuite) TestVerifyMisbehaviour() { + // Setup different validators and signers for testing different types of updates + altPrivVal := ibctestingmock.NewPV() + altPubKey, err := altPrivVal.GetPubKey() + suite.Require().NoError(err) + + // create modified heights to use for test-cases + altVal := tmtypes.NewValidator(altPubKey, 100) + + // Create alternative validator set with only altVal, invalid update (too much change in valSet) + altValSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{altVal}) + altSigners := getAltSigners(altVal, altPrivVal) + + var ( + path *ibctesting.Path + misbehaviour exported.ClientMessage + ) + + testCases := []struct { + name string + malleate func() + expPass bool + }{ + { + "valid fork misbehaviour", func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + err := path.EndpointA.UpdateClient() + suite.Require().NoError(err) + + height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + misbehaviour = &ibctm.Misbehaviour{ + Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + } + }, + true, + }, + { + "valid time misbehaviour", func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + misbehaviour = &ibctm.Misbehaviour{ + Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height+3, trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height, trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + } + }, + true, + }, + { + "valid time misbehaviour, header 1 time stricly less than header 2 time", func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + misbehaviour = &ibctm.Misbehaviour{ + Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height+3, trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height, trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Hour), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + } + }, + true, + }, + { + "valid misbehavior at height greater than last consensusState", func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + misbehaviour = &ibctm.Misbehaviour{ + Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height+1, trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height+1, trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + } + }, true, + }, + { + "valid misbehaviour with different trusted heights", func() { + trustedHeight1 := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals1, found := suite.chainB.GetValsAtHeight(int64(trustedHeight1.RevisionHeight) + 1) + suite.Require().True(found) + + err := path.EndpointA.UpdateClient() + suite.Require().NoError(err) + + trustedHeight2 := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals2, found := suite.chainB.GetValsAtHeight(int64(trustedHeight2.RevisionHeight) + 1) + suite.Require().True(found) + + misbehaviour = &ibctm.Misbehaviour{ + Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height, trustedHeight1, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals1, suite.chainB.Signers), + Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height, trustedHeight2, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals2, suite.chainB.Signers), + } + }, + true, + }, + { + "valid misbehaviour at a previous revision", func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + err := path.EndpointA.UpdateClient() + suite.Require().NoError(err) + + height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + misbehaviour = &ibctm.Misbehaviour{ + Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + } + + // increment revision number + err = path.EndpointB.UpgradeChain() + suite.Require().NoError(err) + }, + true, + }, + { + "valid misbehaviour at a future revision", func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + futureRevision := fmt.Sprintf("%s-%d", strings.TrimSuffix(suite.chainB.ChainID, fmt.Sprintf("-%d", clienttypes.ParseChainID(suite.chainB.ChainID))), height.GetRevisionNumber()+1) + + misbehaviour = &ibctm.Misbehaviour{ + Header1: suite.chainB.CreateTMClientHeader(futureRevision, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + Header2: suite.chainB.CreateTMClientHeader(futureRevision, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + } + }, + true, + }, + { + "valid misbehaviour with trusted heights at a previous revision", func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + // increment revision of chainID + err = path.EndpointB.UpgradeChain() + suite.Require().NoError(err) + + height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + misbehaviour = &ibctm.Misbehaviour{ + Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + } + }, + true, + }, + { + "consensus state's valset hash different from misbehaviour should still pass", func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + err := path.EndpointA.UpdateClient() + suite.Require().NoError(err) + + height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + // Create bothValSet with both suite validator and altVal + bothValSet := tmtypes.NewValidatorSet(append(suite.chainB.Vals.Validators, altValSet.Proposer)) + bothSigners := suite.chainB.Signers + bothSigners[altValSet.Proposer.Address.String()] = altPrivVal + + misbehaviour = &ibctm.Misbehaviour{ + Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), bothValSet, suite.chainB.NextVals, trustedVals, bothSigners), + Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, bothValSet, suite.chainB.NextVals, trustedVals, bothSigners), + } + }, true, + }, + { + "invalid misbehaviour: misbehaviour from different chain", func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + err := path.EndpointA.UpdateClient() + suite.Require().NoError(err) + + height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + misbehaviour = &ibctm.Misbehaviour{ + Header1: suite.chainB.CreateTMClientHeader("evmos", int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + Header2: suite.chainB.CreateTMClientHeader("evmos", int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + } + }, false, + }, + { + "misbehaviour trusted validators does not match validator hash in trusted consensus state", func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + err := path.EndpointA.UpdateClient() + suite.Require().NoError(err) + + height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + misbehaviour = &ibctm.Misbehaviour{ + Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, altValSet, suite.chainB.Signers), + Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, altValSet, suite.chainB.Signers), + } + }, false, + }, + { + "trusted consensus state does not exist", func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + misbehaviour = &ibctm.Misbehaviour{ + Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height, trustedHeight.Increment().(clienttypes.Height), suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height, trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + } + }, false, + }, + { + "invalid tendermint misbehaviour", func() { + misbehaviour = &solomachine.Misbehaviour{} + }, false, + }, + { + "trusting period expired", func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + err := path.EndpointA.UpdateClient() + suite.Require().NoError(err) + + height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + suite.chainA.ExpireClient(path.EndpointA.ClientConfig.(*ibctesting.TendermintConfig).TrustingPeriod) + + misbehaviour = &ibctm.Misbehaviour{ + Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + } + }, false, + }, + { + "header 1 valset has too much change", func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + err := path.EndpointA.UpdateClient() + suite.Require().NoError(err) + + height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + misbehaviour = &ibctm.Misbehaviour{ + Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), altValSet, suite.chainB.NextVals, trustedVals, altSigners), + Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + } + }, false, + }, + { + "header 2 valset has too much change", func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + err := path.EndpointA.UpdateClient() + suite.Require().NoError(err) + + height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + misbehaviour = &ibctm.Misbehaviour{ + Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, altValSet, suite.chainB.NextVals, trustedVals, altSigners), + } + }, false, + }, + { + "both header 1 and header 2 valsets have too much change", func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + err := path.EndpointA.UpdateClient() + suite.Require().NoError(err) + + height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + misbehaviour = &ibctm.Misbehaviour{ + Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), altValSet, suite.chainB.NextVals, trustedVals, altSigners), + Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, altValSet, suite.chainB.NextVals, trustedVals, altSigners), + } + }, false, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + suite.SetupTest() + path = ibctesting.NewPath(suite.chainA, suite.chainB) + + err := path.EndpointA.CreateClient() + suite.Require().NoError(err) + + tc.malleate() + + clientState := path.EndpointA.GetClientState() + clientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) + + err = clientState.VerifyClientMessage(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), clientStore, misbehaviour) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } +} + +// test both fork and time misbehaviour for chainIDs not in the revision format +// this function is separate as it must use a global variable in the testing package +// to initialize chains not in the revision format +func (suite *TendermintTestSuite) TestVerifyMisbehaviourNonRevisionChainID() { + // NOTE: chains set to non revision format + ibctesting.ChainIDSuffix = "" + + // Setup different validators and signers for testing different types of updates + altPrivVal := ibctestingmock.NewPV() + altPubKey, err := altPrivVal.GetPubKey() + suite.Require().NoError(err) + + // create modified heights to use for test-cases + altVal := tmtypes.NewValidator(altPubKey, 100) + + // Create alternative validator set with only altVal, invalid update (too much change in valSet) + altValSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{altVal}) + altSigners := getAltSigners(altVal, altPrivVal) + + var ( + path *ibctesting.Path + misbehaviour exported.ClientMessage + ) + + testCases := []struct { + name string + malleate func() + expPass bool + }{ + { + "valid fork misbehaviour", func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + err := path.EndpointA.UpdateClient() + suite.Require().NoError(err) + + height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + misbehaviour = &ibctm.Misbehaviour{ + Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + } + }, + true, + }, + { + "valid time misbehaviour", func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + misbehaviour = &ibctm.Misbehaviour{ + Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height+3, trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height, trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + } + }, + true, + }, + { + "valid time misbehaviour, header 1 time stricly less than header 2 time", func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + misbehaviour = &ibctm.Misbehaviour{ + Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height+3, trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height, trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Hour), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + } + }, + true, + }, + { + "valid misbehavior at height greater than last consensusState", func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + misbehaviour = &ibctm.Misbehaviour{ + Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height+1, trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height+1, trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + } + }, true, + }, + { + "valid misbehaviour with different trusted heights", func() { + trustedHeight1 := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals1, found := suite.chainB.GetValsAtHeight(int64(trustedHeight1.RevisionHeight) + 1) + suite.Require().True(found) + + err := path.EndpointA.UpdateClient() + suite.Require().NoError(err) + + trustedHeight2 := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals2, found := suite.chainB.GetValsAtHeight(int64(trustedHeight2.RevisionHeight) + 1) + suite.Require().True(found) + + misbehaviour = &ibctm.Misbehaviour{ + Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height, trustedHeight1, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals1, suite.chainB.Signers), + Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height, trustedHeight2, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals2, suite.chainB.Signers), + } + }, + true, + }, + { + "consensus state's valset hash different from misbehaviour should still pass", func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + err := path.EndpointA.UpdateClient() + suite.Require().NoError(err) + + height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + // Create bothValSet with both suite validator and altVal + bothValSet := tmtypes.NewValidatorSet(append(suite.chainB.Vals.Validators, altValSet.Proposer)) + bothSigners := suite.chainB.Signers + bothSigners[altValSet.Proposer.Address.String()] = altPrivVal + + misbehaviour = &ibctm.Misbehaviour{ + Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), bothValSet, suite.chainB.NextVals, trustedVals, bothSigners), + Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, bothValSet, suite.chainB.NextVals, trustedVals, bothSigners), + } + }, true, + }, + { + "invalid misbehaviour: misbehaviour from different chain", func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + err := path.EndpointA.UpdateClient() + suite.Require().NoError(err) + + height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + misbehaviour = &ibctm.Misbehaviour{ + Header1: suite.chainB.CreateTMClientHeader("evmos", int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + Header2: suite.chainB.CreateTMClientHeader("evmos", int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + } + }, false, + }, + { + "misbehaviour trusted validators does not match validator hash in trusted consensus state", func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + err := path.EndpointA.UpdateClient() + suite.Require().NoError(err) + + height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + misbehaviour = &ibctm.Misbehaviour{ + Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, altValSet, suite.chainB.Signers), + Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, altValSet, suite.chainB.Signers), + } + }, false, + }, + { + "trusted consensus state does not exist", func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + misbehaviour = &ibctm.Misbehaviour{ + Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height, trustedHeight.Increment().(clienttypes.Height), suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height, trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + } + }, false, + }, + { + "invalid tendermint misbehaviour", func() { + misbehaviour = &solomachine.Misbehaviour{} + }, false, + }, + { + "trusting period expired", func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + err := path.EndpointA.UpdateClient() + suite.Require().NoError(err) + + height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + suite.chainA.ExpireClient(path.EndpointA.ClientConfig.(*ibctesting.TendermintConfig).TrustingPeriod) + + misbehaviour = &ibctm.Misbehaviour{ + Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + } + }, false, + }, + { + "header 1 valset has too much change", func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + err := path.EndpointA.UpdateClient() + suite.Require().NoError(err) + + height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + misbehaviour = &ibctm.Misbehaviour{ + Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), altValSet, suite.chainB.NextVals, trustedVals, altSigners), + Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + } + }, false, + }, + { + "header 2 valset has too much change", func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + err := path.EndpointA.UpdateClient() + suite.Require().NoError(err) + + height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + misbehaviour = &ibctm.Misbehaviour{ + Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, altValSet, suite.chainB.NextVals, trustedVals, altSigners), + } + }, false, + }, + { + "both header 1 and header 2 valsets have too much change", func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + err := path.EndpointA.UpdateClient() + suite.Require().NoError(err) + + height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + misbehaviour = &ibctm.Misbehaviour{ + Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), altValSet, suite.chainB.NextVals, trustedVals, altSigners), + Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, altValSet, suite.chainB.NextVals, trustedVals, altSigners), + } + }, false, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + suite.SetupTest() + path = ibctesting.NewPath(suite.chainA, suite.chainB) + + err := path.EndpointA.CreateClient() + suite.Require().NoError(err) + + tc.malleate() + + clientState := path.EndpointA.GetClientState() + clientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) + + err = clientState.VerifyClientMessage(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), clientStore, misbehaviour) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } + + // NOTE: reset chain creation to revision format + ibctesting.ChainIDSuffix = "-1" +} diff --git a/modules/light-clients/07-tendermint/types/misbehaviour_test.go b/modules/light-clients/07-tendermint/misbehaviour_test.go similarity index 79% rename from modules/light-clients/07-tendermint/types/misbehaviour_test.go rename to modules/light-clients/07-tendermint/misbehaviour_test.go index 5b67212be5e..d49df447f3c 100644 --- a/modules/light-clients/07-tendermint/types/misbehaviour_test.go +++ b/modules/light-clients/07-tendermint/misbehaviour_test.go @@ -1,4 +1,4 @@ -package types_test +package tendermint_test import ( "time" @@ -7,24 +7,23 @@ import ( tmproto "github.com/tendermint/tendermint/proto/tendermint/types" tmtypes "github.com/tendermint/tendermint/types" - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v4/modules/core/exported" - "github.com/cosmos/ibc-go/v4/modules/light-clients/07-tendermint/types" - ibctesting "github.com/cosmos/ibc-go/v4/testing" - ibctestingmock "github.com/cosmos/ibc-go/v4/testing/mock" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v7/modules/core/exported" + ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" + ibctesting "github.com/cosmos/ibc-go/v7/testing" + ibctestingmock "github.com/cosmos/ibc-go/v7/testing/mock" ) func (suite *TendermintTestSuite) TestMisbehaviour() { heightMinus1 := clienttypes.NewHeight(0, height.RevisionHeight-1) - misbehaviour := &types.Misbehaviour{ + misbehaviour := &ibctm.Misbehaviour{ Header1: suite.header, Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), heightMinus1, suite.now, suite.valSet, suite.valSet, suite.valSet, suite.signers), ClientId: clientID, } suite.Require().Equal(exported.Tendermint, misbehaviour.ClientType()) - suite.Require().Equal(clientID, misbehaviour.GetClientID()) } func (suite *TendermintTestSuite) TestMisbehaviourValidateBasic() { @@ -48,130 +47,130 @@ func (suite *TendermintTestSuite) TestMisbehaviourValidateBasic() { testCases := []struct { name string - misbehaviour *types.Misbehaviour - malleateMisbehaviour func(misbehaviour *types.Misbehaviour) error + misbehaviour *ibctm.Misbehaviour + malleateMisbehaviour func(misbehaviour *ibctm.Misbehaviour) error expPass bool }{ { "valid fork misbehaviour, two headers at same height have different time", - &types.Misbehaviour{ + &ibctm.Misbehaviour{ Header1: suite.header, Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), heightMinus1, suite.now.Add(time.Minute), suite.valSet, suite.valSet, suite.valSet, suite.signers), ClientId: clientID, }, - func(misbehaviour *types.Misbehaviour) error { return nil }, + func(misbehaviour *ibctm.Misbehaviour) error { return nil }, true, }, { "valid time misbehaviour, both headers at different heights are at same time", - &types.Misbehaviour{ + &ibctm.Misbehaviour{ Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+5), heightMinus1, suite.now, suite.valSet, suite.valSet, suite.valSet, suite.signers), Header2: suite.header, ClientId: clientID, }, - func(misbehaviour *types.Misbehaviour) error { return nil }, + func(misbehaviour *ibctm.Misbehaviour) error { return nil }, true, }, { "misbehaviour Header1 is nil", - types.NewMisbehaviour(clientID, nil, suite.header), - func(m *types.Misbehaviour) error { return nil }, + ibctm.NewMisbehaviour(clientID, nil, suite.header), + func(m *ibctm.Misbehaviour) error { return nil }, false, }, { "misbehaviour Header2 is nil", - types.NewMisbehaviour(clientID, suite.header, nil), - func(m *types.Misbehaviour) error { return nil }, + ibctm.NewMisbehaviour(clientID, suite.header, nil), + func(m *ibctm.Misbehaviour) error { return nil }, false, }, { "valid misbehaviour with different trusted headers", - &types.Misbehaviour{ + &ibctm.Misbehaviour{ Header1: suite.header, Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), clienttypes.NewHeight(0, height.RevisionHeight-3), suite.now.Add(time.Minute), suite.valSet, suite.valSet, bothValSet, suite.signers), ClientId: clientID, }, - func(misbehaviour *types.Misbehaviour) error { return nil }, + func(misbehaviour *ibctm.Misbehaviour) error { return nil }, true, }, { "trusted height is 0 in Header1", - &types.Misbehaviour{ + &ibctm.Misbehaviour{ Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), clienttypes.ZeroHeight(), suite.now.Add(time.Minute), suite.valSet, suite.valSet, suite.valSet, suite.signers), Header2: suite.header, ClientId: clientID, }, - func(misbehaviour *types.Misbehaviour) error { return nil }, + func(misbehaviour *ibctm.Misbehaviour) error { return nil }, false, }, { "trusted height is 0 in Header2", - &types.Misbehaviour{ + &ibctm.Misbehaviour{ Header1: suite.header, Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), clienttypes.ZeroHeight(), suite.now.Add(time.Minute), suite.valSet, suite.valSet, suite.valSet, suite.signers), ClientId: clientID, }, - func(misbehaviour *types.Misbehaviour) error { return nil }, + func(misbehaviour *ibctm.Misbehaviour) error { return nil }, false, }, { "trusted valset is nil in Header1", - &types.Misbehaviour{ + &ibctm.Misbehaviour{ Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), heightMinus1, suite.now.Add(time.Minute), suite.valSet, suite.valSet, nil, suite.signers), Header2: suite.header, ClientId: clientID, }, - func(misbehaviour *types.Misbehaviour) error { return nil }, + func(misbehaviour *ibctm.Misbehaviour) error { return nil }, false, }, { "trusted valset is nil in Header2", - &types.Misbehaviour{ + &ibctm.Misbehaviour{ Header1: suite.header, Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), heightMinus1, suite.now.Add(time.Minute), suite.valSet, suite.valSet, nil, suite.signers), ClientId: clientID, }, - func(misbehaviour *types.Misbehaviour) error { return nil }, + func(misbehaviour *ibctm.Misbehaviour) error { return nil }, false, }, { "invalid client ID ", - &types.Misbehaviour{ + &ibctm.Misbehaviour{ Header1: suite.header, Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), heightMinus1, suite.now, suite.valSet, suite.valSet, suite.valSet, suite.signers), ClientId: "GAIA", }, - func(misbehaviour *types.Misbehaviour) error { return nil }, + func(misbehaviour *ibctm.Misbehaviour) error { return nil }, false, }, { "chainIDs do not match", - &types.Misbehaviour{ + &ibctm.Misbehaviour{ Header1: suite.header, Header2: suite.chainA.CreateTMClientHeader("ethermint", int64(height.RevisionHeight), heightMinus1, suite.now, suite.valSet, suite.valSet, suite.valSet, suite.signers), ClientId: clientID, }, - func(misbehaviour *types.Misbehaviour) error { return nil }, + func(misbehaviour *ibctm.Misbehaviour) error { return nil }, false, }, { "header2 height is greater", - &types.Misbehaviour{ + &ibctm.Misbehaviour{ Header1: suite.header, Header2: suite.chainA.CreateTMClientHeader(chainID, 6, clienttypes.NewHeight(0, height.RevisionHeight+1), suite.now, suite.valSet, suite.valSet, suite.valSet, suite.signers), ClientId: clientID, }, - func(misbehaviour *types.Misbehaviour) error { return nil }, + func(misbehaviour *ibctm.Misbehaviour) error { return nil }, false, }, { "header 1 doesn't have 2/3 majority", - &types.Misbehaviour{ + &ibctm.Misbehaviour{ Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), heightMinus1, suite.now, bothValSet, bothValSet, suite.valSet, bothSigners), Header2: suite.header, ClientId: clientID, }, - func(misbehaviour *types.Misbehaviour) error { + func(misbehaviour *ibctm.Misbehaviour) error { // voteSet contains only altVal which is less than 2/3 of total power (height/1height) wrongVoteSet := tmtypes.NewVoteSet(chainID, int64(misbehaviour.Header1.GetHeight().GetRevisionHeight()), 1, tmproto.PrecommitType, altValSet) blockID, err := tmtypes.BlockIDFromProto(&misbehaviour.Header1.Commit.BlockID) @@ -187,12 +186,12 @@ func (suite *TendermintTestSuite) TestMisbehaviourValidateBasic() { }, { "header 2 doesn't have 2/3 majority", - &types.Misbehaviour{ + &ibctm.Misbehaviour{ Header1: suite.header, Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), heightMinus1, suite.now, bothValSet, bothValSet, suite.valSet, bothSigners), ClientId: clientID, }, - func(misbehaviour *types.Misbehaviour) error { + func(misbehaviour *ibctm.Misbehaviour) error { // voteSet contains only altVal which is less than 2/3 of total power (height/1height) wrongVoteSet := tmtypes.NewVoteSet(chainID, int64(misbehaviour.Header2.GetHeight().GetRevisionHeight()), 1, tmproto.PrecommitType, altValSet) blockID, err := tmtypes.BlockIDFromProto(&misbehaviour.Header2.Commit.BlockID) @@ -208,12 +207,12 @@ func (suite *TendermintTestSuite) TestMisbehaviourValidateBasic() { }, { "validators sign off on wrong commit", - &types.Misbehaviour{ + &ibctm.Misbehaviour{ Header1: suite.header, Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), heightMinus1, suite.now, bothValSet, bothValSet, suite.valSet, bothSigners), ClientId: clientID, }, - func(misbehaviour *types.Misbehaviour) error { + func(misbehaviour *ibctm.Misbehaviour) error { tmBlockID := ibctesting.MakeBlockID(tmhash.Sum([]byte("other_hash")), 3, tmhash.Sum([]byte("other_partset"))) misbehaviour.Header2.Commit.BlockID = tmBlockID.ToProto() return nil diff --git a/modules/light-clients/07-tendermint/module.go b/modules/light-clients/07-tendermint/module.go index 05be0ea1217..c66bc36102d 100644 --- a/modules/light-clients/07-tendermint/module.go +++ b/modules/light-clients/07-tendermint/module.go @@ -1,10 +1,56 @@ package tendermint import ( - "github.com/cosmos/ibc-go/v4/modules/light-clients/07-tendermint/types" + "encoding/json" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/types/module" + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "github.com/spf13/cobra" ) -// Name returns the IBC client name -func Name() string { - return types.SubModuleName +var _ module.AppModuleBasic = AppModuleBasic{} + +// AppModuleBasic defines the basic application module used by the tendermint light client. +// Only the RegisterInterfaces function needs to be implemented. All other function perform +// a no-op. +type AppModuleBasic struct{} + +// Name returns the tendermint module name. +func (AppModuleBasic) Name() string { + return ModuleName +} + +// RegisterLegacyAminoCodec performs a no-op. The Tendermint client does not support amino. +func (AppModuleBasic) RegisterLegacyAminoCodec(*codec.LegacyAmino) {} + +// RegisterInterfaces registers module concrete types into protobuf Any. This allows core IBC +// to unmarshal tendermint light client types. +func (AppModuleBasic) RegisterInterfaces(registry codectypes.InterfaceRegistry) { + RegisterInterfaces(registry) +} + +// DefaultGenesis performs a no-op. Genesis is not supported for the tendermint light client. +func (AppModuleBasic) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage { + return nil +} + +// ValidateGenesis performs a no-op. Genesis is not supported for the tendermint light cilent. +func (AppModuleBasic) ValidateGenesis(cdc codec.JSONCodec, config client.TxEncodingConfig, bz json.RawMessage) error { + return nil +} + +// RegisterGRPCGatewayRoutes performs a no-op. +func (AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *runtime.ServeMux) {} + +// GetTxCmd performs a no-op. Please see the 02-client cli commands. +func (AppModuleBasic) GetTxCmd() *cobra.Command { + return nil +} + +// GetQueryCmd performs a no-op. Please see the 02-client cli commands. +func (AppModuleBasic) GetQueryCmd() *cobra.Command { + return nil } diff --git a/modules/light-clients/07-tendermint/types/proposal_handle.go b/modules/light-clients/07-tendermint/proposal_handle.go similarity index 69% rename from modules/light-clients/07-tendermint/types/proposal_handle.go rename to modules/light-clients/07-tendermint/proposal_handle.go index 85720f7eaeb..98b37ebf019 100644 --- a/modules/light-clients/07-tendermint/types/proposal_handle.go +++ b/modules/light-clients/07-tendermint/proposal_handle.go @@ -1,15 +1,15 @@ -package types +package tendermint import ( "reflect" "time" + errorsmod "cosmossdk.io/errors" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v4/modules/core/exported" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v7/modules/core/exported" ) // CheckSubstituteAndUpdateState will try to update the client with the state of the @@ -19,24 +19,22 @@ import ( // Please see ADR 026 for more information. // // The following must always be true: -// - The substitute client is the same type as the subject client -// - The subject and substitute client states match in all parameters (expect frozen height, latest height, and chain-id) +// - The substitute client is the same type as the subject client +// - The subject and substitute client states match in all parameters (expect frozen height, latest height, and chain-id) // // In case 1) before updating the client, the client will be unfrozen by resetting // the FrozenHeight to the zero Height. func (cs ClientState) CheckSubstituteAndUpdateState( ctx sdk.Context, cdc codec.BinaryCodec, subjectClientStore, substituteClientStore sdk.KVStore, substituteClient exported.ClientState, -) (exported.ClientState, error) { +) error { substituteClientState, ok := substituteClient.(*ClientState) if !ok { - return nil, sdkerrors.Wrapf( - clienttypes.ErrInvalidClient, "expected type %T, got %T", &ClientState{}, substituteClient, - ) + return errorsmod.Wrapf(clienttypes.ErrInvalidClient, "expected type %T, got %T", &ClientState{}, substituteClient) } if !IsMatchingClientState(cs, *substituteClientState) { - return nil, sdkerrors.Wrap(clienttypes.ErrInvalidSubstitute, "subject client state does not match substitute client state") + return errorsmod.Wrap(clienttypes.ErrInvalidSubstitute, "subject client state does not match substitute client state") } if cs.Status(ctx, subjectClientStore, cdc) == exported.Frozen { @@ -48,22 +46,22 @@ func (cs ClientState) CheckSubstituteAndUpdateState( // starting from initial height and ending on the latest height (inclusive) height := substituteClientState.GetLatestHeight() - consensusState, err := GetConsensusState(substituteClientStore, cdc, height) - if err != nil { - return nil, sdkerrors.Wrap(err, "unable to retrieve latest consensus state for substitute client") + consensusState, found := GetConsensusState(substituteClientStore, cdc, height) + if !found { + return errorsmod.Wrap(clienttypes.ErrConsensusStateNotFound, "unable to retrieve latest consensus state for substitute client") } - SetConsensusState(subjectClientStore, cdc, consensusState, height) + setConsensusState(subjectClientStore, cdc, consensusState, height) // set metadata stored for the substitute consensus state processedHeight, found := GetProcessedHeight(substituteClientStore, height) if !found { - return nil, sdkerrors.Wrap(clienttypes.ErrUpdateClientFailed, "unable to retrieve processed height for substitute client latest height") + return errorsmod.Wrap(clienttypes.ErrUpdateClientFailed, "unable to retrieve processed height for substitute client latest height") } processedTime, found := GetProcessedTime(substituteClientStore, height) if !found { - return nil, sdkerrors.Wrap(clienttypes.ErrUpdateClientFailed, "unable to retrieve processed time for substitute client latest height") + return errorsmod.Wrap(clienttypes.ErrUpdateClientFailed, "unable to retrieve processed time for substitute client latest height") } setConsensusMetadataWithValues(subjectClientStore, height, processedHeight, processedTime) @@ -76,8 +74,9 @@ func (cs ClientState) CheckSubstituteAndUpdateState( // no validation is necessary since the substitute is verified to be Active // in 02-client. + setClientState(subjectClientStore, cdc, &cs) - return &cs, nil + return nil } // IsMatchingClientState returns true if all the client state parameters match diff --git a/modules/light-clients/07-tendermint/types/proposal_handle_test.go b/modules/light-clients/07-tendermint/proposal_handle_test.go similarity index 65% rename from modules/light-clients/07-tendermint/types/proposal_handle_test.go rename to modules/light-clients/07-tendermint/proposal_handle_test.go index bdd2d98ead5..e6c3ef7d906 100644 --- a/modules/light-clients/07-tendermint/types/proposal_handle_test.go +++ b/modules/light-clients/07-tendermint/proposal_handle_test.go @@ -1,12 +1,12 @@ -package types_test +package tendermint_test import ( "time" - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v4/modules/core/exported" - "github.com/cosmos/ibc-go/v4/modules/light-clients/07-tendermint/types" - ibctesting "github.com/cosmos/ibc-go/v4/testing" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v7/modules/core/exported" + ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" + ibctesting "github.com/cosmos/ibc-go/v7/testing" ) var frozenHeight = clienttypes.NewHeight(0, 1) @@ -28,13 +28,13 @@ func (suite *TendermintTestSuite) TestCheckSubstituteUpdateStateBasic() { { "non-matching substitute", func() { suite.coordinator.SetupClients(substitutePath) - substituteClientState, ok := suite.chainA.GetClientState(substitutePath.EndpointA.ClientID).(*types.ClientState) + substituteClientState, ok := suite.chainA.GetClientState(substitutePath.EndpointA.ClientID).(*ibctm.ClientState) suite.Require().True(ok) // change trusting period so that test should fail substituteClientState.TrustingPeriod = time.Hour * 24 * 7 tmClientState := substituteClientState - tmClientState.ChainId = tmClientState.ChainId + "different chain" + tmClientState.ChainId += "different chain" }, }, } @@ -48,7 +48,7 @@ func (suite *TendermintTestSuite) TestCheckSubstituteUpdateStateBasic() { substitutePath = ibctesting.NewPath(suite.chainA, suite.chainB) suite.coordinator.SetupClients(subjectPath) - subjectClientState := suite.chainA.GetClientState(subjectPath.EndpointA.ClientID).(*types.ClientState) + subjectClientState := suite.chainA.GetClientState(subjectPath.EndpointA.ClientID).(*ibctm.ClientState) // expire subject client suite.coordinator.IncrementTimeBy(subjectClientState.TrustingPeriod) @@ -59,50 +59,26 @@ func (suite *TendermintTestSuite) TestCheckSubstituteUpdateStateBasic() { subjectClientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), subjectPath.EndpointA.ClientID) substituteClientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), substitutePath.EndpointA.ClientID) - updatedClient, err := subjectClientState.CheckSubstituteAndUpdateState(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), subjectClientStore, substituteClientStore, substituteClientState) + err := subjectClientState.CheckSubstituteAndUpdateState(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), subjectClientStore, substituteClientStore, substituteClientState) suite.Require().Error(err) - suite.Require().Nil(updatedClient) }) } } -// to expire clients, time needs to be fast forwarded on both chainA and chainB. -// this is to prevent headers from failing when attempting to update later. func (suite *TendermintTestSuite) TestCheckSubstituteAndUpdateState() { testCases := []struct { name string FreezeClient bool - ExpireClient bool expPass bool }{ { - name: "PASS: update checks are deprecated, client is frozen and expired", - FreezeClient: true, - ExpireClient: true, - expPass: true, - }, - { - name: "PASS: update checks are deprecated, not frozen or expired", - FreezeClient: false, - ExpireClient: false, - expPass: true, - }, - { - name: "PASS: update checks are deprecated, not frozen or expired", + name: "PASS: update checks are deprecated, client is not frozen", FreezeClient: false, - ExpireClient: false, expPass: true, }, { name: "PASS: update checks are deprecated, client is frozen", FreezeClient: true, - ExpireClient: false, - expPass: true, - }, - { - name: "PASS: update checks are deprecated, client is expired", - FreezeClient: false, - ExpireClient: true, expPass: true, }, } @@ -110,37 +86,23 @@ func (suite *TendermintTestSuite) TestCheckSubstituteAndUpdateState() { for _, tc := range testCases { tc := tc - // for each test case a header used for unexpiring clients and unfreezing - // a client are each tested to ensure that unexpiry headers cannot update - // a client when a unfreezing header is required. suite.Run(tc.name, func() { - // start by testing unexpiring the client suite.SetupTest() // reset // construct subject using test case parameters subjectPath := ibctesting.NewPath(suite.chainA, suite.chainB) suite.coordinator.SetupClients(subjectPath) - subjectClientState := suite.chainA.GetClientState(subjectPath.EndpointA.ClientID).(*types.ClientState) + subjectClientState := suite.chainA.GetClientState(subjectPath.EndpointA.ClientID).(*ibctm.ClientState) - // apply freezing or expiry as determined by the test case if tc.FreezeClient { subjectClientState.FrozenHeight = frozenHeight } - if tc.ExpireClient { - // expire subject client - suite.coordinator.IncrementTimeBy(subjectClientState.TrustingPeriod) - suite.coordinator.CommitBlock(suite.chainA, suite.chainB) - } // construct the substitute to match the subject client - // NOTE: the substitute is explicitly created after the freezing or expiry occurs, - // primarily to prevent the substitute from becoming frozen. It also should be - // the natural flow of events in practice. The subject will become frozen/expired - // and a substitute will be created along with a governance proposal as a response substitutePath := ibctesting.NewPath(suite.chainA, suite.chainB) suite.coordinator.SetupClients(substitutePath) - substituteClientState := suite.chainA.GetClientState(substitutePath.EndpointA.ClientID).(*types.ClientState) + substituteClientState := suite.chainA.GetClientState(substitutePath.EndpointA.ClientID).(*ibctm.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) @@ -154,7 +116,7 @@ func (suite *TendermintTestSuite) TestCheckSubstituteAndUpdateState() { } // get updated substitute - substituteClientState = suite.chainA.GetClientState(substitutePath.EndpointA.ClientID).(*types.ClientState) + substituteClientState = suite.chainA.GetClientState(substitutePath.EndpointA.ClientID).(*ibctm.ClientState) // test that subject gets updated chain-id newChainID := "new-chain-id" @@ -164,39 +126,40 @@ func (suite *TendermintTestSuite) TestCheckSubstituteAndUpdateState() { substituteClientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), substitutePath.EndpointA.ClientID) expectedConsState := substitutePath.EndpointA.GetConsensusState(substituteClientState.GetLatestHeight()) - expectedProcessedTime, found := types.GetProcessedTime(substituteClientStore, substituteClientState.GetLatestHeight()) + expectedProcessedTime, found := ibctm.GetProcessedTime(substituteClientStore, substituteClientState.GetLatestHeight()) suite.Require().True(found) - expectedProcessedHeight, found := types.GetProcessedTime(substituteClientStore, substituteClientState.GetLatestHeight()) + expectedProcessedHeight, found := ibctm.GetProcessedTime(substituteClientStore, substituteClientState.GetLatestHeight()) suite.Require().True(found) - expectedIterationKey := types.GetIterationKey(substituteClientStore, substituteClientState.GetLatestHeight()) + expectedIterationKey := ibctm.GetIterationKey(substituteClientStore, substituteClientState.GetLatestHeight()) - updatedClient, err := subjectClientState.CheckSubstituteAndUpdateState(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), subjectClientStore, substituteClientStore, substituteClientState) + err := subjectClientState.CheckSubstituteAndUpdateState(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), subjectClientStore, substituteClientStore, substituteClientState) if tc.expPass { suite.Require().NoError(err) - suite.Require().Equal(clienttypes.ZeroHeight(), updatedClient.(*types.ClientState).FrozenHeight) + + updatedClient := subjectPath.EndpointA.GetClientState() + suite.Require().Equal(clienttypes.ZeroHeight(), updatedClient.(*ibctm.ClientState).FrozenHeight) subjectClientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), subjectPath.EndpointA.ClientID) // check that the correct consensus state was copied over suite.Require().Equal(substituteClientState.GetLatestHeight(), updatedClient.GetLatestHeight()) subjectConsState := subjectPath.EndpointA.GetConsensusState(updatedClient.GetLatestHeight()) - subjectProcessedTime, found := types.GetProcessedTime(subjectClientStore, updatedClient.GetLatestHeight()) + subjectProcessedTime, found := ibctm.GetProcessedTime(subjectClientStore, updatedClient.GetLatestHeight()) suite.Require().True(found) - subjectProcessedHeight, found := types.GetProcessedTime(substituteClientStore, updatedClient.GetLatestHeight()) + subjectProcessedHeight, found := ibctm.GetProcessedTime(substituteClientStore, updatedClient.GetLatestHeight()) suite.Require().True(found) - subjectIterationKey := types.GetIterationKey(substituteClientStore, updatedClient.GetLatestHeight()) + subjectIterationKey := ibctm.GetIterationKey(substituteClientStore, updatedClient.GetLatestHeight()) suite.Require().Equal(expectedConsState, subjectConsState) suite.Require().Equal(expectedProcessedTime, subjectProcessedTime) suite.Require().Equal(expectedProcessedHeight, subjectProcessedHeight) suite.Require().Equal(expectedIterationKey, subjectIterationKey) - suite.Require().Equal(newChainID, updatedClient.(*types.ClientState).ChainId) - suite.Require().Equal(time.Hour*24*7, updatedClient.(*types.ClientState).TrustingPeriod) + suite.Require().Equal(newChainID, updatedClient.(*ibctm.ClientState).ChainId) + suite.Require().Equal(time.Hour*24*7, updatedClient.(*ibctm.ClientState).TrustingPeriod) } else { suite.Require().Error(err) - suite.Require().Nil(updatedClient) } }) } @@ -205,7 +168,7 @@ func (suite *TendermintTestSuite) TestCheckSubstituteAndUpdateState() { func (suite *TendermintTestSuite) TestIsMatchingClientState() { var ( subjectPath, substitutePath *ibctesting.Path - subjectClientState, substituteClientState *types.ClientState + subjectClientState, substituteClientState *ibctm.ClientState ) testCases := []struct { @@ -215,8 +178,8 @@ func (suite *TendermintTestSuite) TestIsMatchingClientState() { }{ { "matching clients", func() { - subjectClientState = suite.chainA.GetClientState(subjectPath.EndpointA.ClientID).(*types.ClientState) - substituteClientState = suite.chainA.GetClientState(substitutePath.EndpointA.ClientID).(*types.ClientState) + subjectClientState = suite.chainA.GetClientState(subjectPath.EndpointA.ClientID).(*ibctm.ClientState) + substituteClientState = suite.chainA.GetClientState(substitutePath.EndpointA.ClientID).(*ibctm.ClientState) }, true, }, { @@ -239,14 +202,14 @@ func (suite *TendermintTestSuite) TestIsMatchingClientState() { }, { "matching, trusting period is different", func() { - subjectClientState.TrustingPeriod = time.Duration(time.Hour * 10) - substituteClientState.TrustingPeriod = time.Duration(time.Hour * 1) + subjectClientState.TrustingPeriod = time.Hour * 10 + substituteClientState.TrustingPeriod = time.Hour * 1 }, true, }, { "not matching, trust level is different", func() { - subjectClientState.TrustLevel = types.Fraction{2, 3} - substituteClientState.TrustLevel = types.Fraction{1, 3} + subjectClientState.TrustLevel = ibctm.Fraction{2, 3} + substituteClientState.TrustLevel = ibctm.Fraction{1, 3} }, false, }, } @@ -264,7 +227,7 @@ func (suite *TendermintTestSuite) TestIsMatchingClientState() { tc.malleate() - suite.Require().Equal(tc.expPass, types.IsMatchingClientState(*subjectClientState, *substituteClientState)) + suite.Require().Equal(tc.expPass, ibctm.IsMatchingClientState(*subjectClientState, *substituteClientState)) }) } } diff --git a/modules/light-clients/07-tendermint/types/store.go b/modules/light-clients/07-tendermint/store.go similarity index 89% rename from modules/light-clients/07-tendermint/types/store.go rename to modules/light-clients/07-tendermint/store.go index 1c1157f65de..8f992bc5183 100644 --- a/modules/light-clients/07-tendermint/types/store.go +++ b/modules/light-clients/07-tendermint/store.go @@ -1,4 +1,4 @@ -package types +package tendermint import ( "bytes" @@ -8,11 +8,10 @@ import ( "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/store/prefix" sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" - "github.com/cosmos/ibc-go/v4/modules/core/exported" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + "github.com/cosmos/ibc-go/v7/modules/core/exported" ) /* @@ -43,38 +42,30 @@ var ( KeyIteration = []byte("/iterationKey") ) -// SetConsensusState stores the consensus state at the given height. -func SetConsensusState(clientStore sdk.KVStore, cdc codec.BinaryCodec, consensusState *ConsensusState, height exported.Height) { +// setClientState stores the client state +func setClientState(clientStore sdk.KVStore, cdc codec.BinaryCodec, clientState *ClientState) { + key := host.ClientStateKey() + val := clienttypes.MustMarshalClientState(cdc, clientState) + clientStore.Set(key, val) +} + +// setConsensusState stores the consensus state at the given height. +func setConsensusState(clientStore sdk.KVStore, cdc codec.BinaryCodec, consensusState *ConsensusState, height exported.Height) { key := host.ConsensusStateKey(height) val := clienttypes.MustMarshalConsensusState(cdc, consensusState) clientStore.Set(key, val) } -// GetConsensusState retrieves the consensus state from the client prefixed -// store. An error is returned if the consensus state does not exist. -func GetConsensusState(store sdk.KVStore, cdc codec.BinaryCodec, height exported.Height) (*ConsensusState, error) { +// GetConsensusState retrieves the consensus state from the client prefixed store. +// If the ConsensusState does not exist in state for the provided height a nil value and false boolean flag is returned +func GetConsensusState(store sdk.KVStore, cdc codec.BinaryCodec, height exported.Height) (*ConsensusState, bool) { bz := store.Get(host.ConsensusStateKey(height)) - if bz == nil { - return nil, sdkerrors.Wrapf( - clienttypes.ErrConsensusStateNotFound, - "consensus state does not exist for height %s", height, - ) - } - - consensusStateI, err := clienttypes.UnmarshalConsensusState(cdc, bz) - if err != nil { - return nil, sdkerrors.Wrapf(clienttypes.ErrInvalidConsensus, "unmarshal error: %v", err) - } - - consensusState, ok := consensusStateI.(*ConsensusState) - if !ok { - return nil, sdkerrors.Wrapf( - clienttypes.ErrInvalidConsensus, - "invalid consensus type %T, expected %T", consensusState, &ConsensusState{}, - ) + if len(bz) == 0 { + return nil, false } - return consensusState, nil + consensusStateI := clienttypes.MustUnmarshalConsensusState(cdc, bz) + return consensusStateI.(*ConsensusState), true } // deleteConsensusState deletes the consensus state at the given height @@ -109,11 +100,11 @@ func IterateConsensusMetadata(store sdk.KVStore, cb func(key, val []byte) bool) } // iterate over iteration keys - iterator = sdk.KVStorePrefixIterator(store, []byte(KeyIterateConsensusStatePrefix)) + iter := sdk.KVStorePrefixIterator(store, []byte(KeyIterateConsensusStatePrefix)) - defer iterator.Close() - for ; iterator.Valid(); iterator.Next() { - if cb(iterator.Key(), iterator.Value()) { + defer iter.Close() + for ; iter.Valid(); iter.Next() { + if cb(iter.Key(), iter.Value()) { break } } @@ -138,7 +129,7 @@ func SetProcessedTime(clientStore sdk.KVStore, height exported.Height, timeNs ui func GetProcessedTime(clientStore sdk.KVStore, height exported.Height) (uint64, bool) { key := ProcessedTimeKey(height) bz := clientStore.Get(key) - if bz == nil { + if len(bz) == 0 { return 0, false } return sdk.BigEndianToUint64(bz), true @@ -169,7 +160,7 @@ func SetProcessedHeight(clientStore sdk.KVStore, consHeight, processedHeight exp func GetProcessedHeight(clientStore sdk.KVStore, height exported.Height) (exported.Height, bool) { key := ProcessedHeightKey(height) bz := clientStore.Get(key) - if bz == nil { + if len(bz) == 0 { return nil, false } processedHeight, err := clienttypes.ParseHeight(string(bz)) @@ -224,7 +215,7 @@ func GetHeightFromIterationKey(iterKey []byte) exported.Height { // IterateConsensusStateAscending iterates through the consensus states in ascending order. It calls the provided // callback on each height, until stop=true is returned. -func IterateConsensusStateAscending(clientStore sdk.KVStore, cb func(height exported.Height) (stop bool)) error { +func IterateConsensusStateAscending(clientStore sdk.KVStore, cb func(height exported.Height) (stop bool)) { iterator := sdk.KVStorePrefixIterator(clientStore, []byte(KeyIterateConsensusStatePrefix)) defer iterator.Close() @@ -232,10 +223,9 @@ func IterateConsensusStateAscending(clientStore sdk.KVStore, cb func(height expo iterKey := iterator.Key() height := GetHeightFromIterationKey(iterKey) if cb(height) { - return nil + break } } - return nil } // GetNextConsensusState returns the lowest consensus state that is larger than the given height. @@ -283,17 +273,16 @@ func GetPreviousConsensusState(clientStore sdk.KVStore, cdc codec.BinaryCodec, h // PruneAllExpiredConsensusStates iterates over all consensus states for a given // client store. If a consensus state is expired, it is deleted and its metadata -// is deleted. +// is deleted. The number of consensus states pruned is returned. func PruneAllExpiredConsensusStates( ctx sdk.Context, clientStore sdk.KVStore, cdc codec.BinaryCodec, clientState *ClientState, -) (err error) { +) int { var heights []exported.Height pruneCb := func(height exported.Height) bool { - consState, err := GetConsensusState(clientStore, cdc, height) - // this error should never occur - if err != nil { + consState, found := GetConsensusState(clientStore, cdc, height) + if !found { // consensus state should always be found return true } @@ -305,22 +294,19 @@ func PruneAllExpiredConsensusStates( } IterateConsensusStateAscending(clientStore, pruneCb) - if err != nil { - return err - } for _, height := range heights { deleteConsensusState(clientStore, height) deleteConsensusMetadata(clientStore, height) } - return nil + return len(heights) } // Helper function for GetNextConsensusState and GetPreviousConsensusState func getTmConsensusState(clientStore sdk.KVStore, cdc codec.BinaryCodec, key []byte) (*ConsensusState, bool) { bz := clientStore.Get(key) - if bz == nil { + if len(bz) == 0 { return nil, false } diff --git a/modules/light-clients/07-tendermint/types/store_test.go b/modules/light-clients/07-tendermint/store_test.go similarity index 58% rename from modules/light-clients/07-tendermint/types/store_test.go rename to modules/light-clients/07-tendermint/store_test.go index af003ba442d..9aa24613431 100644 --- a/modules/light-clients/07-tendermint/types/store_test.go +++ b/modules/light-clients/07-tendermint/store_test.go @@ -1,16 +1,16 @@ -package types_test +package tendermint_test import ( "math" "time" - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - commitmenttypes "github.com/cosmos/ibc-go/v4/modules/core/23-commitment/types" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" - "github.com/cosmos/ibc-go/v4/modules/core/exported" - solomachinetypes "github.com/cosmos/ibc-go/v4/modules/light-clients/06-solomachine/types" - "github.com/cosmos/ibc-go/v4/modules/light-clients/07-tendermint/types" - ibctesting "github.com/cosmos/ibc-go/v4/testing" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + commitmenttypes "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + "github.com/cosmos/ibc-go/v7/modules/core/exported" + solomachine "github.com/cosmos/ibc-go/v7/modules/light-clients/06-solomachine" + tendermint "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" + ibctesting "github.com/cosmos/ibc-go/v7/testing" ) func (suite *TendermintTestSuite) TestGetConsensusState() { @@ -23,31 +23,32 @@ func (suite *TendermintTestSuite) TestGetConsensusState() { name string malleate func() expPass bool + expPanic bool }{ { - "success", func() {}, true, + "success", func() {}, true, false, }, { "consensus state not found", func() { // use height with no consensus state set height = height.(clienttypes.Height).Increment() - }, false, + }, false, false, }, { "not a consensus state interface", func() { // marshal an empty client state and set as consensus state store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) - clientStateBz := suite.chainA.App.GetIBCKeeper().ClientKeeper.MustMarshalClientState(&types.ClientState{}) + clientStateBz := suite.chainA.App.GetIBCKeeper().ClientKeeper.MustMarshalClientState(&tendermint.ClientState{}) store.Set(host.ConsensusStateKey(height), clientStateBz) - }, false, + }, false, true, }, { "invalid consensus state (solomachine)", func() { // marshal and set solomachine consensus state store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) - consensusStateBz := suite.chainA.App.GetIBCKeeper().ClientKeeper.MustMarshalConsensusState(&solomachinetypes.ConsensusState{}) + consensusStateBz := suite.chainA.App.GetIBCKeeper().ClientKeeper.MustMarshalConsensusState(&solomachine.ConsensusState{}) store.Set(host.ConsensusStateKey(height), consensusStateBz) - }, false, + }, false, true, }, } @@ -64,16 +65,26 @@ func (suite *TendermintTestSuite) TestGetConsensusState() { tc.malleate() // change vars as necessary + if tc.expPanic { + suite.Require().Panics(func() { + store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) + tendermint.GetConsensusState(store, suite.chainA.Codec, height) + }) + + return + } + store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) - consensusState, err := types.GetConsensusState(store, suite.chainA.Codec, height) + consensusState, found := tendermint.GetConsensusState(store, suite.chainA.Codec, height) if tc.expPass { - suite.Require().NoError(err) + suite.Require().True(found) + expConsensusState, found := suite.chainA.GetConsensusState(path.EndpointA.ClientID, height) suite.Require().True(found) suite.Require().Equal(expConsensusState, consensusState) } else { - suite.Require().Error(err) + suite.Require().False(found) suite.Require().Nil(consensusState) } }) @@ -96,7 +107,7 @@ func (suite *TendermintTestSuite) TestGetProcessedTime() { height := clientState.GetLatestHeight() store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) - actualTime, ok := types.GetProcessedTime(store, height) + actualTime, ok := tendermint.GetProcessedTime(store, height) suite.Require().True(ok, "could not retrieve processed time for stored consensus state") suite.Require().Equal(uint64(expectedTime.UnixNano()), actualTime, "retrieved processed time is not expected value") @@ -112,12 +123,12 @@ func (suite *TendermintTestSuite) TestGetProcessedTime() { height = clientState.GetLatestHeight() store = suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) - actualTime, ok = types.GetProcessedTime(store, height) + actualTime, ok = tendermint.GetProcessedTime(store, height) suite.Require().True(ok, "could not retrieve processed time for stored consensus state") suite.Require().Equal(uint64(expectedTime.UnixNano()), actualTime, "retrieved processed time is not expected value") // try to get processed time for height that doesn't exist in store - _, ok = types.GetProcessedTime(store, clienttypes.NewHeight(1, 1)) + _, ok = tendermint.GetProcessedTime(store, clienttypes.NewHeight(1, 1)) suite.Require().False(ok, "retrieved processed time for a non-existent consensus state") } @@ -129,8 +140,8 @@ func (suite *TendermintTestSuite) TestIterationKey() { clienttypes.NewHeight(math.MaxUint64, math.MaxUint64), } for _, h := range testHeights { - k := types.IterationKey(h) - retrievedHeight := types.GetHeightFromIterationKey(k) + k := tendermint.IterationKey(h) + retrievedHeight := tendermint.GetHeightFromIterationKey(k) suite.Require().Equal(h, retrievedHeight, "retrieving height from iteration key failed") } } @@ -139,16 +150,16 @@ func (suite *TendermintTestSuite) TestIterateConsensusStates() { nextValsHash := []byte("nextVals") // Set iteration keys and consensus states - types.SetIterationKey(suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), "testClient"), clienttypes.NewHeight(0, 1)) - suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(suite.chainA.GetContext(), "testClient", clienttypes.NewHeight(0, 1), types.NewConsensusState(time.Now(), commitmenttypes.NewMerkleRoot([]byte("hash0-1")), nextValsHash)) - types.SetIterationKey(suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), "testClient"), clienttypes.NewHeight(4, 9)) - suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(suite.chainA.GetContext(), "testClient", clienttypes.NewHeight(4, 9), types.NewConsensusState(time.Now(), commitmenttypes.NewMerkleRoot([]byte("hash4-9")), nextValsHash)) - types.SetIterationKey(suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), "testClient"), clienttypes.NewHeight(0, 10)) - suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(suite.chainA.GetContext(), "testClient", clienttypes.NewHeight(0, 10), types.NewConsensusState(time.Now(), commitmenttypes.NewMerkleRoot([]byte("hash0-10")), nextValsHash)) - types.SetIterationKey(suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), "testClient"), clienttypes.NewHeight(0, 4)) - suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(suite.chainA.GetContext(), "testClient", clienttypes.NewHeight(0, 4), types.NewConsensusState(time.Now(), commitmenttypes.NewMerkleRoot([]byte("hash0-4")), nextValsHash)) - types.SetIterationKey(suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), "testClient"), clienttypes.NewHeight(40, 1)) - suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(suite.chainA.GetContext(), "testClient", clienttypes.NewHeight(40, 1), types.NewConsensusState(time.Now(), commitmenttypes.NewMerkleRoot([]byte("hash40-1")), nextValsHash)) + tendermint.SetIterationKey(suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), "testClient"), clienttypes.NewHeight(0, 1)) + suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(suite.chainA.GetContext(), "testClient", clienttypes.NewHeight(0, 1), tendermint.NewConsensusState(time.Now(), commitmenttypes.NewMerkleRoot([]byte("hash0-1")), nextValsHash)) + tendermint.SetIterationKey(suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), "testClient"), clienttypes.NewHeight(4, 9)) + suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(suite.chainA.GetContext(), "testClient", clienttypes.NewHeight(4, 9), tendermint.NewConsensusState(time.Now(), commitmenttypes.NewMerkleRoot([]byte("hash4-9")), nextValsHash)) + tendermint.SetIterationKey(suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), "testClient"), clienttypes.NewHeight(0, 10)) + suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(suite.chainA.GetContext(), "testClient", clienttypes.NewHeight(0, 10), tendermint.NewConsensusState(time.Now(), commitmenttypes.NewMerkleRoot([]byte("hash0-10")), nextValsHash)) + tendermint.SetIterationKey(suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), "testClient"), clienttypes.NewHeight(0, 4)) + suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(suite.chainA.GetContext(), "testClient", clienttypes.NewHeight(0, 4), tendermint.NewConsensusState(time.Now(), commitmenttypes.NewMerkleRoot([]byte("hash0-4")), nextValsHash)) + tendermint.SetIterationKey(suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), "testClient"), clienttypes.NewHeight(40, 1)) + suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(suite.chainA.GetContext(), "testClient", clienttypes.NewHeight(40, 1), tendermint.NewConsensusState(time.Now(), commitmenttypes.NewMerkleRoot([]byte("hash40-1")), nextValsHash)) var testArr []string cb := func(height exported.Height) bool { @@ -156,39 +167,39 @@ func (suite *TendermintTestSuite) TestIterateConsensusStates() { return false } - types.IterateConsensusStateAscending(suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), "testClient"), cb) + tendermint.IterateConsensusStateAscending(suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), "testClient"), cb) expectedArr := []string{"0-1", "0-4", "0-10", "4-9", "40-1"} suite.Require().Equal(expectedArr, testArr) } func (suite *TendermintTestSuite) TestGetNeighboringConsensusStates() { nextValsHash := []byte("nextVals") - cs01 := types.NewConsensusState(time.Now().UTC(), commitmenttypes.NewMerkleRoot([]byte("hash0-1")), nextValsHash) - cs04 := types.NewConsensusState(time.Now().UTC(), commitmenttypes.NewMerkleRoot([]byte("hash0-4")), nextValsHash) - cs49 := types.NewConsensusState(time.Now().UTC(), commitmenttypes.NewMerkleRoot([]byte("hash4-9")), nextValsHash) + cs01 := tendermint.NewConsensusState(time.Now().UTC(), commitmenttypes.NewMerkleRoot([]byte("hash0-1")), nextValsHash) + cs04 := tendermint.NewConsensusState(time.Now().UTC(), commitmenttypes.NewMerkleRoot([]byte("hash0-4")), nextValsHash) + cs49 := tendermint.NewConsensusState(time.Now().UTC(), commitmenttypes.NewMerkleRoot([]byte("hash4-9")), nextValsHash) height01 := clienttypes.NewHeight(0, 1) height04 := clienttypes.NewHeight(0, 4) height49 := clienttypes.NewHeight(4, 9) // Set iteration keys and consensus states - types.SetIterationKey(suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), "testClient"), height01) + tendermint.SetIterationKey(suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), "testClient"), height01) suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(suite.chainA.GetContext(), "testClient", height01, cs01) - types.SetIterationKey(suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), "testClient"), height04) + tendermint.SetIterationKey(suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), "testClient"), height04) suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(suite.chainA.GetContext(), "testClient", height04, cs04) - types.SetIterationKey(suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), "testClient"), height49) + tendermint.SetIterationKey(suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), "testClient"), height49) suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(suite.chainA.GetContext(), "testClient", height49, cs49) - prevCs01, ok := types.GetPreviousConsensusState(suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), "testClient"), suite.chainA.Codec, height01) + prevCs01, ok := tendermint.GetPreviousConsensusState(suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), "testClient"), suite.chainA.Codec, height01) suite.Require().Nil(prevCs01, "consensus state exists before lowest consensus state") suite.Require().False(ok) - prevCs49, ok := types.GetPreviousConsensusState(suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), "testClient"), suite.chainA.Codec, height49) + prevCs49, ok := tendermint.GetPreviousConsensusState(suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), "testClient"), suite.chainA.Codec, height49) suite.Require().Equal(cs04, prevCs49, "previous consensus state is not returned correctly") suite.Require().True(ok) - nextCs01, ok := types.GetNextConsensusState(suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), "testClient"), suite.chainA.Codec, height01) + nextCs01, ok := tendermint.GetNextConsensusState(suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), "testClient"), suite.chainA.Codec, height01) suite.Require().Equal(cs04, nextCs01, "next consensus state not returned correctly") suite.Require().True(ok) - nextCs49, ok := types.GetNextConsensusState(suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), "testClient"), suite.chainA.Codec, height49) + nextCs49, ok := tendermint.GetNextConsensusState(suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), "testClient"), suite.chainA.Codec, height49) suite.Require().Nil(nextCs49, "next consensus state exists after highest consensus state") suite.Require().False(ok) } diff --git a/modules/light-clients/07-tendermint/types/tendermint.pb.go b/modules/light-clients/07-tendermint/tendermint.pb.go similarity index 84% rename from modules/light-clients/07-tendermint/types/tendermint.pb.go rename to modules/light-clients/07-tendermint/tendermint.pb.go index 3c34c10a9ad..dcff8304099 100644 --- a/modules/light-clients/07-tendermint/types/tendermint.pb.go +++ b/modules/light-clients/07-tendermint/tendermint.pb.go @@ -1,16 +1,16 @@ // Code generated by protoc-gen-gogo. DO NOT EDIT. // source: ibc/lightclients/tendermint/v1/tendermint.proto -package types +package tendermint import ( fmt "fmt" - _go "github.com/confio/ics23/go" - types "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - types1 "github.com/cosmos/ibc-go/v4/modules/core/23-commitment/types" - _ "github.com/gogo/protobuf/gogoproto" - proto "github.com/gogo/protobuf/proto" - github_com_gogo_protobuf_types "github.com/gogo/protobuf/types" + _ "github.com/cosmos/gogoproto/gogoproto" + proto "github.com/cosmos/gogoproto/proto" + github_com_cosmos_gogoproto_types "github.com/cosmos/gogoproto/types" + types "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + types1 "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/types" + _go "github.com/cosmos/ics23/go" github_com_tendermint_tendermint_libs_bytes "github.com/tendermint/tendermint/libs/bytes" types2 "github.com/tendermint/tendermint/proto/tendermint/types" _ "google.golang.org/protobuf/types/known/durationpb" @@ -144,7 +144,8 @@ var xxx_messageInfo_ConsensusState proto.InternalMessageInfo // Misbehaviour is a wrapper over two conflicting Headers // that implements Misbehaviour interface expected by ICS-02 type Misbehaviour struct { - ClientId string `protobuf:"bytes,1,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty" yaml:"client_id"` + // ClientID is deprecated + ClientId string `protobuf:"bytes,1,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty" yaml:"client_id"` // Deprecated: Do not use. Header1 *Header `protobuf:"bytes,2,opt,name=header_1,json=header1,proto3" json:"header_1,omitempty" yaml:"header_1"` Header2 *Header `protobuf:"bytes,3,opt,name=header_2,json=header2,proto3" json:"header_2,omitempty" yaml:"header_2"` } @@ -322,75 +323,76 @@ func init() { } var fileDescriptor_c6d6cf2b288949be = []byte{ - // 1082 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x56, 0xcf, 0x6f, 0xe3, 0xc4, - 0x17, 0x6f, 0xda, 0x7e, 0xb7, 0xc9, 0x24, 0xdd, 0xf6, 0x6b, 0x4a, 0x37, 0x2d, 0xdd, 0x38, 0x32, - 0x52, 0xc9, 0x81, 0xda, 0x24, 0xbb, 0x12, 0x52, 0xc5, 0x05, 0x77, 0x17, 0xb5, 0x88, 0x95, 0x2a, - 0x97, 0x1f, 0x12, 0x12, 0x32, 0x13, 0x7b, 0x92, 0x8c, 0xd6, 0xf6, 0x58, 0x9e, 0x49, 0x68, 0xf9, - 0x0b, 0xe0, 0x80, 0xb4, 0x47, 0xc4, 0x89, 0x03, 0x7f, 0xcc, 0x1e, 0x7b, 0xe4, 0x64, 0x50, 0x7b, - 0xe5, 0x94, 0x23, 0x27, 0x34, 0x3f, 0x6c, 0x4f, 0xb3, 0x5d, 0xaa, 0xe5, 0x12, 0xcd, 0x7b, 0xef, - 0xf3, 0x3e, 0x9f, 0xcc, 0x9b, 0x37, 0x6f, 0x0c, 0x1c, 0x3c, 0x0c, 0x9c, 0x08, 0x8f, 0x27, 0x2c, - 0x88, 0x30, 0x4a, 0x18, 0x75, 0x18, 0x4a, 0x42, 0x94, 0xc5, 0x38, 0x61, 0xce, 0xac, 0xaf, 0x59, - 0x76, 0x9a, 0x11, 0x46, 0x8c, 0x0e, 0x1e, 0x06, 0xb6, 0x9e, 0x60, 0x6b, 0x90, 0x59, 0x7f, 0xb7, - 0xab, 0xe5, 0xb3, 0x8b, 0x14, 0x51, 0x67, 0x06, 0x23, 0x1c, 0x42, 0x46, 0x32, 0xc9, 0xb0, 0xbb, - 0xf7, 0x0a, 0x42, 0xfc, 0xaa, 0x68, 0x2b, 0xcd, 0x08, 0x19, 0x15, 0x56, 0x67, 0x4c, 0xc8, 0x38, - 0x42, 0x8e, 0xb0, 0x86, 0xd3, 0x91, 0x13, 0x4e, 0x33, 0xc8, 0x30, 0x49, 0x54, 0xdc, 0x5c, 0x8c, - 0x33, 0x1c, 0x23, 0xca, 0x60, 0x9c, 0x16, 0x00, 0xbe, 0xbf, 0x80, 0x64, 0xc8, 0x91, 0x7f, 0x97, - 0xef, 0x49, 0xae, 0x14, 0xe0, 0xbd, 0x0a, 0x40, 0xe2, 0x18, 0xb3, 0xb8, 0x00, 0x95, 0x96, 0x02, - 0x6e, 0x8d, 0xc9, 0x98, 0x88, 0xa5, 0xc3, 0x57, 0xd2, 0x6b, 0xfd, 0xb5, 0x06, 0x9a, 0x47, 0x82, - 0xef, 0x8c, 0x41, 0x86, 0x8c, 0x1d, 0x50, 0x0f, 0x26, 0x10, 0x27, 0x3e, 0x0e, 0xdb, 0xb5, 0x6e, - 0xad, 0xd7, 0xf0, 0xd6, 0x84, 0x7d, 0x12, 0x1a, 0x08, 0x34, 0x59, 0x36, 0xa5, 0xcc, 0x8f, 0xd0, - 0x0c, 0x45, 0xed, 0xe5, 0x6e, 0xad, 0xd7, 0x1c, 0xf4, 0xec, 0x7f, 0xaf, 0xa7, 0xfd, 0x49, 0x06, - 0x03, 0xbe, 0x61, 0x77, 0xf7, 0x65, 0x6e, 0x2e, 0xcd, 0x73, 0xd3, 0xb8, 0x80, 0x71, 0x74, 0x68, - 0x69, 0x54, 0x96, 0x07, 0x84, 0xf5, 0x19, 0x37, 0x8c, 0x11, 0xd8, 0x10, 0x16, 0x4e, 0xc6, 0x7e, - 0x8a, 0x32, 0x4c, 0xc2, 0xf6, 0x8a, 0x90, 0xda, 0xb1, 0x65, 0xb1, 0xec, 0xa2, 0x58, 0xf6, 0x13, - 0x55, 0x4c, 0xd7, 0x52, 0xdc, 0xdb, 0x1a, 0x77, 0x95, 0x6f, 0xfd, 0xfc, 0x87, 0x59, 0xf3, 0xee, - 0x17, 0xde, 0x53, 0xe1, 0x34, 0x30, 0xd8, 0x9c, 0x26, 0x43, 0x92, 0x84, 0x9a, 0xd0, 0xea, 0x5d, - 0x42, 0xef, 0x2a, 0xa1, 0x07, 0x52, 0x68, 0x91, 0x40, 0x2a, 0x6d, 0x94, 0x6e, 0x25, 0x85, 0xc0, - 0x46, 0x0c, 0xcf, 0xfd, 0x20, 0x22, 0xc1, 0x73, 0x3f, 0xcc, 0xf0, 0x88, 0xb5, 0xff, 0xf7, 0x86, - 0x5b, 0x5a, 0xc8, 0x97, 0x42, 0xeb, 0x31, 0x3c, 0x3f, 0xe2, 0xce, 0x27, 0xdc, 0x67, 0x7c, 0x03, - 0xd6, 0x47, 0x19, 0xf9, 0x1e, 0x25, 0xfe, 0x04, 0xf1, 0x03, 0x69, 0xdf, 0x13, 0x22, 0xbb, 0xe2, - 0x88, 0x78, 0x8b, 0xd8, 0xaa, 0x73, 0x66, 0x7d, 0xfb, 0x58, 0x20, 0xdc, 0x3d, 0xa5, 0xb2, 0x25, - 0x55, 0x6e, 0xa4, 0x5b, 0x5e, 0x4b, 0xda, 0x12, 0xcb, 0xe9, 0x23, 0xc8, 0x10, 0x65, 0x05, 0xfd, - 0xda, 0x9b, 0xd2, 0xdf, 0x48, 0xb7, 0xbc, 0x96, 0xb4, 0x15, 0xfd, 0x09, 0x68, 0x8a, 0xab, 0xe3, - 0xd3, 0x14, 0x05, 0xb4, 0x5d, 0xef, 0xae, 0xf4, 0x9a, 0x83, 0x4d, 0x1b, 0x07, 0x74, 0xf0, 0xc8, - 0x3e, 0xe5, 0x91, 0xb3, 0x14, 0x05, 0xee, 0x76, 0xd5, 0x42, 0x1a, 0xdc, 0xf2, 0x40, 0x5a, 0x40, - 0xa8, 0x71, 0x08, 0x5a, 0xd3, 0x74, 0x9c, 0xc1, 0x10, 0xf9, 0x29, 0x64, 0x93, 0x76, 0xa3, 0xbb, - 0xd2, 0x6b, 0xb8, 0x0f, 0xe6, 0xb9, 0xf9, 0x96, 0x3a, 0x37, 0x2d, 0x6a, 0x79, 0x4d, 0x65, 0x9e, - 0x42, 0x36, 0x31, 0x20, 0xd8, 0x81, 0x51, 0x44, 0xbe, 0xf3, 0xa7, 0x69, 0x08, 0x19, 0xf2, 0xe1, - 0x88, 0xa1, 0xcc, 0x47, 0xe7, 0x29, 0xce, 0x2e, 0xda, 0xa0, 0x5b, 0xeb, 0xd5, 0xdd, 0xfd, 0x79, - 0x6e, 0x76, 0x25, 0xd1, 0x6b, 0xa1, 0x56, 0xbb, 0xe6, 0x6d, 0x8b, 0xe8, 0x17, 0x22, 0xf8, 0x31, - 0x8f, 0x3d, 0x15, 0x21, 0x83, 0x02, 0xf3, 0x96, 0xbc, 0x18, 0xd3, 0x21, 0x9a, 0xc0, 0x19, 0x26, - 0xd3, 0xac, 0xdd, 0x14, 0x42, 0xef, 0xcf, 0x73, 0x73, 0xff, 0xb5, 0x42, 0x7a, 0x02, 0x97, 0xdb, - 0x5b, 0x94, 0x7b, 0xa6, 0x01, 0x0e, 0x57, 0x7f, 0xf8, 0xd5, 0x5c, 0xb2, 0x7e, 0x5b, 0x06, 0xf7, - 0x8f, 0x48, 0x42, 0x51, 0x42, 0xa7, 0x54, 0xde, 0x78, 0x17, 0x34, 0xca, 0xa1, 0x23, 0xae, 0x3c, - 0x3f, 0xd2, 0xc5, 0xb6, 0xfc, 0xbc, 0x40, 0xb8, 0x75, 0x7e, 0xa4, 0x2f, 0x78, 0xf7, 0x55, 0x69, - 0xc6, 0x47, 0x60, 0x35, 0x23, 0x84, 0xa9, 0x99, 0x60, 0x69, 0x1d, 0x51, 0x4d, 0xa1, 0x59, 0xdf, - 0x7e, 0x86, 0xb2, 0xe7, 0x11, 0xf2, 0x08, 0x61, 0xee, 0x2a, 0xa7, 0xf1, 0x44, 0x96, 0xf1, 0x63, - 0x0d, 0x6c, 0x25, 0xe8, 0x9c, 0xf9, 0xe5, 0xa4, 0xa5, 0xfe, 0x04, 0xd2, 0x89, 0xb8, 0xf7, 0x2d, - 0xf7, 0xab, 0x79, 0x6e, 0xbe, 0x23, 0xab, 0x70, 0x1b, 0xca, 0xfa, 0x3b, 0x37, 0x1f, 0x8f, 0x31, - 0x9b, 0x4c, 0x87, 0x5c, 0x4e, 0x9f, 0xff, 0xda, 0x32, 0xc2, 0x43, 0xea, 0x0c, 0x2f, 0x18, 0xa2, - 0xf6, 0x31, 0x3a, 0x77, 0xf9, 0xc2, 0x33, 0x38, 0xdd, 0x97, 0x25, 0xdb, 0x31, 0xa4, 0x13, 0x55, - 0xa6, 0x9f, 0x96, 0x41, 0x4b, 0xaf, 0x9e, 0xd1, 0x07, 0x0d, 0xd9, 0xdc, 0xe5, 0x5c, 0x74, 0xb7, - 0xe6, 0xb9, 0xb9, 0x29, 0xff, 0x56, 0x19, 0xb2, 0xbc, 0xba, 0x5c, 0x9f, 0x84, 0x06, 0x04, 0xf5, - 0x09, 0x82, 0x21, 0xca, 0xfc, 0xbe, 0xaa, 0xcb, 0xfe, 0x5d, 0xb3, 0xf2, 0x58, 0xe0, 0xdd, 0xce, - 0x55, 0x6e, 0xae, 0xc9, 0x75, 0x7f, 0x9e, 0x9b, 0x1b, 0x52, 0xa4, 0x20, 0xb3, 0xbc, 0x35, 0xb9, - 0xec, 0x6b, 0x12, 0x03, 0x35, 0x23, 0xff, 0x83, 0xc4, 0xe0, 0x15, 0x89, 0x41, 0x29, 0x31, 0x50, - 0xf5, 0xf8, 0x65, 0x05, 0xdc, 0x93, 0x68, 0x03, 0x82, 0x75, 0x8a, 0xc7, 0x09, 0x0a, 0x7d, 0x09, - 0x51, 0x2d, 0xd3, 0xd1, 0x75, 0xe4, 0x7b, 0x78, 0x26, 0x60, 0x4a, 0x70, 0xef, 0x32, 0x37, 0x6b, - 0xd5, 0x24, 0xb8, 0x41, 0x61, 0x79, 0x2d, 0xaa, 0x61, 0xf9, 0xa0, 0x29, 0xcf, 0xd8, 0xa7, 0xa8, - 0x68, 0xab, 0x5b, 0x24, 0xca, 0xc3, 0x3b, 0x43, 0xcc, 0x6d, 0x57, 0xf4, 0x37, 0xd2, 0x2d, 0xaf, - 0x35, 0xd3, 0x70, 0xc6, 0xb7, 0x40, 0x3e, 0x05, 0x42, 0x5f, 0x0c, 0xb2, 0x95, 0x3b, 0x07, 0xd9, - 0x43, 0x35, 0xc8, 0xde, 0xd6, 0x1e, 0x98, 0x32, 0xdf, 0xf2, 0xd6, 0x95, 0x43, 0x8d, 0xb2, 0x08, - 0x18, 0x05, 0xa2, 0x6a, 0x56, 0xf5, 0xb8, 0xdc, 0xb5, 0x8b, 0x87, 0xf3, 0xdc, 0xdc, 0xb9, 0xa9, - 0x52, 0x71, 0x58, 0xde, 0xff, 0x95, 0xb3, 0x6a, 0x5b, 0xeb, 0x53, 0x50, 0x2f, 0x1e, 0x59, 0x63, - 0x0f, 0x34, 0x92, 0x69, 0x8c, 0x32, 0x1e, 0x11, 0x27, 0xb3, 0xea, 0x55, 0x0e, 0xa3, 0x0b, 0x9a, - 0x21, 0x4a, 0x48, 0x8c, 0x13, 0x11, 0x5f, 0x16, 0x71, 0xdd, 0xe5, 0xfa, 0x2f, 0xaf, 0x3a, 0xb5, - 0xcb, 0xab, 0x4e, 0xed, 0xcf, 0xab, 0x4e, 0xed, 0xc5, 0x75, 0x67, 0xe9, 0xf2, 0xba, 0xb3, 0xf4, - 0xfb, 0x75, 0x67, 0xe9, 0xeb, 0xa7, 0xda, 0x15, 0x0b, 0x08, 0x8d, 0x09, 0xe5, 0x9f, 0x5e, 0x07, - 0x63, 0xe2, 0xcc, 0x1e, 0x3b, 0x31, 0x09, 0xa7, 0x11, 0xa2, 0xf2, 0x43, 0xec, 0xa0, 0xf8, 0x12, - 0xfb, 0xe0, 0xc3, 0x83, 0xc5, 0x4f, 0xa5, 0xe1, 0x3d, 0x31, 0x52, 0x1e, 0xfd, 0x13, 0x00, 0x00, - 0xff, 0xff, 0x0b, 0x9f, 0x2c, 0x5c, 0xb8, 0x09, 0x00, 0x00, + // 1089 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x56, 0x4f, 0x6f, 0xe3, 0x44, + 0x14, 0x6f, 0xda, 0xb2, 0x4d, 0x26, 0xe9, 0x76, 0x31, 0xa5, 0x9b, 0x96, 0x6e, 0x1c, 0x19, 0xa9, + 0xf4, 0x40, 0x6d, 0x92, 0x22, 0xad, 0x54, 0xb8, 0xe0, 0x2e, 0xa8, 0xbb, 0x62, 0xa5, 0xca, 0xe5, + 0x8f, 0x84, 0x84, 0xcc, 0xc4, 0x9e, 0x24, 0xa3, 0xb5, 0x3d, 0x96, 0x67, 0x1c, 0x5a, 0x3e, 0x01, + 0x9c, 0xd8, 0x23, 0xe2, 0xc4, 0x81, 0x0f, 0xb3, 0xc7, 0x1e, 0x39, 0x19, 0xd4, 0x7e, 0x83, 0x1c, + 0x38, 0x70, 0x42, 0xf3, 0xc7, 0xf6, 0xb4, 0xdb, 0xa5, 0x5a, 0x2e, 0xd1, 0xbc, 0xf7, 0x7e, 0xef, + 0xf7, 0xcb, 0xbc, 0x79, 0xf3, 0xc6, 0xc0, 0xc1, 0xa3, 0xc0, 0x89, 0xf0, 0x64, 0xca, 0x82, 0x08, + 0xa3, 0x84, 0x51, 0x87, 0xa1, 0x24, 0x44, 0x59, 0x8c, 0x13, 0xe6, 0xcc, 0x06, 0x9a, 0x65, 0xa7, + 0x19, 0x61, 0xc4, 0xe8, 0xe1, 0x51, 0x60, 0xeb, 0x09, 0xb6, 0x06, 0x99, 0x0d, 0xb6, 0xfa, 0x5a, + 0x3e, 0x3b, 0x4b, 0x11, 0x75, 0x66, 0x30, 0xc2, 0x21, 0x64, 0x24, 0x93, 0x0c, 0x5b, 0xdb, 0x2f, + 0x21, 0xc4, 0x6f, 0x19, 0x0d, 0x08, 0x8d, 0x09, 0x75, 0x70, 0x40, 0x87, 0xfb, 0xfc, 0x1f, 0xa4, + 0x19, 0x21, 0xe3, 0x32, 0xda, 0x9b, 0x10, 0x32, 0x89, 0x90, 0x23, 0xac, 0x51, 0x3e, 0x76, 0xc2, + 0x3c, 0x83, 0x0c, 0x93, 0x44, 0xc5, 0xcd, 0xeb, 0x71, 0x86, 0x63, 0x44, 0x19, 0x8c, 0xd3, 0x12, + 0xc0, 0xf7, 0x1b, 0x90, 0x0c, 0x39, 0xf2, 0xef, 0x73, 0x05, 0xb9, 0x52, 0x80, 0xf7, 0x6a, 0x00, + 0x89, 0x63, 0xcc, 0xe2, 0x12, 0x54, 0x59, 0x0a, 0xb8, 0x3e, 0x21, 0x13, 0x22, 0x96, 0x0e, 0x5f, + 0x49, 0xaf, 0xf5, 0xf7, 0x0a, 0x68, 0x1f, 0x0a, 0xbe, 0x13, 0x06, 0x19, 0x32, 0x36, 0x41, 0x33, + 0x98, 0x42, 0x9c, 0xf8, 0x38, 0xec, 0x36, 0xfa, 0x8d, 0xdd, 0x96, 0xb7, 0x22, 0xec, 0xc7, 0xa1, + 0x81, 0x40, 0x9b, 0x65, 0x39, 0x65, 0x7e, 0x84, 0x66, 0x28, 0xea, 0x2e, 0xf6, 0x1b, 0xbb, 0xed, + 0xe1, 0xae, 0xfd, 0xdf, 0xf5, 0xb5, 0x3f, 0xcb, 0x60, 0xc0, 0x37, 0xec, 0x6e, 0xbd, 0x28, 0xcc, + 0x85, 0x79, 0x61, 0x1a, 0x67, 0x30, 0x8e, 0x0e, 0x2c, 0x8d, 0xca, 0xf2, 0x80, 0xb0, 0x3e, 0xe7, + 0x86, 0x31, 0x06, 0x6b, 0xc2, 0xc2, 0xc9, 0xc4, 0x4f, 0x51, 0x86, 0x49, 0xd8, 0x5d, 0x12, 0x52, + 0x9b, 0xb6, 0x2c, 0x96, 0x5d, 0x16, 0xcb, 0x7e, 0xa4, 0x8a, 0xe9, 0x5a, 0x8a, 0x7b, 0x43, 0xe3, + 0xae, 0xf3, 0xad, 0x5f, 0xfe, 0x34, 0x1b, 0xde, 0xdd, 0xd2, 0x7b, 0x2c, 0x9c, 0x06, 0x06, 0xf7, + 0xf2, 0x64, 0x44, 0x92, 0x50, 0x13, 0x5a, 0xbe, 0x4d, 0xe8, 0x5d, 0x25, 0x74, 0x5f, 0x0a, 0x5d, + 0x27, 0x90, 0x4a, 0x6b, 0x95, 0x5b, 0x49, 0x21, 0xb0, 0x16, 0xc3, 0x53, 0x3f, 0x88, 0x48, 0xf0, + 0xcc, 0x0f, 0x33, 0x3c, 0x66, 0xdd, 0x37, 0x5e, 0x73, 0x4b, 0xd7, 0xf2, 0xa5, 0xd0, 0x6a, 0x0c, + 0x4f, 0x0f, 0xb9, 0xf3, 0x11, 0xf7, 0x19, 0xdf, 0x82, 0xd5, 0x71, 0x46, 0x7e, 0x40, 0x89, 0x3f, + 0x45, 0xfc, 0x40, 0xba, 0x77, 0x84, 0xc8, 0x96, 0x38, 0x22, 0xde, 0x22, 0xb6, 0xea, 0x9c, 0xd9, + 0xc0, 0x3e, 0x12, 0x08, 0x77, 0x5b, 0xa9, 0xac, 0x4b, 0x95, 0x2b, 0xe9, 0x96, 0xd7, 0x91, 0xb6, + 0xc4, 0x72, 0xfa, 0x08, 0x32, 0x44, 0x59, 0x49, 0xbf, 0xf2, 0xba, 0xf4, 0x57, 0xd2, 0x2d, 0xaf, + 0x23, 0x6d, 0x45, 0x7f, 0x02, 0xda, 0xe2, 0xea, 0xf8, 0x34, 0x45, 0x01, 0xed, 0x36, 0xfb, 0x4b, + 0x82, 0x5c, 0x5e, 0x2f, 0x5b, 0x5c, 0x2f, 0xce, 0x7c, 0xcc, 0x31, 0x27, 0x29, 0x0a, 0xdc, 0x8d, + 0xba, 0x99, 0xb4, 0x44, 0xcb, 0x03, 0x69, 0x09, 0xa1, 0xc6, 0x01, 0xe8, 0xe4, 0xe9, 0x24, 0x83, + 0x21, 0xf2, 0x53, 0xc8, 0xa6, 0xdd, 0x56, 0x7f, 0x69, 0xb7, 0xe5, 0xde, 0x9f, 0x17, 0xe6, 0x5b, + 0xea, 0x04, 0xb5, 0xa8, 0xe5, 0xb5, 0x95, 0x79, 0x0c, 0xd9, 0xd4, 0x80, 0x60, 0x13, 0x46, 0x11, + 0xf9, 0xde, 0xcf, 0xd3, 0x10, 0x32, 0xe4, 0xc3, 0x31, 0x43, 0x99, 0x8f, 0x4e, 0x53, 0x9c, 0x9d, + 0x75, 0x41, 0xbf, 0xb1, 0xdb, 0x74, 0x77, 0xe6, 0x85, 0xd9, 0x97, 0x44, 0xaf, 0x84, 0x5a, 0xdd, + 0x86, 0xb7, 0x21, 0xa2, 0x5f, 0x8a, 0xe0, 0x27, 0x3c, 0xf6, 0xa9, 0x08, 0x19, 0x14, 0x98, 0x37, + 0xe4, 0xc5, 0x98, 0x8e, 0xd0, 0x14, 0xce, 0x30, 0xc9, 0xb3, 0x6e, 0x5b, 0x08, 0xbd, 0x3f, 0x2f, + 0xcc, 0x9d, 0x57, 0x0a, 0xe9, 0x09, 0x5c, 0x6e, 0xfb, 0xba, 0xdc, 0x53, 0x0d, 0x70, 0xb0, 0xfc, + 0xe3, 0x6f, 0xe6, 0x82, 0xf5, 0xfb, 0x22, 0xb8, 0x7b, 0x48, 0x12, 0x8a, 0x12, 0x9a, 0x53, 0x79, + 0xf7, 0x5d, 0xd0, 0xaa, 0xc6, 0x8f, 0xb8, 0xfc, 0xbc, 0xfe, 0xd7, 0x1b, 0xf4, 0x8b, 0x12, 0xe1, + 0x36, 0xf9, 0xe1, 0x3e, 0xe7, 0x7d, 0x58, 0xa7, 0x19, 0x1f, 0x83, 0xe5, 0x8c, 0x10, 0xa6, 0xa6, + 0x83, 0xa5, 0xf5, 0x46, 0x3d, 0x8f, 0x66, 0x03, 0xfb, 0x29, 0xca, 0x9e, 0x45, 0xc8, 0x23, 0x84, + 0xb9, 0xcb, 0x9c, 0xc6, 0x13, 0x59, 0xc6, 0x4f, 0x0d, 0xb0, 0x9e, 0xa0, 0x53, 0xe6, 0x57, 0x33, + 0x98, 0xfa, 0x53, 0x48, 0xa7, 0x62, 0x02, 0x74, 0xdc, 0xaf, 0xe7, 0x85, 0xf9, 0x8e, 0xac, 0xc2, + 0x4d, 0x28, 0xeb, 0x9f, 0xc2, 0xfc, 0x70, 0x82, 0xd9, 0x34, 0x1f, 0x71, 0x39, 0xfd, 0x65, 0xd0, + 0x96, 0x11, 0x1e, 0x51, 0x67, 0x74, 0xc6, 0x10, 0xb5, 0x8f, 0xd0, 0xa9, 0xcb, 0x17, 0x9e, 0xc1, + 0xe9, 0xbe, 0xaa, 0xd8, 0x8e, 0x20, 0x9d, 0xaa, 0x32, 0xfd, 0xbc, 0x08, 0x3a, 0x7a, 0xf5, 0x8c, + 0x7d, 0xd0, 0x92, 0x6d, 0x5e, 0x4d, 0x48, 0xd1, 0x88, 0xf7, 0xe4, 0xdf, 0xaa, 0x42, 0xfc, 0x18, + 0x9a, 0xd2, 0x7a, 0x1c, 0x1a, 0x10, 0x34, 0xa7, 0x08, 0x86, 0x28, 0xf3, 0x07, 0xaa, 0x32, 0x3b, + 0xb7, 0xcd, 0xcd, 0x23, 0x81, 0x77, 0x7b, 0x17, 0x85, 0xb9, 0x22, 0xd7, 0x83, 0x79, 0x61, 0xae, + 0x49, 0x99, 0x92, 0xcc, 0xf2, 0x56, 0xe4, 0x72, 0xa0, 0x49, 0x0c, 0xd5, 0xbc, 0xfc, 0x1f, 0x12, + 0xc3, 0x97, 0x24, 0x86, 0x95, 0xc4, 0x50, 0x55, 0xe4, 0xd7, 0x25, 0x70, 0x47, 0xa2, 0x0d, 0x08, + 0x56, 0x29, 0x9e, 0x24, 0x28, 0xf4, 0x25, 0x44, 0x35, 0x4d, 0x4f, 0xd7, 0x91, 0x6f, 0xe5, 0x89, + 0x80, 0x29, 0xc1, 0xed, 0xf3, 0xc2, 0x6c, 0xd4, 0x53, 0xe1, 0x0a, 0x85, 0xe5, 0x75, 0xa8, 0x86, + 0xe5, 0x43, 0xa7, 0x3a, 0x65, 0x9f, 0xa2, 0xb2, 0xb1, 0x6e, 0x90, 0xa8, 0x8e, 0xef, 0x04, 0x31, + 0xb7, 0x5b, 0xd3, 0x5f, 0x49, 0xb7, 0xbc, 0xce, 0x4c, 0xc3, 0x19, 0xdf, 0x01, 0xf9, 0x2c, 0x08, + 0x7d, 0x31, 0xd4, 0x96, 0x6e, 0x1d, 0x6a, 0x0f, 0xd4, 0x50, 0x7b, 0x5b, 0x7b, 0x6c, 0xaa, 0x7c, + 0xcb, 0x5b, 0x55, 0x0e, 0x35, 0xd6, 0x22, 0x60, 0x94, 0x88, 0xba, 0x5d, 0xd5, 0x43, 0x73, 0xdb, + 0x2e, 0x1e, 0xcc, 0x0b, 0x73, 0xf3, 0xaa, 0x4a, 0xcd, 0x61, 0x79, 0x6f, 0x2a, 0x67, 0xdd, 0xb8, + 0xd6, 0x13, 0xd0, 0x2c, 0x1f, 0x5c, 0x63, 0x1b, 0xb4, 0x92, 0x3c, 0x46, 0x19, 0x8f, 0x88, 0x93, + 0x59, 0xf6, 0x6a, 0x87, 0xd1, 0x07, 0xed, 0x10, 0x25, 0x24, 0xc6, 0x89, 0x88, 0x2f, 0x8a, 0xb8, + 0xee, 0x72, 0xc3, 0x17, 0x17, 0xbd, 0xc6, 0xf9, 0x45, 0xaf, 0xf1, 0xd7, 0x45, 0xaf, 0xf1, 0xfc, + 0xb2, 0xb7, 0x70, 0x7e, 0xd9, 0x5b, 0xf8, 0xe3, 0xb2, 0xb7, 0xf0, 0xcd, 0x13, 0xed, 0x92, 0x95, + 0x9f, 0x3f, 0xa3, 0x60, 0x6f, 0x42, 0x9c, 0xd9, 0x43, 0x27, 0x26, 0x61, 0x1e, 0x21, 0x2a, 0x3f, + 0xd2, 0xf6, 0xca, 0xaf, 0xb4, 0x0f, 0x1e, 0xee, 0xd5, 0x7b, 0xfd, 0xa8, 0x5e, 0x8e, 0xee, 0x88, + 0xc9, 0xb2, 0xff, 0x6f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x77, 0xfc, 0x54, 0x53, 0xd9, 0x09, 0x00, + 0x00, } func (m *ClientState) Marshal() (dAtA []byte, err error) { @@ -476,7 +478,7 @@ func (m *ClientState) MarshalToSizedBuffer(dAtA []byte) (int, error) { } i-- dAtA[i] = 0x32 - n3, err3 := github_com_gogo_protobuf_types.StdDurationMarshalTo(m.MaxClockDrift, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdDuration(m.MaxClockDrift):]) + n3, err3 := github_com_cosmos_gogoproto_types.StdDurationMarshalTo(m.MaxClockDrift, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdDuration(m.MaxClockDrift):]) if err3 != nil { return 0, err3 } @@ -484,7 +486,7 @@ func (m *ClientState) MarshalToSizedBuffer(dAtA []byte) (int, error) { i = encodeVarintTendermint(dAtA, i, uint64(n3)) i-- dAtA[i] = 0x2a - n4, err4 := github_com_gogo_protobuf_types.StdDurationMarshalTo(m.UnbondingPeriod, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdDuration(m.UnbondingPeriod):]) + n4, err4 := github_com_cosmos_gogoproto_types.StdDurationMarshalTo(m.UnbondingPeriod, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdDuration(m.UnbondingPeriod):]) if err4 != nil { return 0, err4 } @@ -492,7 +494,7 @@ func (m *ClientState) MarshalToSizedBuffer(dAtA []byte) (int, error) { i = encodeVarintTendermint(dAtA, i, uint64(n4)) i-- dAtA[i] = 0x22 - n5, err5 := github_com_gogo_protobuf_types.StdDurationMarshalTo(m.TrustingPeriod, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdDuration(m.TrustingPeriod):]) + n5, err5 := github_com_cosmos_gogoproto_types.StdDurationMarshalTo(m.TrustingPeriod, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdDuration(m.TrustingPeriod):]) if err5 != nil { return 0, err5 } @@ -557,7 +559,7 @@ func (m *ConsensusState) MarshalToSizedBuffer(dAtA []byte) (int, error) { } i-- dAtA[i] = 0x12 - n8, err8 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Timestamp, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Timestamp):]) + n8, err8 := github_com_cosmos_gogoproto_types.StdTimeMarshalTo(m.Timestamp, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdTime(m.Timestamp):]) if err8 != nil { return 0, err8 } @@ -747,11 +749,11 @@ func (m *ClientState) Size() (n int) { } l = m.TrustLevel.Size() n += 1 + l + sovTendermint(uint64(l)) - l = github_com_gogo_protobuf_types.SizeOfStdDuration(m.TrustingPeriod) + l = github_com_cosmos_gogoproto_types.SizeOfStdDuration(m.TrustingPeriod) n += 1 + l + sovTendermint(uint64(l)) - l = github_com_gogo_protobuf_types.SizeOfStdDuration(m.UnbondingPeriod) + l = github_com_cosmos_gogoproto_types.SizeOfStdDuration(m.UnbondingPeriod) n += 1 + l + sovTendermint(uint64(l)) - l = github_com_gogo_protobuf_types.SizeOfStdDuration(m.MaxClockDrift) + l = github_com_cosmos_gogoproto_types.SizeOfStdDuration(m.MaxClockDrift) n += 1 + l + sovTendermint(uint64(l)) l = m.FrozenHeight.Size() n += 1 + l + sovTendermint(uint64(l)) @@ -784,7 +786,7 @@ func (m *ConsensusState) Size() (n int) { } var l int _ = l - l = github_com_gogo_protobuf_types.SizeOfStdTime(m.Timestamp) + l = github_com_cosmos_gogoproto_types.SizeOfStdTime(m.Timestamp) n += 1 + l + sovTendermint(uint64(l)) l = m.Root.Size() n += 1 + l + sovTendermint(uint64(l)) @@ -983,7 +985,7 @@ func (m *ClientState) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if err := github_com_gogo_protobuf_types.StdDurationUnmarshal(&m.TrustingPeriod, dAtA[iNdEx:postIndex]); err != nil { + if err := github_com_cosmos_gogoproto_types.StdDurationUnmarshal(&m.TrustingPeriod, dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -1016,7 +1018,7 @@ func (m *ClientState) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if err := github_com_gogo_protobuf_types.StdDurationUnmarshal(&m.UnbondingPeriod, dAtA[iNdEx:postIndex]); err != nil { + if err := github_com_cosmos_gogoproto_types.StdDurationUnmarshal(&m.UnbondingPeriod, dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -1049,7 +1051,7 @@ func (m *ClientState) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if err := github_com_gogo_protobuf_types.StdDurationUnmarshal(&m.MaxClockDrift, dAtA[iNdEx:postIndex]); err != nil { + if err := github_com_cosmos_gogoproto_types.StdDurationUnmarshal(&m.MaxClockDrift, dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -1304,7 +1306,7 @@ func (m *ConsensusState) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(&m.Timestamp, dAtA[iNdEx:postIndex]); err != nil { + if err := github_com_cosmos_gogoproto_types.StdTimeUnmarshal(&m.Timestamp, dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex diff --git a/modules/light-clients/07-tendermint/types/tendermint_test.go b/modules/light-clients/07-tendermint/tendermint_test.go similarity index 91% rename from modules/light-clients/07-tendermint/types/tendermint_test.go rename to modules/light-clients/07-tendermint/tendermint_test.go index f653f94fe48..21554335a18 100644 --- a/modules/light-clients/07-tendermint/types/tendermint_test.go +++ b/modules/light-clients/07-tendermint/tendermint_test.go @@ -1,4 +1,4 @@ -package types_test +package tendermint_test import ( "testing" @@ -11,11 +11,11 @@ import ( tmproto "github.com/tendermint/tendermint/proto/tendermint/types" tmtypes "github.com/tendermint/tendermint/types" - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - ibctmtypes "github.com/cosmos/ibc-go/v4/modules/light-clients/07-tendermint/types" - ibctesting "github.com/cosmos/ibc-go/v4/testing" - ibctestingmock "github.com/cosmos/ibc-go/v4/testing/mock" - "github.com/cosmos/ibc-go/v4/testing/simapp" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" + ibctesting "github.com/cosmos/ibc-go/v7/testing" + ibctestingmock "github.com/cosmos/ibc-go/v7/testing/mock" + "github.com/cosmos/ibc-go/v7/testing/simapp" ) const ( @@ -50,7 +50,7 @@ type TendermintTestSuite struct { valSet *tmtypes.ValidatorSet signers map[string]tmtypes.PrivValidator valsHash tmbytes.HexBytes - header *ibctmtypes.Header + header *ibctm.Header now time.Time headerTime time.Time clientTime time.Time diff --git a/modules/light-clients/07-tendermint/types/client_state.go b/modules/light-clients/07-tendermint/types/client_state.go deleted file mode 100644 index 229fa53adb7..00000000000 --- a/modules/light-clients/07-tendermint/types/client_state.go +++ /dev/null @@ -1,583 +0,0 @@ -package types - -import ( - "strings" - "time" - - ics23 "github.com/confio/ics23/go" - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/tendermint/tendermint/light" - tmtypes "github.com/tendermint/tendermint/types" - - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - connectiontypes "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types" - channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" - commitmenttypes "github.com/cosmos/ibc-go/v4/modules/core/23-commitment/types" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" - "github.com/cosmos/ibc-go/v4/modules/core/exported" -) - -var _ exported.ClientState = (*ClientState)(nil) - -// NewClientState creates a new ClientState instance -func NewClientState( - chainID string, trustLevel Fraction, - trustingPeriod, ubdPeriod, maxClockDrift time.Duration, - latestHeight clienttypes.Height, specs []*ics23.ProofSpec, - upgradePath []string, allowUpdateAfterExpiry, allowUpdateAfterMisbehaviour bool, -) *ClientState { - return &ClientState{ - ChainId: chainID, - TrustLevel: trustLevel, - TrustingPeriod: trustingPeriod, - UnbondingPeriod: ubdPeriod, - MaxClockDrift: maxClockDrift, - LatestHeight: latestHeight, - FrozenHeight: clienttypes.ZeroHeight(), - ProofSpecs: specs, - UpgradePath: upgradePath, - AllowUpdateAfterExpiry: allowUpdateAfterExpiry, - AllowUpdateAfterMisbehaviour: allowUpdateAfterMisbehaviour, - } -} - -// GetChainID returns the chain-id -func (cs ClientState) GetChainID() string { - return cs.ChainId -} - -// ClientType is tendermint. -func (cs ClientState) ClientType() string { - return exported.Tendermint -} - -// GetLatestHeight returns latest block height. -func (cs ClientState) GetLatestHeight() exported.Height { - return cs.LatestHeight -} - -// Status returns the status of the tendermint client. -// The client may be: -// - Active: FrozenHeight is zero and client is not expired -// - Frozen: Frozen Height is not zero -// - Expired: the latest consensus state timestamp + trusting period <= current time -// -// A frozen client will become expired, so the Frozen status -// has higher precedence. -func (cs ClientState) Status( - ctx sdk.Context, - clientStore sdk.KVStore, - cdc codec.BinaryCodec, -) exported.Status { - if !cs.FrozenHeight.IsZero() { - return exported.Frozen - } - - // get latest consensus state from clientStore to check for expiry - consState, err := GetConsensusState(clientStore, cdc, cs.GetLatestHeight()) - if err != nil { - // if the client state does not have an associated consensus state for its latest height - // then it must be expired - return exported.Expired - } - - if cs.IsExpired(consState.Timestamp, ctx.BlockTime()) { - return exported.Expired - } - - return exported.Active -} - -// IsExpired returns whether or not the client has passed the trusting period since the last -// update (in which case no headers are considered valid). -func (cs ClientState) IsExpired(latestTimestamp, now time.Time) bool { - expirationTime := latestTimestamp.Add(cs.TrustingPeriod) - return !expirationTime.After(now) -} - -// Validate performs a basic validation of the client state fields. -func (cs ClientState) Validate() error { - if strings.TrimSpace(cs.ChainId) == "" { - return sdkerrors.Wrap(ErrInvalidChainID, "chain id cannot be empty string") - } - - // NOTE: the value of tmtypes.MaxChainIDLen may change in the future. - // If this occurs, the code here must account for potential difference - // between the tendermint version being run by the counterparty chain - // and the tendermint version used by this light client. - // https://github.com/cosmos/ibc-go/issues/177 - if len(cs.ChainId) > tmtypes.MaxChainIDLen { - return sdkerrors.Wrapf(ErrInvalidChainID, "chainID is too long; got: %d, max: %d", len(cs.ChainId), tmtypes.MaxChainIDLen) - } - - if err := light.ValidateTrustLevel(cs.TrustLevel.ToTendermint()); err != nil { - return err - } - if cs.TrustingPeriod == 0 { - return sdkerrors.Wrap(ErrInvalidTrustingPeriod, "trusting period cannot be zero") - } - if cs.UnbondingPeriod == 0 { - return sdkerrors.Wrap(ErrInvalidUnbondingPeriod, "unbonding period cannot be zero") - } - if cs.MaxClockDrift == 0 { - return sdkerrors.Wrap(ErrInvalidMaxClockDrift, "max clock drift cannot be zero") - } - - // the latest height revision number must match the chain id revision number - if cs.LatestHeight.RevisionNumber != clienttypes.ParseChainID(cs.ChainId) { - return sdkerrors.Wrapf(ErrInvalidHeaderHeight, - "latest height revision number must match chain id revision number (%d != %d)", cs.LatestHeight.RevisionNumber, clienttypes.ParseChainID(cs.ChainId)) - } - if cs.LatestHeight.RevisionHeight == 0 { - return sdkerrors.Wrapf(ErrInvalidHeaderHeight, "tendermint client's latest height revision height cannot be zero") - } - if cs.TrustingPeriod >= cs.UnbondingPeriod { - return sdkerrors.Wrapf( - ErrInvalidTrustingPeriod, - "trusting period (%s) should be < unbonding period (%s)", cs.TrustingPeriod, cs.UnbondingPeriod, - ) - } - - if cs.ProofSpecs == nil { - return sdkerrors.Wrap(ErrInvalidProofSpecs, "proof specs cannot be nil for tm client") - } - for i, spec := range cs.ProofSpecs { - if spec == nil { - return sdkerrors.Wrapf(ErrInvalidProofSpecs, "proof spec cannot be nil at index: %d", i) - } - } - // UpgradePath may be empty, but if it isn't, each key must be non-empty - for i, k := range cs.UpgradePath { - if strings.TrimSpace(k) == "" { - return sdkerrors.Wrapf(clienttypes.ErrInvalidClient, "key in upgrade path at index %d cannot be empty", i) - } - } - - return nil -} - -// GetProofSpecs returns the format the client expects for proof verification -// as a string array specifying the proof type for each position in chained proof -func (cs ClientState) GetProofSpecs() []*ics23.ProofSpec { - return cs.ProofSpecs -} - -// ZeroCustomFields returns a ClientState that is a copy of the current ClientState -// with all client customizable fields zeroed out -func (cs ClientState) ZeroCustomFields() exported.ClientState { - // copy over all chain-specified fields - // and leave custom fields empty - return &ClientState{ - ChainId: cs.ChainId, - UnbondingPeriod: cs.UnbondingPeriod, - LatestHeight: cs.LatestHeight, - ProofSpecs: cs.ProofSpecs, - UpgradePath: cs.UpgradePath, - } -} - -// Initialize will check that initial consensus state is a Tendermint consensus state -// and will store ProcessedTime for initial consensus state as ctx.BlockTime() -func (cs ClientState) Initialize(ctx sdk.Context, _ codec.BinaryCodec, clientStore sdk.KVStore, consState exported.ConsensusState) error { - if _, ok := consState.(*ConsensusState); !ok { - return sdkerrors.Wrapf(clienttypes.ErrInvalidConsensus, "invalid initial consensus state. expected type: %T, got: %T", - &ConsensusState{}, consState) - } - // set metadata for initial consensus state. - setConsensusMetadata(ctx, clientStore, cs.GetLatestHeight()) - return nil -} - -// VerifyClientState verifies a proof of the client state of the running chain -// stored on the target machine -func (cs ClientState) VerifyClientState( - store sdk.KVStore, - cdc codec.BinaryCodec, - height exported.Height, - prefix exported.Prefix, - counterpartyClientIdentifier string, - proof []byte, - clientState exported.ClientState, -) error { - merkleProof, provingConsensusState, err := produceVerificationArgs(store, cdc, cs, height, prefix, proof) - if err != nil { - return err - } - - clientPrefixedPath := commitmenttypes.NewMerklePath(host.FullClientStatePath(counterpartyClientIdentifier)) - path, err := commitmenttypes.ApplyPrefix(prefix, clientPrefixedPath) - if err != nil { - return err - } - - if clientState == nil { - return sdkerrors.Wrap(clienttypes.ErrInvalidClient, "client state cannot be empty") - } - - _, ok := clientState.(*ClientState) - if !ok { - return sdkerrors.Wrapf(clienttypes.ErrInvalidClient, "invalid client type %T, expected %T", clientState, &ClientState{}) - } - - bz, err := cdc.MarshalInterface(clientState) - if err != nil { - return err - } - - return merkleProof.VerifyMembership(cs.ProofSpecs, provingConsensusState.GetRoot(), path, bz) -} - -// VerifyClientConsensusState verifies a proof of the consensus state of the -// Tendermint client stored on the target machine. -func (cs ClientState) VerifyClientConsensusState( - store sdk.KVStore, - cdc codec.BinaryCodec, - height exported.Height, - counterpartyClientIdentifier string, - consensusHeight exported.Height, - prefix exported.Prefix, - proof []byte, - consensusState exported.ConsensusState, -) error { - merkleProof, provingConsensusState, err := produceVerificationArgs(store, cdc, cs, height, prefix, proof) - if err != nil { - return err - } - - clientPrefixedPath := commitmenttypes.NewMerklePath(host.FullConsensusStatePath(counterpartyClientIdentifier, consensusHeight)) - path, err := commitmenttypes.ApplyPrefix(prefix, clientPrefixedPath) - if err != nil { - return err - } - - if consensusState == nil { - return sdkerrors.Wrap(clienttypes.ErrInvalidConsensus, "consensus state cannot be empty") - } - - _, ok := consensusState.(*ConsensusState) - if !ok { - return sdkerrors.Wrapf(clienttypes.ErrInvalidConsensus, "invalid consensus type %T, expected %T", consensusState, &ConsensusState{}) - } - - bz, err := cdc.MarshalInterface(consensusState) - if err != nil { - return err - } - - if err := merkleProof.VerifyMembership(cs.ProofSpecs, provingConsensusState.GetRoot(), path, bz); err != nil { - return err - } - - return nil -} - -// VerifyConnectionState verifies a proof of the connection state of the -// specified connection end stored on the target machine. -func (cs ClientState) VerifyConnectionState( - store sdk.KVStore, - cdc codec.BinaryCodec, - height exported.Height, - prefix exported.Prefix, - proof []byte, - connectionID string, - connectionEnd exported.ConnectionI, -) error { - merkleProof, consensusState, err := produceVerificationArgs(store, cdc, cs, height, prefix, proof) - if err != nil { - return err - } - - connectionPath := commitmenttypes.NewMerklePath(host.ConnectionPath(connectionID)) - path, err := commitmenttypes.ApplyPrefix(prefix, connectionPath) - if err != nil { - return err - } - - connection, ok := connectionEnd.(connectiontypes.ConnectionEnd) - if !ok { - return sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "invalid connection type %T", connectionEnd) - } - - bz, err := cdc.Marshal(&connection) - if err != nil { - return err - } - - if err := merkleProof.VerifyMembership(cs.ProofSpecs, consensusState.GetRoot(), path, bz); err != nil { - return err - } - - return nil -} - -// VerifyChannelState verifies a proof of the channel state of the specified -// channel end, under the specified port, stored on the target machine. -func (cs ClientState) VerifyChannelState( - store sdk.KVStore, - cdc codec.BinaryCodec, - height exported.Height, - prefix exported.Prefix, - proof []byte, - portID, - channelID string, - channel exported.ChannelI, -) error { - merkleProof, consensusState, err := produceVerificationArgs(store, cdc, cs, height, prefix, proof) - if err != nil { - return err - } - - channelPath := commitmenttypes.NewMerklePath(host.ChannelPath(portID, channelID)) - path, err := commitmenttypes.ApplyPrefix(prefix, channelPath) - if err != nil { - return err - } - - channelEnd, ok := channel.(channeltypes.Channel) - if !ok { - return sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "invalid channel type %T", channel) - } - - bz, err := cdc.Marshal(&channelEnd) - if err != nil { - return err - } - - if err := merkleProof.VerifyMembership(cs.ProofSpecs, consensusState.GetRoot(), path, bz); err != nil { - return err - } - - return nil -} - -// VerifyPacketCommitment verifies a proof of an outgoing packet commitment at -// the specified port, specified channel, and specified sequence. -func (cs ClientState) VerifyPacketCommitment( - ctx sdk.Context, - store sdk.KVStore, - cdc codec.BinaryCodec, - height exported.Height, - delayTimePeriod uint64, - delayBlockPeriod uint64, - prefix exported.Prefix, - proof []byte, - portID, - channelID string, - sequence uint64, - commitmentBytes []byte, -) error { - merkleProof, consensusState, err := produceVerificationArgs(store, cdc, cs, height, prefix, proof) - if err != nil { - return err - } - - // check delay period has passed - if err := verifyDelayPeriodPassed(ctx, store, height, delayTimePeriod, delayBlockPeriod); err != nil { - return err - } - - commitmentPath := commitmenttypes.NewMerklePath(host.PacketCommitmentPath(portID, channelID, sequence)) - path, err := commitmenttypes.ApplyPrefix(prefix, commitmentPath) - if err != nil { - return err - } - - if err := merkleProof.VerifyMembership(cs.ProofSpecs, consensusState.GetRoot(), path, commitmentBytes); err != nil { - return err - } - - return nil -} - -// VerifyPacketAcknowledgement verifies a proof of an incoming packet -// acknowledgement at the specified port, specified channel, and specified sequence. -func (cs ClientState) VerifyPacketAcknowledgement( - ctx sdk.Context, - store sdk.KVStore, - cdc codec.BinaryCodec, - height exported.Height, - delayTimePeriod uint64, - delayBlockPeriod uint64, - prefix exported.Prefix, - proof []byte, - portID, - channelID string, - sequence uint64, - acknowledgement []byte, -) error { - merkleProof, consensusState, err := produceVerificationArgs(store, cdc, cs, height, prefix, proof) - if err != nil { - return err - } - - // check delay period has passed - if err := verifyDelayPeriodPassed(ctx, store, height, delayTimePeriod, delayBlockPeriod); err != nil { - return err - } - - ackPath := commitmenttypes.NewMerklePath(host.PacketAcknowledgementPath(portID, channelID, sequence)) - path, err := commitmenttypes.ApplyPrefix(prefix, ackPath) - if err != nil { - return err - } - - if err := merkleProof.VerifyMembership(cs.ProofSpecs, consensusState.GetRoot(), path, channeltypes.CommitAcknowledgement(acknowledgement)); err != nil { - return err - } - - return nil -} - -// VerifyPacketReceiptAbsence verifies a proof of the absence of an -// incoming packet receipt at the specified port, specified channel, and -// specified sequence. -func (cs ClientState) VerifyPacketReceiptAbsence( - ctx sdk.Context, - store sdk.KVStore, - cdc codec.BinaryCodec, - height exported.Height, - delayTimePeriod uint64, - delayBlockPeriod uint64, - prefix exported.Prefix, - proof []byte, - portID, - channelID string, - sequence uint64, -) error { - merkleProof, consensusState, err := produceVerificationArgs(store, cdc, cs, height, prefix, proof) - if err != nil { - return err - } - - // check delay period has passed - if err := verifyDelayPeriodPassed(ctx, store, height, delayTimePeriod, delayBlockPeriod); err != nil { - return err - } - - receiptPath := commitmenttypes.NewMerklePath(host.PacketReceiptPath(portID, channelID, sequence)) - path, err := commitmenttypes.ApplyPrefix(prefix, receiptPath) - if err != nil { - return err - } - - if err := merkleProof.VerifyNonMembership(cs.ProofSpecs, consensusState.GetRoot(), path); err != nil { - return err - } - - return nil -} - -// VerifyNextSequenceRecv verifies a proof of the next sequence number to be -// received of the specified channel at the specified port. -func (cs ClientState) VerifyNextSequenceRecv( - ctx sdk.Context, - store sdk.KVStore, - cdc codec.BinaryCodec, - height exported.Height, - delayTimePeriod uint64, - delayBlockPeriod uint64, - prefix exported.Prefix, - proof []byte, - portID, - channelID string, - nextSequenceRecv uint64, -) error { - merkleProof, consensusState, err := produceVerificationArgs(store, cdc, cs, height, prefix, proof) - if err != nil { - return err - } - - // check delay period has passed - if err := verifyDelayPeriodPassed(ctx, store, height, delayTimePeriod, delayBlockPeriod); err != nil { - return err - } - - nextSequenceRecvPath := commitmenttypes.NewMerklePath(host.NextSequenceRecvPath(portID, channelID)) - path, err := commitmenttypes.ApplyPrefix(prefix, nextSequenceRecvPath) - if err != nil { - return err - } - - bz := sdk.Uint64ToBigEndian(nextSequenceRecv) - - if err := merkleProof.VerifyMembership(cs.ProofSpecs, consensusState.GetRoot(), path, bz); err != nil { - return err - } - - return nil -} - -// verifyDelayPeriodPassed will ensure that at least delayTimePeriod amount of time and delayBlockPeriod number of blocks have passed -// since consensus state was submitted before allowing verification to continue. -func verifyDelayPeriodPassed(ctx sdk.Context, store sdk.KVStore, proofHeight exported.Height, delayTimePeriod, delayBlockPeriod uint64) error { - // check that executing chain's timestamp has passed consensusState's processed time + delay time period - processedTime, ok := GetProcessedTime(store, proofHeight) - if !ok { - return sdkerrors.Wrapf(ErrProcessedTimeNotFound, "processed time not found for height: %s", proofHeight) - } - currentTimestamp := uint64(ctx.BlockTime().UnixNano()) - validTime := processedTime + delayTimePeriod - // NOTE: delay time period is inclusive, so if currentTimestamp is validTime, then we return no error - if currentTimestamp < validTime { - return sdkerrors.Wrapf(ErrDelayPeriodNotPassed, "cannot verify packet until time: %d, current time: %d", - validTime, currentTimestamp) - } - // check that executing chain's height has passed consensusState's processed height + delay block period - processedHeight, ok := GetProcessedHeight(store, proofHeight) - if !ok { - return sdkerrors.Wrapf(ErrProcessedHeightNotFound, "processed height not found for height: %s", proofHeight) - } - currentHeight := clienttypes.GetSelfHeight(ctx) - validHeight := clienttypes.NewHeight(processedHeight.GetRevisionNumber(), processedHeight.GetRevisionHeight()+delayBlockPeriod) - // NOTE: delay block period is inclusive, so if currentHeight is validHeight, then we return no error - if currentHeight.LT(validHeight) { - return sdkerrors.Wrapf(ErrDelayPeriodNotPassed, "cannot verify packet until height: %s, current height: %s", - validHeight, currentHeight) - } - return nil -} - -// produceVerificationArgs perfoms the basic checks on the arguments that are -// shared between the verification functions and returns the unmarshalled -// merkle proof, the consensus state and an error if one occurred. -func produceVerificationArgs( - store sdk.KVStore, - cdc codec.BinaryCodec, - cs ClientState, - height exported.Height, - prefix exported.Prefix, - proof []byte, -) (merkleProof commitmenttypes.MerkleProof, consensusState *ConsensusState, err error) { - if cs.GetLatestHeight().LT(height) { - return commitmenttypes.MerkleProof{}, nil, sdkerrors.Wrapf( - sdkerrors.ErrInvalidHeight, - "client state height < proof height (%d < %d), please ensure the client has been updated", cs.GetLatestHeight(), height, - ) - } - - if prefix == nil { - return commitmenttypes.MerkleProof{}, nil, sdkerrors.Wrap(commitmenttypes.ErrInvalidPrefix, "prefix cannot be empty") - } - - _, ok := prefix.(*commitmenttypes.MerklePrefix) - if !ok { - return commitmenttypes.MerkleProof{}, nil, sdkerrors.Wrapf(commitmenttypes.ErrInvalidPrefix, "invalid prefix type %T, expected *MerklePrefix", prefix) - } - - if proof == nil { - return commitmenttypes.MerkleProof{}, nil, sdkerrors.Wrap(commitmenttypes.ErrInvalidProof, "proof cannot be empty") - } - - if err = cdc.Unmarshal(proof, &merkleProof); err != nil { - return commitmenttypes.MerkleProof{}, nil, sdkerrors.Wrap(commitmenttypes.ErrInvalidProof, "failed to unmarshal proof into commitment merkle proof") - } - - consensusState, err = GetConsensusState(store, cdc, height) - if err != nil { - return commitmenttypes.MerkleProof{}, nil, sdkerrors.Wrap(err, "please ensure the proof was constructed against a height that exists on the client") - } - - return merkleProof, consensusState, nil -} diff --git a/modules/light-clients/07-tendermint/types/client_state_test.go b/modules/light-clients/07-tendermint/types/client_state_test.go deleted file mode 100644 index cf596933b6f..00000000000 --- a/modules/light-clients/07-tendermint/types/client_state_test.go +++ /dev/null @@ -1,880 +0,0 @@ -package types_test - -import ( - "time" - - ics23 "github.com/confio/ics23/go" - - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" - commitmenttypes "github.com/cosmos/ibc-go/v4/modules/core/23-commitment/types" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" - "github.com/cosmos/ibc-go/v4/modules/core/exported" - "github.com/cosmos/ibc-go/v4/modules/light-clients/07-tendermint/types" - ibctesting "github.com/cosmos/ibc-go/v4/testing" - ibcmock "github.com/cosmos/ibc-go/v4/testing/mock" -) - -const ( - testClientID = "clientidone" - testConnectionID = "connectionid" - testPortID = "testportid" - testChannelID = "testchannelid" - testSequence = 1 - - // Do not change the length of these variables - fiftyCharChainID = "12345678901234567890123456789012345678901234567890" - fiftyOneCharChainID = "123456789012345678901234567890123456789012345678901" -) - -var invalidProof = []byte("invalid proof") - -func (suite *TendermintTestSuite) TestStatus() { - var ( - path *ibctesting.Path - clientState *types.ClientState - ) - - testCases := []struct { - name string - malleate func() - expStatus exported.Status - }{ - {"client is active", func() {}, exported.Active}, - {"client is frozen", func() { - clientState.FrozenHeight = clienttypes.NewHeight(0, 1) - path.EndpointA.SetClientState(clientState) - }, exported.Frozen}, - {"client status without consensus state", func() { - clientState.LatestHeight = clientState.LatestHeight.Increment().(clienttypes.Height) - path.EndpointA.SetClientState(clientState) - }, exported.Expired}, - {"client status is expired", func() { - suite.coordinator.IncrementTimeBy(clientState.TrustingPeriod) - }, exported.Expired}, - } - - for _, tc := range testCases { - path = ibctesting.NewPath(suite.chainA, suite.chainB) - suite.coordinator.SetupClients(path) - - clientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) - clientState = path.EndpointA.GetClientState().(*types.ClientState) - - tc.malleate() - - status := clientState.Status(suite.chainA.GetContext(), clientStore, suite.chainA.App.AppCodec()) - suite.Require().Equal(tc.expStatus, status) - - } -} - -func (suite *TendermintTestSuite) TestValidate() { - testCases := []struct { - name string - clientState *types.ClientState - expPass bool - }{ - { - name: "valid client", - clientState: types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), - expPass: true, - }, - { - name: "valid client with nil upgrade path", - clientState: types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), nil, false, false), - expPass: true, - }, - { - name: "invalid chainID", - clientState: types.NewClientState(" ", types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), - expPass: false, - }, - { - // NOTE: if this test fails, the code must account for the change in chainID length across tendermint versions! - // Do not only fix the test, fix the code! - // https://github.com/cosmos/ibc-go/issues/177 - name: "valid chainID - chainID validation failed for chainID of length 50! ", - clientState: types.NewClientState(fiftyCharChainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), - expPass: true, - }, - { - // NOTE: if this test fails, the code must account for the change in chainID length across tendermint versions! - // Do not only fix the test, fix the code! - // https://github.com/cosmos/ibc-go/issues/177 - name: "invalid chainID - chainID validation did not fail for chainID of length 51! ", - clientState: types.NewClientState(fiftyOneCharChainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), - expPass: false, - }, - { - name: "invalid trust level", - clientState: types.NewClientState(chainID, types.Fraction{Numerator: 0, Denominator: 1}, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), - expPass: false, - }, - { - name: "invalid trusting period", - clientState: types.NewClientState(chainID, types.DefaultTrustLevel, 0, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), - expPass: false, - }, - { - name: "invalid unbonding period", - clientState: types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, 0, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), - expPass: false, - }, - { - name: "invalid max clock drift", - clientState: types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, 0, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), - expPass: false, - }, - { - name: "invalid revision number", - clientState: types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, clienttypes.NewHeight(1, 1), commitmenttypes.GetSDKSpecs(), upgradePath, false, false), - expPass: false, - }, - { - name: "invalid revision height", - clientState: types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, clienttypes.ZeroHeight(), commitmenttypes.GetSDKSpecs(), upgradePath, false, false), - expPass: false, - }, - { - name: "trusting period not less than unbonding period", - clientState: types.NewClientState(chainID, types.DefaultTrustLevel, ubdPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), - expPass: false, - }, - { - name: "proof specs is nil", - clientState: types.NewClientState(chainID, types.DefaultTrustLevel, ubdPeriod, ubdPeriod, maxClockDrift, height, nil, upgradePath, false, false), - expPass: false, - }, - { - name: "proof specs contains nil", - clientState: types.NewClientState(chainID, types.DefaultTrustLevel, ubdPeriod, ubdPeriod, maxClockDrift, height, []*ics23.ProofSpec{ics23.TendermintSpec, nil}, upgradePath, false, false), - expPass: false, - }, - } - - for _, tc := range testCases { - err := tc.clientState.Validate() - if tc.expPass { - suite.Require().NoError(err, tc.name) - } else { - suite.Require().Error(err, tc.name) - } - } -} - -func (suite *TendermintTestSuite) TestInitialize() { - testCases := []struct { - name string - consensusState exported.ConsensusState - expPass bool - }{ - { - name: "valid consensus", - consensusState: &types.ConsensusState{}, - expPass: true, - }, - { - name: "invalid consensus: consensus state is solomachine consensus", - consensusState: ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachine", "", 2).ConsensusState(), - expPass: false, - }, - } - - path := ibctesting.NewPath(suite.chainA, suite.chainB) - err := path.EndpointA.CreateClient() - suite.Require().NoError(err) - - clientState := suite.chainA.GetClientState(path.EndpointA.ClientID) - store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) - - for _, tc := range testCases { - err := clientState.Initialize(suite.chainA.GetContext(), suite.chainA.Codec, store, tc.consensusState) - if tc.expPass { - suite.Require().NoError(err, "valid case returned an error") - } else { - suite.Require().Error(err, "invalid case didn't return an error") - } - } -} - -func (suite *TendermintTestSuite) TestVerifyClientConsensusState() { - testCases := []struct { - name string - clientState *types.ClientState - consensusState *types.ConsensusState - prefix commitmenttypes.MerklePrefix - proof []byte - expPass bool - }{ - // FIXME: uncomment - // { - // name: "successful verification", - // clientState: types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), - // consensusState: types.ConsensusState{ - // Root: commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), - // }, - // prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")), - // expPass: true, - // }, - { - name: "ApplyPrefix failed", - clientState: types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), - consensusState: &types.ConsensusState{ - Root: commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), - }, - prefix: commitmenttypes.MerklePrefix{}, - expPass: false, - }, - { - name: "latest client height < height", - clientState: types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), - consensusState: &types.ConsensusState{ - Root: commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), - }, - prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")), - expPass: false, - }, - { - name: "proof verification failed", - clientState: types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), - consensusState: &types.ConsensusState{ - Root: commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), - NextValidatorsHash: suite.valsHash, - }, - prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")), - proof: []byte{}, - expPass: false, - }, - } - - for i, tc := range testCases { - tc := tc - - err := tc.clientState.VerifyClientConsensusState( - nil, suite.cdc, height, "chainA", tc.clientState.LatestHeight, tc.prefix, tc.proof, tc.consensusState, - ) - - if tc.expPass { - suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.name) - } else { - suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.name) - } - } -} - -// test verification of the connection on chainB being represented in the -// light client on chainA -func (suite *TendermintTestSuite) TestVerifyConnectionState() { - var ( - clientState *types.ClientState - proof []byte - proofHeight exported.Height - prefix commitmenttypes.MerklePrefix - ) - - testCases := []struct { - name string - malleate func() - expPass bool - }{ - { - "successful verification", func() {}, true, - }, - { - "ApplyPrefix failed", func() { - prefix = commitmenttypes.MerklePrefix{} - }, false, - }, - { - "latest client height < height", func() { - proofHeight = clientState.LatestHeight.Increment() - }, false, - }, - { - "proof verification failed", func() { - proof = invalidProof - }, false, - }, - } - - for _, tc := range testCases { - tc := tc - - suite.Run(tc.name, func() { - suite.SetupTest() // reset - - // setup testing conditions - path := ibctesting.NewPath(suite.chainA, suite.chainB) - suite.coordinator.Setup(path) - connection := path.EndpointB.GetConnection() - - var ok bool - clientStateI := suite.chainA.GetClientState(path.EndpointA.ClientID) - clientState, ok = clientStateI.(*types.ClientState) - suite.Require().True(ok) - - prefix = suite.chainB.GetPrefix() - - // make connection proof - connectionKey := host.ConnectionKey(path.EndpointB.ConnectionID) - proof, proofHeight = suite.chainB.QueryProof(connectionKey) - - tc.malleate() // make changes as necessary - - store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) - - err := clientState.VerifyConnectionState( - store, suite.chainA.Codec, proofHeight, &prefix, proof, path.EndpointB.ConnectionID, connection, - ) - - if tc.expPass { - suite.Require().NoError(err) - } else { - suite.Require().Error(err) - } - }) - } -} - -// test verification of the channel on chainB being represented in the light -// client on chainA -func (suite *TendermintTestSuite) TestVerifyChannelState() { - var ( - clientState *types.ClientState - proof []byte - proofHeight exported.Height - prefix commitmenttypes.MerklePrefix - ) - - testCases := []struct { - name string - malleate func() - expPass bool - }{ - { - "successful verification", func() {}, true, - }, - { - "ApplyPrefix failed", func() { - prefix = commitmenttypes.MerklePrefix{} - }, false, - }, - { - "latest client height < height", func() { - proofHeight = clientState.LatestHeight.Increment() - }, false, - }, - { - "proof verification failed", func() { - proof = invalidProof - }, false, - }, - } - - for _, tc := range testCases { - tc := tc - - suite.Run(tc.name, func() { - suite.SetupTest() // reset - - // setup testing conditions - path := ibctesting.NewPath(suite.chainA, suite.chainB) - suite.coordinator.Setup(path) - channel := path.EndpointB.GetChannel() - - var ok bool - clientStateI := suite.chainA.GetClientState(path.EndpointA.ClientID) - clientState, ok = clientStateI.(*types.ClientState) - suite.Require().True(ok) - - prefix = suite.chainB.GetPrefix() - - // make channel proof - channelKey := host.ChannelKey(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID) - proof, proofHeight = suite.chainB.QueryProof(channelKey) - - tc.malleate() // make changes as necessary - - store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) - - err := clientState.VerifyChannelState( - store, suite.chainA.Codec, proofHeight, &prefix, proof, - path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, channel, - ) - - if tc.expPass { - suite.Require().NoError(err) - } else { - suite.Require().Error(err) - } - }) - } -} - -// test verification of the packet commitment on chainB being represented -// in the light client on chainA. A send from chainB to chainA is simulated. -func (suite *TendermintTestSuite) TestVerifyPacketCommitment() { - var ( - clientState *types.ClientState - proof []byte - delayTimePeriod uint64 - delayBlockPeriod uint64 - proofHeight exported.Height - prefix commitmenttypes.MerklePrefix - ) - - testCases := []struct { - name string - malleate func() - expPass bool - }{ - { - "successful verification", func() {}, true, - }, - { - name: "delay time period has passed", - malleate: func() { - delayTimePeriod = uint64(time.Second.Nanoseconds()) - }, - expPass: true, - }, - { - name: "delay time period has not passed", - malleate: func() { - delayTimePeriod = uint64(time.Hour.Nanoseconds()) - }, - expPass: false, - }, - { - name: "delay block period has passed", - malleate: func() { - delayBlockPeriod = 1 - }, - expPass: true, - }, - { - name: "delay block period has not passed", - malleate: func() { - delayBlockPeriod = 1000 - }, - expPass: false, - }, - - { - "ApplyPrefix failed", func() { - prefix = commitmenttypes.MerklePrefix{} - }, false, - }, - { - "latest client height < height", func() { - proofHeight = clientState.LatestHeight.Increment() - }, false, - }, - { - "proof verification failed", func() { - proof = invalidProof - }, false, - }, - } - - for _, tc := range testCases { - tc := tc - - suite.Run(tc.name, func() { - suite.SetupTest() // reset - - // setup testing conditions - path := ibctesting.NewPath(suite.chainA, suite.chainB) - suite.coordinator.Setup(path) - packet := channeltypes.NewPacket(ibctesting.MockPacketData, 1, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, clienttypes.NewHeight(0, 100), 0) - err := path.EndpointB.SendPacket(packet) - suite.Require().NoError(err) - - var ok bool - clientStateI := suite.chainA.GetClientState(path.EndpointA.ClientID) - clientState, ok = clientStateI.(*types.ClientState) - suite.Require().True(ok) - - prefix = suite.chainB.GetPrefix() - - // make packet commitment proof - packetKey := host.PacketCommitmentKey(packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) - proof, proofHeight = path.EndpointB.QueryProof(packetKey) - - // reset time and block delays to 0, malleate may change to a specific non-zero value. - delayTimePeriod = 0 - delayBlockPeriod = 0 - tc.malleate() // make changes as necessary - - ctx := suite.chainA.GetContext() - store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(ctx, path.EndpointA.ClientID) - - commitment := channeltypes.CommitPacket(suite.chainA.App.GetIBCKeeper().Codec(), packet) - err = clientState.VerifyPacketCommitment( - ctx, store, suite.chainA.Codec, proofHeight, delayTimePeriod, delayBlockPeriod, &prefix, proof, - packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence(), commitment, - ) - - if tc.expPass { - suite.Require().NoError(err) - } else { - suite.Require().Error(err) - } - }) - } -} - -// test verification of the acknowledgement on chainB being represented -// in the light client on chainA. A send and ack from chainA to chainB -// is simulated. -func (suite *TendermintTestSuite) TestVerifyPacketAcknowledgement() { - var ( - clientState *types.ClientState - proof []byte - delayTimePeriod uint64 - delayBlockPeriod uint64 - proofHeight exported.Height - prefix commitmenttypes.MerklePrefix - ) - - testCases := []struct { - name string - malleate func() - expPass bool - }{ - { - "successful verification", func() {}, true, - }, - { - name: "delay time period has passed", - malleate: func() { - delayTimePeriod = uint64(time.Second.Nanoseconds()) - }, - expPass: true, - }, - { - name: "delay time period has not passed", - malleate: func() { - delayTimePeriod = uint64(time.Hour.Nanoseconds()) - }, - expPass: false, - }, - { - name: "delay block period has passed", - malleate: func() { - delayBlockPeriod = 1 - }, - expPass: true, - }, - { - name: "delay block period has not passed", - malleate: func() { - delayBlockPeriod = 10 - }, - expPass: false, - }, - - { - "ApplyPrefix failed", func() { - prefix = commitmenttypes.MerklePrefix{} - }, false, - }, - { - "latest client height < height", func() { - proofHeight = clientState.LatestHeight.Increment() - }, false, - }, - { - "proof verification failed", func() { - proof = invalidProof - }, false, - }, - } - - for _, tc := range testCases { - tc := tc - - suite.Run(tc.name, func() { - suite.SetupTest() // reset - - // setup testing conditions - path := ibctesting.NewPath(suite.chainA, suite.chainB) - suite.coordinator.Setup(path) - packet := channeltypes.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, clienttypes.NewHeight(0, 100), 0) - - // send packet - err := path.EndpointA.SendPacket(packet) - suite.Require().NoError(err) - - // write receipt and ack - err = path.EndpointB.RecvPacket(packet) - suite.Require().NoError(err) - - var ok bool - clientStateI := suite.chainA.GetClientState(path.EndpointA.ClientID) - clientState, ok = clientStateI.(*types.ClientState) - suite.Require().True(ok) - - prefix = suite.chainB.GetPrefix() - - // make packet acknowledgement proof - acknowledgementKey := host.PacketAcknowledgementKey(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()) - proof, proofHeight = suite.chainB.QueryProof(acknowledgementKey) - - // reset time and block delays to 0, malleate may change to a specific non-zero value. - delayTimePeriod = 0 - delayBlockPeriod = 0 - tc.malleate() // make changes as necessary - - ctx := suite.chainA.GetContext() - store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(ctx, path.EndpointA.ClientID) - - err = clientState.VerifyPacketAcknowledgement( - ctx, store, suite.chainA.Codec, proofHeight, delayTimePeriod, delayBlockPeriod, &prefix, proof, - packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence(), ibcmock.MockAcknowledgement.Acknowledgement(), - ) - - if tc.expPass { - suite.Require().NoError(err) - } else { - suite.Require().Error(err) - } - }) - } -} - -// test verification of the absent acknowledgement on chainB being represented -// in the light client on chainA. A send from chainB to chainA is simulated, but -// no receive. -func (suite *TendermintTestSuite) TestVerifyPacketReceiptAbsence() { - var ( - clientState *types.ClientState - proof []byte - delayTimePeriod uint64 - delayBlockPeriod uint64 - proofHeight exported.Height - prefix commitmenttypes.MerklePrefix - ) - - testCases := []struct { - name string - malleate func() - expPass bool - }{ - { - "successful verification", func() {}, true, - }, - { - name: "delay time period has passed", - malleate: func() { - delayTimePeriod = uint64(time.Second.Nanoseconds()) - }, - expPass: true, - }, - { - name: "delay time period has not passed", - malleate: func() { - delayTimePeriod = uint64(time.Hour.Nanoseconds()) - }, - expPass: false, - }, - { - name: "delay block period has passed", - malleate: func() { - delayBlockPeriod = 1 - }, - expPass: true, - }, - { - name: "delay block period has not passed", - malleate: func() { - delayBlockPeriod = 10 - }, - expPass: false, - }, - - { - "ApplyPrefix failed", func() { - prefix = commitmenttypes.MerklePrefix{} - }, false, - }, - { - "latest client height < height", func() { - proofHeight = clientState.LatestHeight.Increment() - }, false, - }, - { - "proof verification failed", func() { - proof = invalidProof - }, false, - }, - } - - for _, tc := range testCases { - tc := tc - - suite.Run(tc.name, func() { - suite.SetupTest() // reset - - // setup testing conditions - path := ibctesting.NewPath(suite.chainA, suite.chainB) - suite.coordinator.Setup(path) - packet := channeltypes.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, clienttypes.NewHeight(0, 100), 0) - - // send packet, but no recv - err := path.EndpointA.SendPacket(packet) - suite.Require().NoError(err) - - var ok bool - clientStateI := suite.chainA.GetClientState(path.EndpointA.ClientID) - clientState, ok = clientStateI.(*types.ClientState) - suite.Require().True(ok) - - prefix = suite.chainB.GetPrefix() - - // make packet receipt absence proof - receiptKey := host.PacketReceiptKey(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()) - proof, proofHeight = path.EndpointB.QueryProof(receiptKey) - - // reset time and block delays to 0, malleate may change to a specific non-zero value. - delayTimePeriod = 0 - delayBlockPeriod = 0 - tc.malleate() // make changes as necessary - - ctx := suite.chainA.GetContext() - store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(ctx, path.EndpointA.ClientID) - - err = clientState.VerifyPacketReceiptAbsence( - ctx, store, suite.chainA.Codec, proofHeight, delayTimePeriod, delayBlockPeriod, &prefix, proof, - packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence(), - ) - - if tc.expPass { - suite.Require().NoError(err) - } else { - suite.Require().Error(err) - } - }) - } -} - -// test verification of the next receive sequence on chainB being represented -// in the light client on chainA. A send and receive from chainB to chainA is -// simulated. -func (suite *TendermintTestSuite) TestVerifyNextSeqRecv() { - var ( - clientState *types.ClientState - proof []byte - delayTimePeriod uint64 - delayBlockPeriod uint64 - proofHeight exported.Height - prefix commitmenttypes.MerklePrefix - ) - - testCases := []struct { - name string - malleate func() - expPass bool - }{ - { - "successful verification", func() {}, true, - }, - { - name: "delay time period has passed", - malleate: func() { - delayTimePeriod = uint64(time.Second.Nanoseconds()) - }, - expPass: true, - }, - { - name: "delay time period has not passed", - malleate: func() { - delayTimePeriod = uint64(time.Hour.Nanoseconds()) - }, - expPass: false, - }, - { - name: "delay block period has passed", - malleate: func() { - delayBlockPeriod = 1 - }, - expPass: true, - }, - { - name: "delay block period has not passed", - malleate: func() { - delayBlockPeriod = 10 - }, - expPass: false, - }, - - { - "ApplyPrefix failed", func() { - prefix = commitmenttypes.MerklePrefix{} - }, false, - }, - { - "latest client height < height", func() { - proofHeight = clientState.LatestHeight.Increment() - }, false, - }, - { - "proof verification failed", func() { - proof = invalidProof - }, false, - }, - } - - for _, tc := range testCases { - tc := tc - - suite.Run(tc.name, func() { - suite.SetupTest() // reset - - // setup testing conditions - path := ibctesting.NewPath(suite.chainA, suite.chainB) - path.SetChannelOrdered() - suite.coordinator.Setup(path) - packet := channeltypes.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, clienttypes.NewHeight(0, 100), 0) - - // send packet - err := path.EndpointA.SendPacket(packet) - suite.Require().NoError(err) - - // next seq recv incremented - err = path.EndpointB.RecvPacket(packet) - suite.Require().NoError(err) - - var ok bool - clientStateI := suite.chainA.GetClientState(path.EndpointA.ClientID) - clientState, ok = clientStateI.(*types.ClientState) - suite.Require().True(ok) - - prefix = suite.chainB.GetPrefix() - - // make next seq recv proof - nextSeqRecvKey := host.NextSequenceRecvKey(packet.GetDestPort(), packet.GetDestChannel()) - proof, proofHeight = suite.chainB.QueryProof(nextSeqRecvKey) - - // reset time and block delays to 0, malleate may change to a specific non-zero value. - delayTimePeriod = 0 - delayBlockPeriod = 0 - tc.malleate() // make changes as necessary - - ctx := suite.chainA.GetContext() - store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(ctx, path.EndpointA.ClientID) - - err = clientState.VerifyNextSequenceRecv( - ctx, store, suite.chainA.Codec, proofHeight, delayTimePeriod, delayBlockPeriod, &prefix, proof, - packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()+1, - ) - - if tc.expPass { - suite.Require().NoError(err) - } else { - suite.Require().Error(err) - } - }) - } -} diff --git a/modules/light-clients/07-tendermint/types/errors.go b/modules/light-clients/07-tendermint/types/errors.go deleted file mode 100644 index 7b087e41af5..00000000000 --- a/modules/light-clients/07-tendermint/types/errors.go +++ /dev/null @@ -1,26 +0,0 @@ -package types - -import ( - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" -) - -const ( - SubModuleName = "tendermint-client" -) - -// IBC tendermint client sentinel errors -var ( - ErrInvalidChainID = sdkerrors.Register(SubModuleName, 2, "invalid chain-id") - ErrInvalidTrustingPeriod = sdkerrors.Register(SubModuleName, 3, "invalid trusting period") - ErrInvalidUnbondingPeriod = sdkerrors.Register(SubModuleName, 4, "invalid unbonding period") - ErrInvalidHeaderHeight = sdkerrors.Register(SubModuleName, 5, "invalid header height") - ErrInvalidHeader = sdkerrors.Register(SubModuleName, 6, "invalid header") - ErrInvalidMaxClockDrift = sdkerrors.Register(SubModuleName, 7, "invalid max clock drift") - ErrProcessedTimeNotFound = sdkerrors.Register(SubModuleName, 8, "processed time not found") - ErrProcessedHeightNotFound = sdkerrors.Register(SubModuleName, 9, "processed height not found") - ErrDelayPeriodNotPassed = sdkerrors.Register(SubModuleName, 10, "packet-specified delay period has not been reached") - ErrTrustingPeriodExpired = sdkerrors.Register(SubModuleName, 11, "time since latest trusted state has passed the trusting period") - ErrUnbondingPeriodExpired = sdkerrors.Register(SubModuleName, 12, "time since latest trusted state has passed the unbonding period") - ErrInvalidProofSpecs = sdkerrors.Register(SubModuleName, 13, "invalid proof specs") - ErrInvalidValidatorSet = sdkerrors.Register(SubModuleName, 14, "invalid validator set") -) diff --git a/modules/light-clients/07-tendermint/types/misbehaviour_handle.go b/modules/light-clients/07-tendermint/types/misbehaviour_handle.go deleted file mode 100644 index 0f8a8f9c76b..00000000000 --- a/modules/light-clients/07-tendermint/types/misbehaviour_handle.go +++ /dev/null @@ -1,143 +0,0 @@ -package types - -import ( - "bytes" - "time" - - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - tmtypes "github.com/tendermint/tendermint/types" - - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v4/modules/core/exported" -) - -// CheckMisbehaviourAndUpdateState determines whether or not two conflicting -// headers at the same height would have convinced the light client. -// -// NOTE: consensusState1 is the trusted consensus state that corresponds to the TrustedHeight -// of misbehaviour.Header1 -// Similarly, consensusState2 is the trusted consensus state that corresponds -// to misbehaviour.Header2 -// Misbehaviour sets frozen height to {0, 1} since it is only used as a boolean value (zero or non-zero). -func (cs ClientState) CheckMisbehaviourAndUpdateState( - ctx sdk.Context, - cdc codec.BinaryCodec, - clientStore sdk.KVStore, - misbehaviour exported.Misbehaviour, -) (exported.ClientState, error) { - tmMisbehaviour, ok := misbehaviour.(*Misbehaviour) - if !ok { - return nil, sdkerrors.Wrapf(clienttypes.ErrInvalidClientType, "expected type %T, got %T", misbehaviour, &Misbehaviour{}) - } - - // The status of the client is checked in 02-client - - // if heights are equal check that this is valid misbehaviour of a fork - // otherwise if heights are unequal check that this is valid misbehavior of BFT time violation - if tmMisbehaviour.Header1.GetHeight().EQ(tmMisbehaviour.Header2.GetHeight()) { - blockID1, err := tmtypes.BlockIDFromProto(&tmMisbehaviour.Header1.SignedHeader.Commit.BlockID) - if err != nil { - return nil, sdkerrors.Wrap(err, "invalid block ID from header 1 in misbehaviour") - } - blockID2, err := tmtypes.BlockIDFromProto(&tmMisbehaviour.Header2.SignedHeader.Commit.BlockID) - if err != nil { - return nil, sdkerrors.Wrap(err, "invalid block ID from header 2 in misbehaviour") - } - - // Ensure that Commit Hashes are different - if bytes.Equal(blockID1.Hash, blockID2.Hash) { - return nil, sdkerrors.Wrap(clienttypes.ErrInvalidMisbehaviour, "headers block hashes are equal") - } - } else { - // Header1 is at greater height than Header2, therefore Header1 time must be less than or equal to - // Header2 time in order to be valid misbehaviour (violation of monotonic time). - if tmMisbehaviour.Header1.SignedHeader.Header.Time.After(tmMisbehaviour.Header2.SignedHeader.Header.Time) { - return nil, sdkerrors.Wrap(clienttypes.ErrInvalidMisbehaviour, "headers are not at same height and are monotonically increasing") - } - } - - // Regardless of the type of misbehaviour, ensure that both headers are valid and would have been accepted by light-client - - // Retrieve trusted consensus states for each Header in misbehaviour - // and unmarshal from clientStore - - // Get consensus bytes from clientStore - tmConsensusState1, err := GetConsensusState(clientStore, cdc, tmMisbehaviour.Header1.TrustedHeight) - if err != nil { - return nil, sdkerrors.Wrapf(err, "could not get trusted consensus state from clientStore for Header1 at TrustedHeight: %s", tmMisbehaviour.Header1) - } - - // Get consensus bytes from clientStore - tmConsensusState2, err := GetConsensusState(clientStore, cdc, tmMisbehaviour.Header2.TrustedHeight) - if err != nil { - return nil, sdkerrors.Wrapf(err, "could not get trusted consensus state from clientStore for Header2 at TrustedHeight: %s", tmMisbehaviour.Header2) - } - - // Check the validity of the two conflicting headers against their respective - // trusted consensus states - // NOTE: header height and commitment root assertions are checked in - // misbehaviour.ValidateBasic by the client keeper and msg.ValidateBasic - // by the base application. - if err := checkMisbehaviourHeader( - &cs, tmConsensusState1, tmMisbehaviour.Header1, ctx.BlockTime(), - ); err != nil { - return nil, sdkerrors.Wrap(err, "verifying Header1 in Misbehaviour failed") - } - if err := checkMisbehaviourHeader( - &cs, tmConsensusState2, tmMisbehaviour.Header2, ctx.BlockTime(), - ); err != nil { - return nil, sdkerrors.Wrap(err, "verifying Header2 in Misbehaviour failed") - } - - cs.FrozenHeight = FrozenHeight - - return &cs, nil -} - -// checkMisbehaviourHeader checks that a Header in Misbehaviour is valid misbehaviour given -// a trusted ConsensusState -func checkMisbehaviourHeader( - clientState *ClientState, consState *ConsensusState, header *Header, currentTimestamp time.Time, -) error { - tmTrustedValset, err := tmtypes.ValidatorSetFromProto(header.TrustedValidators) - if err != nil { - return sdkerrors.Wrap(err, "trusted validator set is not tendermint validator set type") - } - - tmCommit, err := tmtypes.CommitFromProto(header.Commit) - if err != nil { - return sdkerrors.Wrap(err, "commit is not tendermint commit type") - } - - // check the trusted fields for the header against ConsensusState - if err := checkTrustedHeader(header, consState); err != nil { - return err - } - - // assert that the age of the trusted consensus state is not older than the trusting period - if currentTimestamp.Sub(consState.Timestamp) >= clientState.TrustingPeriod { - return sdkerrors.Wrapf( - ErrTrustingPeriodExpired, - "current timestamp minus the latest consensus state timestamp is greater than or equal to the trusting period (%d >= %d)", - currentTimestamp.Sub(consState.Timestamp), clientState.TrustingPeriod, - ) - } - - chainID := clientState.GetChainID() - // If chainID is in revision format, then set revision number of chainID with the revision number - // of the misbehaviour header - if clienttypes.IsRevisionFormat(chainID) { - chainID, _ = clienttypes.SetRevisionNumber(chainID, header.GetHeight().GetRevisionNumber()) - } - - // - ValidatorSet must have TrustLevel similarity with trusted FromValidatorSet - // - ValidatorSets on both headers are valid given the last trusted ValidatorSet - if err := tmTrustedValset.VerifyCommitLightTrusting( - chainID, tmCommit, clientState.TrustLevel.ToTendermint(), - ); err != nil { - return sdkerrors.Wrapf(clienttypes.ErrInvalidMisbehaviour, "validator set in header has too much change from trusted validator set: %v", err) - } - return nil -} diff --git a/modules/light-clients/07-tendermint/types/misbehaviour_handle_test.go b/modules/light-clients/07-tendermint/types/misbehaviour_handle_test.go deleted file mode 100644 index 92711275887..00000000000 --- a/modules/light-clients/07-tendermint/types/misbehaviour_handle_test.go +++ /dev/null @@ -1,425 +0,0 @@ -package types_test - -import ( - "fmt" - "time" - - "github.com/tendermint/tendermint/crypto/tmhash" - tmtypes "github.com/tendermint/tendermint/types" - - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - commitmenttypes "github.com/cosmos/ibc-go/v4/modules/core/23-commitment/types" - "github.com/cosmos/ibc-go/v4/modules/core/exported" - "github.com/cosmos/ibc-go/v4/modules/light-clients/07-tendermint/types" - ibctestingmock "github.com/cosmos/ibc-go/v4/testing/mock" -) - -func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() { - altPrivVal := ibctestingmock.NewPV() - altPubKey, err := altPrivVal.GetPubKey() - suite.Require().NoError(err) - - altVal := tmtypes.NewValidator(altPubKey, 4) - - // Create alternative validator set with only altVal - altValSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{altVal}) - - // Create bothValSet with both suite validator and altVal - bothValSet, bothSigners := getBothSigners(suite, altVal, altPrivVal) - bothValsHash := bothValSet.Hash() - - altSigners := getAltSigners(altVal, altPrivVal) - - heightMinus1 := clienttypes.NewHeight(height.RevisionNumber, height.RevisionHeight-1) - heightMinus3 := clienttypes.NewHeight(height.RevisionNumber, height.RevisionHeight-3) - - testCases := []struct { - name string - clientState exported.ClientState - consensusState1 exported.ConsensusState - height1 clienttypes.Height - consensusState2 exported.ConsensusState - height2 clienttypes.Height - misbehaviour exported.Misbehaviour - timestamp time.Time - expPass bool - }{ - { - "valid fork misbehaviour", - types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - height, - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - height, - &types.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now, bothValSet, bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothValSet, bothSigners), - ClientId: chainID, - }, - suite.now, - true, - }, - { - "valid time misbehaviour", - types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - height, - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - height, - &types.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+3), height, suite.now, bothValSet, bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now, bothValSet, bothValSet, bothValSet, bothSigners), - ClientId: chainID, - }, - suite.now, - true, - }, - { - "valid time misbehaviour header 1 stricly less than header 2", - types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - height, - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - height, - &types.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+3), height, suite.now, bothValSet, bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now.Add(time.Hour), bothValSet, bothValSet, bothValSet, bothSigners), - ClientId: chainID, - }, - suite.now, - true, - }, - { - "valid misbehavior at height greater than last consensusState", - types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - heightMinus1, - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - heightMinus1, - &types.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), heightMinus1, suite.now, bothValSet, bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), heightMinus1, suite.now.Add(time.Minute), bothValSet, bothValSet, bothValSet, bothSigners), - ClientId: chainID, - }, - suite.now, - true, - }, - { - "valid misbehaviour with different trusted heights", - types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - heightMinus1, - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), suite.valsHash), - heightMinus3, - &types.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), heightMinus1, suite.now, bothValSet, bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), heightMinus3, suite.now.Add(time.Minute), bothValSet, bothValSet, suite.valSet, bothSigners), - ClientId: chainID, - }, - suite.now, - true, - }, - { - "valid misbehaviour at a previous revision", - types.NewClientState(chainIDRevision1, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, clienttypes.NewHeight(1, 1), commitmenttypes.GetSDKSpecs(), upgradePath, false, false), - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - heightMinus1, - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), suite.valsHash), - heightMinus3, - &types.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(chainIDRevision0, int64(height.RevisionHeight+1), heightMinus1, suite.now, bothValSet, bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(chainIDRevision0, int64(height.RevisionHeight+1), heightMinus3, suite.now.Add(time.Minute), bothValSet, bothValSet, suite.valSet, bothSigners), - ClientId: chainID, - }, - suite.now, - true, - }, - { - "valid misbehaviour at a future revision", - types.NewClientState(chainIDRevision0, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - heightMinus1, - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), suite.valsHash), - heightMinus3, - &types.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(chainIDRevision0, 3, heightMinus1, suite.now, bothValSet, bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(chainIDRevision0, 3, heightMinus3, suite.now.Add(time.Minute), bothValSet, bothValSet, suite.valSet, bothSigners), - ClientId: chainID, - }, - suite.now, - true, - }, - { - "valid misbehaviour with trusted heights at a previous revision", - types.NewClientState(chainIDRevision1, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, clienttypes.NewHeight(1, 1), commitmenttypes.GetSDKSpecs(), upgradePath, false, false), - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - heightMinus1, - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), suite.valsHash), - heightMinus3, - &types.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(chainIDRevision1, 1, heightMinus1, suite.now, bothValSet, bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(chainIDRevision1, 1, heightMinus3, suite.now.Add(time.Minute), bothValSet, bothValSet, suite.valSet, bothSigners), - ClientId: chainID, - }, - suite.now, - true, - }, - { - "consensus state's valset hash different from misbehaviour should still pass", - types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), suite.valsHash), - height, - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), suite.valsHash), - height, - &types.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now, bothValSet, bothValSet, suite.valSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now.Add(time.Minute), bothValSet, bothValSet, suite.valSet, bothSigners), - ClientId: chainID, - }, - suite.now, - true, - }, - { - "invalid fork misbehaviour: identical headers", - types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - height, - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - height, - &types.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now, bothValSet, bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now, bothValSet, bothValSet, bothValSet, bothSigners), - ClientId: chainID, - }, - suite.now, - false, - }, - { - "invalid time misbehaviour: monotonically increasing time", - types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - height, - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - height, - &types.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+3), height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now, bothValSet, bothValSet, bothValSet, bothSigners), - ClientId: chainID, - }, - suite.now, - false, - }, - { - "invalid misbehavior misbehaviour from different chain", - types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - height, - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - height, - &types.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader("ethermint", int64(height.RevisionHeight+1), height, suite.now, bothValSet, bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader("ethermint", int64(height.RevisionHeight+1), height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothValSet, bothSigners), - ClientId: chainID, - }, - suite.now, - false, - }, - { - "invalid misbehavior misbehaviour with trusted height different from trusted consensus state", - types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - heightMinus1, - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), suite.valsHash), - heightMinus3, - &types.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), heightMinus1, suite.now, bothValSet, bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now.Add(time.Minute), bothValSet, bothValSet, suite.valSet, bothSigners), - ClientId: chainID, - }, - suite.now, - false, - }, - { - "invalid misbehavior misbehaviour with trusted validators different from trusted consensus state", - types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - heightMinus1, - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), suite.valsHash), - heightMinus3, - &types.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), heightMinus1, suite.now, bothValSet, bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), heightMinus3, suite.now.Add(time.Minute), bothValSet, bothValSet, bothValSet, bothSigners), - ClientId: chainID, - }, - suite.now, - false, - }, - { - "already frozen client state", - &types.ClientState{FrozenHeight: clienttypes.NewHeight(0, 1)}, - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - height, - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - height, - &types.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now, bothValSet, bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothValSet, bothSigners), - ClientId: chainID, - }, - suite.now, - false, - }, - { - "trusted consensus state does not exist", - types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), - nil, // consensus state for trusted height - 1 does not exist in store - clienttypes.Height{}, - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - height, - &types.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), heightMinus1, suite.now, bothValSet, bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothValSet, bothSigners), - ClientId: chainID, - }, - suite.now, - false, - }, - { - "invalid tendermint misbehaviour", - types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - height, - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - height, - nil, - suite.now, - false, - }, - { - "provided height > header height", - types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - height, - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - height, - &types.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), heightMinus1, suite.now, bothValSet, bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), heightMinus1, suite.now.Add(time.Minute), bothValSet, bothValSet, bothValSet, bothSigners), - ClientId: chainID, - }, - suite.now, - false, - }, - { - "trusting period expired", - types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), - types.NewConsensusState(time.Time{}, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - heightMinus1, - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - height, - &types.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), heightMinus1, suite.now, bothValSet, bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothValSet, bothSigners), - ClientId: chainID, - }, - suite.now.Add(trustingPeriod), - false, - }, - { - "trusted validators is incorrect for given consensus state", - types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - height, - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - height, - &types.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now, bothValSet, bothValSet, suite.valSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now.Add(time.Minute), bothValSet, bothValSet, suite.valSet, bothSigners), - ClientId: chainID, - }, - suite.now, - false, - }, - { - "first valset has too much change", - types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - height, - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - height, - &types.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now, altValSet, altValSet, bothValSet, altSigners), - Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothValSet, bothSigners), - ClientId: chainID, - }, - suite.now, - false, - }, - { - "second valset has too much change", - types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - height, - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - height, - &types.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now, bothValSet, bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now.Add(time.Minute), altValSet, altValSet, bothValSet, altSigners), - ClientId: chainID, - }, - suite.now, - false, - }, - { - "both valsets have too much change", - types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - height, - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - height, - &types.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now, altValSet, altValSet, bothValSet, altSigners), - Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now.Add(time.Minute), altValSet, altValSet, bothValSet, altSigners), - ClientId: chainID, - }, - suite.now, - false, - }, - } - - for i, tc := range testCases { - tc := tc - suite.Run(fmt.Sprintf("Case: %s", tc.name), func() { - // reset suite to create fresh application state - suite.SetupTest() - - // Set current timestamp in context - ctx := suite.chainA.GetContext().WithBlockTime(tc.timestamp) - - // Set trusted consensus states in client store - - if tc.consensusState1 != nil { - suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(ctx, clientID, tc.height1, tc.consensusState1) - } - if tc.consensusState2 != nil { - suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(ctx, clientID, tc.height2, tc.consensusState2) - } - - clientState, err := tc.clientState.CheckMisbehaviourAndUpdateState( - ctx, - suite.chainA.App.AppCodec(), - suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(ctx, clientID), // pass in clientID prefixed clientStore - tc.misbehaviour, - ) - - if tc.expPass { - suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.name) - suite.Require().NotNil(clientState, "valid test case %d failed: %s", i, tc.name) - suite.Require().True(!clientState.(*types.ClientState).FrozenHeight.IsZero(), "valid test case %d failed: %s", i, tc.name) - } else { - suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.name) - suite.Require().Nil(clientState, "invalid test case %d passed: %s", i, tc.name) - } - }) - } -} diff --git a/modules/light-clients/07-tendermint/types/update.go b/modules/light-clients/07-tendermint/types/update.go deleted file mode 100644 index b75e9ddbf9e..00000000000 --- a/modules/light-clients/07-tendermint/types/update.go +++ /dev/null @@ -1,263 +0,0 @@ -package types - -import ( - "bytes" - "reflect" - "time" - - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/tendermint/tendermint/light" - tmtypes "github.com/tendermint/tendermint/types" - - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - commitmenttypes "github.com/cosmos/ibc-go/v4/modules/core/23-commitment/types" - "github.com/cosmos/ibc-go/v4/modules/core/exported" -) - -// CheckHeaderAndUpdateState checks if the provided header is valid, and if valid it will: -// create the consensus state for the header.Height -// and update the client state if the header height is greater than the latest client state height -// It returns an error if: -// - the client or header provided are not parseable to tendermint types -// - the header is invalid -// - header height is less than or equal to the trusted header height -// - header revision is not equal to trusted header revision -// - header valset commit verification fails -// - header timestamp is past the trusting period in relation to the consensus state -// - header timestamp is less than or equal to the consensus state timestamp -// -// UpdateClient may be used to either create a consensus state for: -// - a future height greater than the latest client state height -// - a past height that was skipped during bisection -// If we are updating to a past height, a consensus state is created for that height to be persisted in client store -// If we are updating to a future height, the consensus state is created and the client state is updated to reflect -// the new latest height -// UpdateClient must only be used to update within a single revision, thus header revision number and trusted height's revision -// number must be the same. To update to a new revision, use a separate upgrade path -// Tendermint client validity checking uses the bisection algorithm described -// in the [Tendermint spec](https://github.com/tendermint/spec/blob/master/spec/consensus/light-client.md). -// -// Misbehaviour Detection: -// UpdateClient will detect implicit misbehaviour by enforcing certain invariants on any new update call and will return a frozen client. -// 1. Any valid update that creates a different consensus state for an already existing height is evidence of misbehaviour and will freeze client. -// 2. Any valid update that breaks time monotonicity with respect to its neighboring consensus states is evidence of misbehaviour and will freeze client. -// Misbehaviour sets frozen height to {0, 1} since it is only used as a boolean value (zero or non-zero). -// -// Pruning: -// UpdateClient will additionally retrieve the earliest consensus state for this clientID and check if it is expired. If it is, -// that consensus state will be pruned from store along with all associated metadata. This will prevent the client store from -// becoming bloated with expired consensus states that can no longer be used for updates and packet verification. -func (cs ClientState) CheckHeaderAndUpdateState( - ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, - header exported.Header, -) (exported.ClientState, exported.ConsensusState, error) { - tmHeader, ok := header.(*Header) - if !ok { - return nil, nil, sdkerrors.Wrapf( - clienttypes.ErrInvalidHeader, "expected type %T, got %T", &Header{}, header, - ) - } - - // Check if the Client store already has a consensus state for the header's height - // If the consensus state exists, and it matches the header then we return early - // since header has already been submitted in a previous UpdateClient. - var conflictingHeader bool - prevConsState, _ := GetConsensusState(clientStore, cdc, header.GetHeight()) - if prevConsState != nil { - // This header has already been submitted and the necessary state is already stored - // in client store, thus we can return early without further validation. - if reflect.DeepEqual(prevConsState, tmHeader.ConsensusState()) { - return &cs, prevConsState, nil - } - // A consensus state already exists for this height, but it does not match the provided header. - // Thus, we must check that this header is valid, and if so we will freeze the client. - conflictingHeader = true - } - - // get consensus state from clientStore - trustedConsState, err := GetConsensusState(clientStore, cdc, tmHeader.TrustedHeight) - if err != nil { - return nil, nil, sdkerrors.Wrapf( - err, "could not get consensus state from clientstore at TrustedHeight: %s", tmHeader.TrustedHeight, - ) - } - - if err := checkValidity(&cs, trustedConsState, tmHeader, ctx.BlockTime()); err != nil { - return nil, nil, err - } - - consState := tmHeader.ConsensusState() - // Header is different from existing consensus state and also valid, so freeze the client and return - if conflictingHeader { - cs.FrozenHeight = FrozenHeight - return &cs, consState, nil - } - // Check that consensus state timestamps are monotonic - prevCons, prevOk := GetPreviousConsensusState(clientStore, cdc, header.GetHeight()) - nextCons, nextOk := GetNextConsensusState(clientStore, cdc, header.GetHeight()) - // if previous consensus state exists, check consensus state time is greater than previous consensus state time - // if previous consensus state is not before current consensus state, freeze the client and return. - if prevOk && !prevCons.Timestamp.Before(consState.Timestamp) { - cs.FrozenHeight = FrozenHeight - return &cs, consState, nil - } - // if next consensus state exists, check consensus state time is less than next consensus state time - // if next consensus state is not after current consensus state, freeze the client and return. - if nextOk && !nextCons.Timestamp.After(consState.Timestamp) { - cs.FrozenHeight = FrozenHeight - return &cs, consState, nil - } - - // Check the earliest consensus state to see if it is expired, if so then set the prune height - // so that we can delete consensus state and all associated metadata. - var ( - pruneHeight exported.Height - pruneError error - ) - pruneCb := func(height exported.Height) bool { - consState, err := GetConsensusState(clientStore, cdc, height) - // this error should never occur - if err != nil { - pruneError = err - return true - } - if cs.IsExpired(consState.Timestamp, ctx.BlockTime()) { - pruneHeight = height - } - return true - } - IterateConsensusStateAscending(clientStore, pruneCb) - if pruneError != nil { - return nil, nil, pruneError - } - // if pruneHeight is set, delete consensus state and metadata - if pruneHeight != nil { - deleteConsensusState(clientStore, pruneHeight) - deleteConsensusMetadata(clientStore, pruneHeight) - } - - newClientState, consensusState := update(ctx, clientStore, &cs, tmHeader) - return newClientState, consensusState, nil -} - -// checkTrustedHeader checks that consensus state matches trusted fields of Header -func checkTrustedHeader(header *Header, consState *ConsensusState) error { - tmTrustedValidators, err := tmtypes.ValidatorSetFromProto(header.TrustedValidators) - if err != nil { - return sdkerrors.Wrap(err, "trusted validator set in not tendermint validator set type") - } - - // assert that trustedVals is NextValidators of last trusted header - // to do this, we check that trustedVals.Hash() == consState.NextValidatorsHash - tvalHash := tmTrustedValidators.Hash() - if !bytes.Equal(consState.NextValidatorsHash, tvalHash) { - return sdkerrors.Wrapf( - ErrInvalidValidatorSet, - "trusted validators %s, does not hash to latest trusted validators. Expected: %X, got: %X", - header.TrustedValidators, consState.NextValidatorsHash, tvalHash, - ) - } - return nil -} - -// checkValidity checks if the Tendermint header is valid. -// CONTRACT: consState.Height == header.TrustedHeight -func checkValidity( - clientState *ClientState, consState *ConsensusState, - header *Header, currentTimestamp time.Time, -) error { - if err := checkTrustedHeader(header, consState); err != nil { - return err - } - - // UpdateClient only accepts updates with a header at the same revision - // as the trusted consensus state - if header.GetHeight().GetRevisionNumber() != header.TrustedHeight.RevisionNumber { - return sdkerrors.Wrapf( - ErrInvalidHeaderHeight, - "header height revision %d does not match trusted header revision %d", - header.GetHeight().GetRevisionNumber(), header.TrustedHeight.RevisionNumber, - ) - } - - tmTrustedValidators, err := tmtypes.ValidatorSetFromProto(header.TrustedValidators) - if err != nil { - return sdkerrors.Wrap(err, "trusted validator set in not tendermint validator set type") - } - - tmSignedHeader, err := tmtypes.SignedHeaderFromProto(header.SignedHeader) - if err != nil { - return sdkerrors.Wrap(err, "signed header in not tendermint signed header type") - } - - tmValidatorSet, err := tmtypes.ValidatorSetFromProto(header.ValidatorSet) - if err != nil { - return sdkerrors.Wrap(err, "validator set in not tendermint validator set type") - } - - // assert header height is newer than consensus state - if header.GetHeight().LTE(header.TrustedHeight) { - return sdkerrors.Wrapf( - clienttypes.ErrInvalidHeader, - "header height ≤ consensus state height (%s ≤ %s)", header.GetHeight(), header.TrustedHeight, - ) - } - - chainID := clientState.GetChainID() - // If chainID is in revision format, then set revision number of chainID with the revision number - // of the header we are verifying - // This is useful if the update is at a previous revision rather than an update to the latest revision - // of the client. - // The chainID must be set correctly for the previous revision before attempting verification. - // Updates for previous revisions are not supported if the chainID is not in revision format. - if clienttypes.IsRevisionFormat(chainID) { - chainID, _ = clienttypes.SetRevisionNumber(chainID, header.GetHeight().GetRevisionNumber()) - } - - // Construct a trusted header using the fields in consensus state - // Only Height, Time, and NextValidatorsHash are necessary for verification - trustedHeader := tmtypes.Header{ - ChainID: chainID, - Height: int64(header.TrustedHeight.RevisionHeight), - Time: consState.Timestamp, - NextValidatorsHash: consState.NextValidatorsHash, - } - signedHeader := tmtypes.SignedHeader{ - Header: &trustedHeader, - } - - // Verify next header with the passed-in trustedVals - // - asserts trusting period not passed - // - assert header timestamp is not past the trusting period - // - assert header timestamp is past latest stored consensus state timestamp - // - assert that a TrustLevel proportion of TrustedValidators signed new Commit - err = light.Verify( - &signedHeader, - tmTrustedValidators, tmSignedHeader, tmValidatorSet, - clientState.TrustingPeriod, currentTimestamp, clientState.MaxClockDrift, clientState.TrustLevel.ToTendermint(), - ) - if err != nil { - return sdkerrors.Wrap(err, "failed to verify header") - } - return nil -} - -// update the consensus state from a new header and set processed time metadata -func update(ctx sdk.Context, clientStore sdk.KVStore, clientState *ClientState, header *Header) (*ClientState, *ConsensusState) { - height := header.GetHeight().(clienttypes.Height) - if height.GT(clientState.LatestHeight) { - clientState.LatestHeight = height - } - consensusState := &ConsensusState{ - Timestamp: header.GetTime(), - Root: commitmenttypes.NewMerkleRoot(header.Header.GetAppHash()), - NextValidatorsHash: header.Header.NextValidatorsHash, - } - - // set metadata for this consensus state - setConsensusMetadata(ctx, clientStore, header.GetHeight()) - - return clientState, consensusState -} diff --git a/modules/light-clients/07-tendermint/types/update_test.go b/modules/light-clients/07-tendermint/types/update_test.go deleted file mode 100644 index 9dae5d56f7d..00000000000 --- a/modules/light-clients/07-tendermint/types/update_test.go +++ /dev/null @@ -1,450 +0,0 @@ -package types_test - -import ( - "fmt" - "time" - - tmtypes "github.com/tendermint/tendermint/types" - - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - commitmenttypes "github.com/cosmos/ibc-go/v4/modules/core/23-commitment/types" - "github.com/cosmos/ibc-go/v4/modules/core/exported" - types "github.com/cosmos/ibc-go/v4/modules/light-clients/07-tendermint/types" - ibctesting "github.com/cosmos/ibc-go/v4/testing" - ibctestingmock "github.com/cosmos/ibc-go/v4/testing/mock" -) - -func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() { - var ( - clientState *types.ClientState - consensusState *types.ConsensusState - consStateHeight clienttypes.Height - newHeader *types.Header - currentTime time.Time - bothValSet *tmtypes.ValidatorSet - bothSigners map[string]tmtypes.PrivValidator - ) - - // Setup different validators and signers for testing different types of updates - altPrivVal := ibctestingmock.NewPV() - altPubKey, err := altPrivVal.GetPubKey() - suite.Require().NoError(err) - - revisionHeight := int64(height.RevisionHeight) - - // create modified heights to use for test-cases - heightPlus1 := clienttypes.NewHeight(height.RevisionNumber, height.RevisionHeight+1) - heightMinus1 := clienttypes.NewHeight(height.RevisionNumber, height.RevisionHeight-1) - heightMinus3 := clienttypes.NewHeight(height.RevisionNumber, height.RevisionHeight-3) - heightPlus5 := clienttypes.NewHeight(height.RevisionNumber, height.RevisionHeight+5) - - altVal := tmtypes.NewValidator(altPubKey, revisionHeight) - // Create alternative validator set with only altVal, invalid update (too much change in valSet) - altValSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{altVal}) - altSigners := getAltSigners(altVal, altPrivVal) - - testCases := []struct { - name string - setup func(*TendermintTestSuite) - expFrozen bool - expPass bool - }{ - { - name: "successful update with next height and same validator set", - setup: func(suite *TendermintTestSuite) { - clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) - consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus1.RevisionHeight), height, suite.headerTime, suite.valSet, suite.valSet, suite.valSet, suite.signers) - currentTime = suite.now - }, - expFrozen: false, - expPass: true, - }, - { - name: "successful update with future height and different validator set", - setup: func(suite *TendermintTestSuite) { - clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) - consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus5.RevisionHeight), height, suite.headerTime, bothValSet, bothValSet, suite.valSet, bothSigners) - currentTime = suite.now - }, - expFrozen: false, - expPass: true, - }, - { - name: "successful update with next height and different validator set", - setup: func(suite *TendermintTestSuite) { - clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) - consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), bothValSet.Hash()) - newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus1.RevisionHeight), height, suite.headerTime, bothValSet, bothValSet, bothValSet, bothSigners) - currentTime = suite.now - }, - expFrozen: false, - expPass: true, - }, - { - name: "successful update for a previous height", - setup: func(suite *TendermintTestSuite) { - clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) - consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - consStateHeight = heightMinus3 - newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightMinus1.RevisionHeight), heightMinus3, suite.headerTime, bothValSet, bothValSet, suite.valSet, bothSigners) - currentTime = suite.now - }, - expFrozen: false, - expPass: true, - }, - { - name: "successful update for a previous revision", - setup: func(suite *TendermintTestSuite) { - clientState = types.NewClientState(chainIDRevision1, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) - consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - consStateHeight = heightMinus3 - newHeader = suite.chainA.CreateTMClientHeader(chainIDRevision0, int64(height.RevisionHeight), heightMinus3, suite.headerTime, bothValSet, bothValSet, suite.valSet, bothSigners) - currentTime = suite.now - }, - expPass: true, - }, - { - name: "successful update with identical header to a previous update", - setup: func(suite *TendermintTestSuite) { - clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, heightPlus1, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) - consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus1.RevisionHeight), height, suite.headerTime, suite.valSet, suite.valSet, suite.valSet, suite.signers) - currentTime = suite.now - ctx := suite.chainA.GetContext().WithBlockTime(currentTime) - // Store the header's consensus state in client store before UpdateClient call - suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(ctx, clientID, heightPlus1, newHeader.ConsensusState()) - }, - expFrozen: false, - expPass: true, - }, - { - name: "misbehaviour detection: header conflicts with existing consensus state", - setup: func(suite *TendermintTestSuite) { - clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, heightPlus1, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) - consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus1.RevisionHeight), height, suite.headerTime, suite.valSet, suite.valSet, suite.valSet, suite.signers) - currentTime = suite.now - ctx := suite.chainA.GetContext().WithBlockTime(currentTime) - // Change the consensus state of header and store in client store to create a conflict - conflictConsState := newHeader.ConsensusState() - conflictConsState.Root = commitmenttypes.NewMerkleRoot([]byte("conflicting apphash")) - suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(ctx, clientID, heightPlus1, conflictConsState) - }, - expFrozen: true, - expPass: true, - }, - { - name: "misbehaviour detection: previous consensus state time is not before header time. time monotonicity violation", - setup: func(suite *TendermintTestSuite) { - clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) - // create an intermediate consensus state with the same time as the newHeader to create a time violation. - // header time is after client time - consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus5.RevisionHeight), height, suite.headerTime, suite.valSet, suite.valSet, suite.valSet, suite.signers) - currentTime = suite.now - prevConsensusState := types.NewConsensusState(suite.headerTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - ctx := suite.chainA.GetContext().WithBlockTime(currentTime) - suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(ctx, clientID, heightPlus1, prevConsensusState) - clientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(ctx, clientID) - types.SetIterationKey(clientStore, heightPlus1) - }, - expFrozen: true, - expPass: true, - }, - { - name: "misbehaviour detection: next consensus state time is not after header time. time monotonicity violation", - setup: func(suite *TendermintTestSuite) { - clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) - // create the next consensus state with the same time as the intermediate newHeader to create a time violation. - // header time is after clientTime - consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus1.RevisionHeight), height, suite.headerTime, suite.valSet, suite.valSet, suite.valSet, suite.signers) - currentTime = suite.now - nextConsensusState := types.NewConsensusState(suite.headerTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - ctx := suite.chainA.GetContext().WithBlockTime(currentTime) - suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(ctx, clientID, heightPlus5, nextConsensusState) - clientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(ctx, clientID) - types.SetIterationKey(clientStore, heightPlus5) - }, - expFrozen: true, - expPass: true, - }, - { - name: "unsuccessful update with incorrect header chain-id", - setup: func(suite *TendermintTestSuite) { - clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) - consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - newHeader = suite.chainA.CreateTMClientHeader("ethermint", int64(heightPlus1.RevisionHeight), height, suite.headerTime, suite.valSet, suite.valSet, suite.valSet, suite.signers) - currentTime = suite.now - }, - expFrozen: false, - expPass: false, - }, - { - name: "unsuccessful update to a future revision", - setup: func(suite *TendermintTestSuite) { - clientState = types.NewClientState(chainIDRevision0, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) - consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - newHeader = suite.chainA.CreateTMClientHeader(chainIDRevision1, 1, height, suite.headerTime, suite.valSet, suite.valSet, suite.valSet, suite.signers) - currentTime = suite.now - }, - expPass: false, - }, - { - name: "unsuccessful update: header height revision and trusted height revision mismatch", - setup: func(suite *TendermintTestSuite) { - clientState = types.NewClientState(chainIDRevision1, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, clienttypes.NewHeight(1, 1), commitmenttypes.GetSDKSpecs(), upgradePath, false, false) - consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - newHeader = suite.chainA.CreateTMClientHeader(chainIDRevision1, 3, height, suite.headerTime, suite.valSet, suite.valSet, suite.valSet, suite.signers) - currentTime = suite.now - }, - expFrozen: false, - expPass: false, - }, - { - name: "unsuccessful update with next height: update header mismatches nextValSetHash", - setup: func(suite *TendermintTestSuite) { - clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) - consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus1.RevisionHeight), height, suite.headerTime, bothValSet, bothValSet, suite.valSet, bothSigners) - currentTime = suite.now - }, - expFrozen: false, - expPass: false, - }, - { - name: "unsuccessful update with next height: update header mismatches different nextValSetHash", - setup: func(suite *TendermintTestSuite) { - clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) - consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), bothValSet.Hash()) - newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus1.RevisionHeight), height, suite.headerTime, suite.valSet, suite.valSet, bothValSet, suite.signers) - currentTime = suite.now - }, - expFrozen: false, - expPass: false, - }, - { - name: "unsuccessful update with future height: too much change in validator set", - setup: func(suite *TendermintTestSuite) { - clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) - consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus5.RevisionHeight), height, suite.headerTime, altValSet, altValSet, suite.valSet, altSigners) - currentTime = suite.now - }, - expFrozen: false, - expPass: false, - }, - { - name: "unsuccessful updates, passed in incorrect trusted validators for given consensus state", - setup: func(suite *TendermintTestSuite) { - clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) - consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus5.RevisionHeight), height, suite.headerTime, bothValSet, bothValSet, bothValSet, bothSigners) - currentTime = suite.now - }, - expFrozen: false, - expPass: false, - }, - { - name: "unsuccessful update: trusting period has passed since last client timestamp", - setup: func(suite *TendermintTestSuite) { - clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) - consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus1.RevisionHeight), height, suite.headerTime, suite.valSet, suite.valSet, suite.valSet, suite.signers) - // make current time pass trusting period from last timestamp on clientstate - currentTime = suite.now.Add(trustingPeriod) - }, - expFrozen: false, - expPass: false, - }, - { - name: "unsuccessful update: header timestamp is past current timestamp", - setup: func(suite *TendermintTestSuite) { - clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) - consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus1.RevisionHeight), height, suite.now.Add(time.Minute), suite.valSet, suite.valSet, suite.valSet, suite.signers) - currentTime = suite.now - }, - expFrozen: false, - expPass: false, - }, - { - name: "unsuccessful update: header timestamp is not past last client timestamp", - setup: func(suite *TendermintTestSuite) { - clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) - consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus1.RevisionHeight), height, suite.clientTime, suite.valSet, suite.valSet, suite.valSet, suite.signers) - currentTime = suite.now - }, - expFrozen: false, - expPass: false, - }, - { - name: "header basic validation failed", - setup: func(suite *TendermintTestSuite) { - clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) - consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus1.RevisionHeight), height, suite.headerTime, suite.valSet, suite.valSet, suite.valSet, suite.signers) - // cause new header to fail validatebasic by changing commit height to mismatch header height - newHeader.SignedHeader.Commit.Height = revisionHeight - 1 - currentTime = suite.now - }, - expFrozen: false, - expPass: false, - }, - { - name: "header height < consensus height", - setup: func(suite *TendermintTestSuite) { - clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, clienttypes.NewHeight(height.RevisionNumber, heightPlus5.RevisionHeight), commitmenttypes.GetSDKSpecs(), upgradePath, false, false) - consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - // Make new header at height less than latest client state - newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightMinus1.RevisionHeight), height, suite.headerTime, suite.valSet, suite.valSet, suite.valSet, suite.signers) - currentTime = suite.now - }, - expFrozen: false, - expPass: false, - }, - } - - for i, tc := range testCases { - tc := tc - suite.Run(fmt.Sprintf("Case: %s", tc.name), func() { - suite.SetupTest() // reset metadata writes - // Create bothValSet with both suite validator and altVal. Would be valid update - bothValSet, bothSigners = getBothSigners(suite, altVal, altPrivVal) - - consStateHeight = height // must be explicitly changed - // setup test - tc.setup(suite) - - // Set current timestamp in context - ctx := suite.chainA.GetContext().WithBlockTime(currentTime) - - // Set trusted consensus state in client store - suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(ctx, clientID, consStateHeight, consensusState) - - height := newHeader.GetHeight() - expectedConsensus := &types.ConsensusState{ - Timestamp: newHeader.GetTime(), - Root: commitmenttypes.NewMerkleRoot(newHeader.Header.GetAppHash()), - NextValidatorsHash: newHeader.Header.NextValidatorsHash, - } - - newClientState, consensusState, err := clientState.CheckHeaderAndUpdateState( - ctx, - suite.cdc, - suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), clientID), // pass in clientID prefixed clientStore - newHeader, - ) - - if tc.expPass { - suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.name) - - suite.Require().Equal(tc.expFrozen, !newClientState.(*types.ClientState).FrozenHeight.IsZero(), "client state status is unexpected after update") - - // further writes only happen if update is not misbehaviour - if !tc.expFrozen { - // Determine if clientState should be updated or not - // TODO: check the entire Height struct once GetLatestHeight returns clienttypes.Height - if height.GT(clientState.LatestHeight) { - // Header Height is greater than clientState latest Height, clientState should be updated with header.GetHeight() - suite.Require().Equal(height, newClientState.GetLatestHeight(), "clientstate height did not update") - } else { - // Update will add past consensus state, clientState should not be updated at all - suite.Require().Equal(clientState.LatestHeight, newClientState.GetLatestHeight(), "client state height updated for past header") - } - - suite.Require().Equal(expectedConsensus, consensusState, "valid test case %d failed: %s", i, tc.name) - } - } else { - suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.name) - suite.Require().Nil(newClientState, "invalid test case %d passed: %s", i, tc.name) - suite.Require().Nil(consensusState, "invalid test case %d passed: %s", i, tc.name) - } - }) - } -} - -func (suite *TendermintTestSuite) TestPruneConsensusState() { - // create path and setup clients - path := ibctesting.NewPath(suite.chainA, suite.chainB) - suite.coordinator.SetupClients(path) - - // get the first height as it will be pruned first. - var pruneHeight exported.Height - getFirstHeightCb := func(height exported.Height) bool { - pruneHeight = height - return true - } - ctx := path.EndpointA.Chain.GetContext() - clientStore := path.EndpointA.Chain.App.GetIBCKeeper().ClientKeeper.ClientStore(ctx, path.EndpointA.ClientID) - err := types.IterateConsensusStateAscending(clientStore, getFirstHeightCb) - suite.Require().Nil(err) - - // this height will be expired but not pruned - path.EndpointA.UpdateClient() - expiredHeight := path.EndpointA.GetClientState().GetLatestHeight() - - // expected values that must still remain in store after pruning - expectedConsState, ok := path.EndpointA.Chain.GetConsensusState(path.EndpointA.ClientID, expiredHeight) - suite.Require().True(ok) - ctx = path.EndpointA.Chain.GetContext() - clientStore = path.EndpointA.Chain.App.GetIBCKeeper().ClientKeeper.ClientStore(ctx, path.EndpointA.ClientID) - expectedProcessTime, ok := types.GetProcessedTime(clientStore, expiredHeight) - suite.Require().True(ok) - expectedProcessHeight, ok := types.GetProcessedHeight(clientStore, expiredHeight) - suite.Require().True(ok) - expectedConsKey := types.GetIterationKey(clientStore, expiredHeight) - suite.Require().NotNil(expectedConsKey) - - // Increment the time by a week - suite.coordinator.IncrementTimeBy(7 * 24 * time.Hour) - - // create the consensus state that can be used as trusted height for next update - path.EndpointA.UpdateClient() - - // Increment the time by another week, then update the client. - // This will cause the first two consensus states to become expired. - suite.coordinator.IncrementTimeBy(7 * 24 * time.Hour) - path.EndpointA.UpdateClient() - - ctx = path.EndpointA.Chain.GetContext() - clientStore = path.EndpointA.Chain.App.GetIBCKeeper().ClientKeeper.ClientStore(ctx, path.EndpointA.ClientID) - - // check that the first expired consensus state got deleted along with all associated metadata - consState, ok := path.EndpointA.Chain.GetConsensusState(path.EndpointA.ClientID, pruneHeight) - suite.Require().Nil(consState, "expired consensus state not pruned") - suite.Require().False(ok) - // check processed time metadata is pruned - processTime, ok := types.GetProcessedTime(clientStore, pruneHeight) - suite.Require().Equal(uint64(0), processTime, "processed time metadata not pruned") - suite.Require().False(ok) - processHeight, ok := types.GetProcessedHeight(clientStore, pruneHeight) - suite.Require().Nil(processHeight, "processed height metadata not pruned") - suite.Require().False(ok) - - // check iteration key metadata is pruned - consKey := types.GetIterationKey(clientStore, pruneHeight) - suite.Require().Nil(consKey, "iteration key not pruned") - - // check that second expired consensus state doesn't get deleted - // this ensures that there is a cap on gas cost of UpdateClient - consState, ok = path.EndpointA.Chain.GetConsensusState(path.EndpointA.ClientID, expiredHeight) - suite.Require().Equal(expectedConsState, consState, "consensus state incorrectly pruned") - suite.Require().True(ok) - // check processed time metadata is not pruned - processTime, ok = types.GetProcessedTime(clientStore, expiredHeight) - suite.Require().Equal(expectedProcessTime, processTime, "processed time metadata incorrectly pruned") - suite.Require().True(ok) - - // check processed height metadata is not pruned - processHeight, ok = types.GetProcessedHeight(clientStore, expiredHeight) - suite.Require().Equal(expectedProcessHeight, processHeight, "processed height metadata incorrectly pruned") - suite.Require().True(ok) - - // check iteration key metadata is not pruned - consKey = types.GetIterationKey(clientStore, expiredHeight) - suite.Require().Equal(expectedConsKey, consKey, "iteration key incorrectly pruned") -} diff --git a/modules/light-clients/07-tendermint/update.go b/modules/light-clients/07-tendermint/update.go new file mode 100644 index 00000000000..fbd70e0fb27 --- /dev/null +++ b/modules/light-clients/07-tendermint/update.go @@ -0,0 +1,223 @@ +package tendermint + +import ( + "bytes" + "fmt" + + errorsmod "cosmossdk.io/errors" + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/tendermint/tendermint/light" + tmtypes "github.com/tendermint/tendermint/types" + + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + commitmenttypes "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + "github.com/cosmos/ibc-go/v7/modules/core/exported" +) + +// VerifyClientMessage checks if the clientMessage is of type Header or Misbehaviour and verifies the message +func (cs *ClientState) VerifyClientMessage( + ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, + clientMsg exported.ClientMessage, +) error { + switch msg := clientMsg.(type) { + case *Header: + return cs.verifyHeader(ctx, clientStore, cdc, msg) + case *Misbehaviour: + return cs.verifyMisbehaviour(ctx, clientStore, cdc, msg) + default: + return clienttypes.ErrInvalidClientType + } +} + +// verifyHeader returns an error if: +// - the client or header provided are not parseable to tendermint types +// - the header is invalid +// - header height is less than or equal to the trusted header height +// - header revision is not equal to trusted header revision +// - header valset commit verification fails +// - header timestamp is past the trusting period in relation to the consensus state +// - header timestamp is less than or equal to the consensus state timestamp +func (cs *ClientState) verifyHeader( + ctx sdk.Context, clientStore sdk.KVStore, cdc codec.BinaryCodec, + header *Header, +) error { + currentTimestamp := ctx.BlockTime() + + // Retrieve trusted consensus states for each Header in misbehaviour + consState, found := GetConsensusState(clientStore, cdc, header.TrustedHeight) + if !found { + return errorsmod.Wrapf(clienttypes.ErrConsensusStateNotFound, "could not get trusted consensus state from clientStore for Header at TrustedHeight: %s", header.TrustedHeight) + } + + if err := checkTrustedHeader(header, consState); err != nil { + return err + } + + // UpdateClient only accepts updates with a header at the same revision + // as the trusted consensus state + if header.GetHeight().GetRevisionNumber() != header.TrustedHeight.RevisionNumber { + return errorsmod.Wrapf( + ErrInvalidHeaderHeight, + "header height revision %d does not match trusted header revision %d", + header.GetHeight().GetRevisionNumber(), header.TrustedHeight.RevisionNumber, + ) + } + + tmTrustedValidators, err := tmtypes.ValidatorSetFromProto(header.TrustedValidators) + if err != nil { + return errorsmod.Wrap(err, "trusted validator set in not tendermint validator set type") + } + + tmSignedHeader, err := tmtypes.SignedHeaderFromProto(header.SignedHeader) + if err != nil { + return errorsmod.Wrap(err, "signed header in not tendermint signed header type") + } + + tmValidatorSet, err := tmtypes.ValidatorSetFromProto(header.ValidatorSet) + if err != nil { + return errorsmod.Wrap(err, "validator set in not tendermint validator set type") + } + + // assert header height is newer than consensus state + if header.GetHeight().LTE(header.TrustedHeight) { + return errorsmod.Wrapf( + clienttypes.ErrInvalidHeader, + "header height ≤ consensus state height (%s ≤ %s)", header.GetHeight(), header.TrustedHeight, + ) + } + + // Construct a trusted header using the fields in consensus state + // Only Height, Time, and NextValidatorsHash are necessary for verification + // NOTE: updates must be within the same revision + trustedHeader := tmtypes.Header{ + ChainID: cs.GetChainID(), + Height: int64(header.TrustedHeight.RevisionHeight), + Time: consState.Timestamp, + NextValidatorsHash: consState.NextValidatorsHash, + } + signedHeader := tmtypes.SignedHeader{ + Header: &trustedHeader, + } + + // Verify next header with the passed-in trustedVals + // - asserts trusting period not passed + // - assert header timestamp is not past the trusting period + // - assert header timestamp is past latest stored consensus state timestamp + // - assert that a TrustLevel proportion of TrustedValidators signed new Commit + err = light.Verify( + &signedHeader, + tmTrustedValidators, tmSignedHeader, tmValidatorSet, + cs.TrustingPeriod, currentTimestamp, cs.MaxClockDrift, cs.TrustLevel.ToTendermint(), + ) + if err != nil { + return errorsmod.Wrap(err, "failed to verify header") + } + + return nil +} + +// UpdateState may be used to either create a consensus state for: +// - a future height greater than the latest client state height +// - a past height that was skipped during bisection +// If we are updating to a past height, a consensus state is created for that height to be persisted in client store +// If we are updating to a future height, the consensus state is created and the client state is updated to reflect +// the new latest height +// A list containing the updated consensus height is returned. +// UpdateState must only be used to update within a single revision, thus header revision number and trusted height's revision +// number must be the same. To update to a new revision, use a separate upgrade path +// UpdateState will prune the oldest consensus state if it is expired. +func (cs ClientState) UpdateState(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, clientMsg exported.ClientMessage) []exported.Height { + header, ok := clientMsg.(*Header) + if !ok { + panic(fmt.Errorf("expected type %T, got %T", &Header{}, clientMsg)) + } + + cs.pruneOldestConsensusState(ctx, cdc, clientStore) + + // check for duplicate update + if consensusState, _ := GetConsensusState(clientStore, cdc, header.GetHeight()); consensusState != nil { + // perform no-op + return []exported.Height{header.GetHeight()} + } + + height := header.GetHeight().(clienttypes.Height) + if height.GT(cs.LatestHeight) { + cs.LatestHeight = height + } + + consensusState := &ConsensusState{ + Timestamp: header.GetTime(), + Root: commitmenttypes.NewMerkleRoot(header.Header.GetAppHash()), + NextValidatorsHash: header.Header.NextValidatorsHash, + } + + // set client state, consensus state and asssociated metadata + setClientState(clientStore, cdc, &cs) + setConsensusState(clientStore, cdc, consensusState, header.GetHeight()) + setConsensusMetadata(ctx, clientStore, header.GetHeight()) + + return []exported.Height{height} +} + +// pruneOldestConsensusState will retrieve the earliest consensus state for this clientID and check if it is expired. If it is, +// that consensus state will be pruned from store along with all associated metadata. This will prevent the client store from +// becoming bloated with expired consensus states that can no longer be used for updates and packet verification. +func (cs ClientState) pruneOldestConsensusState(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore) { + // Check the earliest consensus state to see if it is expired, if so then set the prune height + // so that we can delete consensus state and all associated metadata. + var ( + pruneHeight exported.Height + ) + + pruneCb := func(height exported.Height) bool { + consState, found := GetConsensusState(clientStore, cdc, height) + // this error should never occur + if !found { + panic(errorsmod.Wrapf(clienttypes.ErrConsensusStateNotFound, "failed to retrieve consensus state at height: %s", height)) + } + + if cs.IsExpired(consState.Timestamp, ctx.BlockTime()) { + pruneHeight = height + } + + return true + } + + IterateConsensusStateAscending(clientStore, pruneCb) + + // if pruneHeight is set, delete consensus state and metadata + if pruneHeight != nil { + deleteConsensusState(clientStore, pruneHeight) + deleteConsensusMetadata(clientStore, pruneHeight) + } +} + +// UpdateStateOnMisbehaviour updates state upon misbehaviour, freezing the ClientState. This method should only be called when misbehaviour is detected +// as it does not perform any misbehaviour checks. +func (cs ClientState) UpdateStateOnMisbehaviour(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, _ exported.ClientMessage) { + cs.FrozenHeight = FrozenHeight + + clientStore.Set(host.ClientStateKey(), clienttypes.MustMarshalClientState(cdc, &cs)) +} + +// checkTrustedHeader checks that consensus state matches trusted fields of Header +func checkTrustedHeader(header *Header, consState *ConsensusState) error { + tmTrustedValidators, err := tmtypes.ValidatorSetFromProto(header.TrustedValidators) + if err != nil { + return errorsmod.Wrap(err, "trusted validator set in not tendermint validator set type") + } + + // assert that trustedVals is NextValidators of last trusted header + // to do this, we check that trustedVals.Hash() == consState.NextValidatorsHash + tvalHash := tmTrustedValidators.Hash() + if !bytes.Equal(consState.NextValidatorsHash, tvalHash) { + return errorsmod.Wrapf( + ErrInvalidValidatorSet, + "trusted validators %s, does not hash to latest trusted validators. Expected: %X, got: %X", + header.TrustedValidators, consState.NextValidatorsHash, tvalHash, + ) + } + return nil +} diff --git a/modules/light-clients/07-tendermint/update_test.go b/modules/light-clients/07-tendermint/update_test.go new file mode 100644 index 00000000000..81374e6ffb1 --- /dev/null +++ b/modules/light-clients/07-tendermint/update_test.go @@ -0,0 +1,833 @@ +package tendermint_test + +import ( + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" + tmtypes "github.com/tendermint/tendermint/types" + + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + commitmenttypes "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + "github.com/cosmos/ibc-go/v7/modules/core/exported" + ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" + ibctesting "github.com/cosmos/ibc-go/v7/testing" + ibctestingmock "github.com/cosmos/ibc-go/v7/testing/mock" +) + +func (suite *TendermintTestSuite) TestVerifyHeader() { + var ( + path *ibctesting.Path + header *ibctm.Header + ) + + // Setup different validators and signers for testing different types of updates + altPrivVal := ibctestingmock.NewPV() + altPubKey, err := altPrivVal.GetPubKey() + suite.Require().NoError(err) + + revisionHeight := int64(height.RevisionHeight) + + // create modified heights to use for test-cases + altVal := tmtypes.NewValidator(altPubKey, 100) + // Create alternative validator set with only altVal, invalid update (too much change in valSet) + altValSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{altVal}) + altSigners := getAltSigners(altVal, altPrivVal) + + testCases := []struct { + name string + malleate func() + expPass bool + }{ + { + name: "success", + malleate: func() {}, + expPass: true, + }, + { + name: "successful verify header for header with a previous height", + malleate: func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + // passing the CurrentHeader.Height as the block height as it will become a previous height once we commit N blocks + header = suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height, trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers) + + // commit some blocks so that the created Header now has a previous height as the BlockHeight + suite.coordinator.CommitNBlocks(suite.chainB, 5) + + err = path.EndpointA.UpdateClient() + suite.Require().NoError(err) + }, + expPass: true, + }, + { + name: "successful verify header: header with future height and different validator set", + malleate: func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + // Create bothValSet with both suite validator and altVal + bothValSet := tmtypes.NewValidatorSet(append(suite.chainB.Vals.Validators, altVal)) + bothSigners := suite.chainB.Signers + bothSigners[altVal.Address.String()] = altPrivVal + + header = suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height+5, trustedHeight, suite.chainB.CurrentHeader.Time, bothValSet, suite.chainB.NextVals, trustedVals, bothSigners) + }, + expPass: true, + }, + { + name: "successful verify header: header with next height and different validator set", + malleate: func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + // Create bothValSet with both suite validator and altVal + bothValSet := tmtypes.NewValidatorSet(append(suite.chainB.Vals.Validators, altVal)) + bothSigners := suite.chainB.Signers + bothSigners[altVal.Address.String()] = altPrivVal + + header = suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height, trustedHeight, suite.chainB.CurrentHeader.Time, bothValSet, suite.chainB.NextVals, trustedVals, bothSigners) + }, + expPass: true, + }, + { + name: "unsuccessful updates, passed in incorrect trusted validators for given consensus state", + malleate: func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + // Create bothValSet with both suite validator and altVal + bothValSet := tmtypes.NewValidatorSet(append(suite.chainB.Vals.Validators, altVal)) + bothSigners := suite.chainB.Signers + bothSigners[altVal.Address.String()] = altPrivVal + + header = suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height+1, trustedHeight, suite.chainB.CurrentHeader.Time, bothValSet, bothValSet, bothValSet, bothSigners) + }, + expPass: false, + }, + { + name: "unsuccessful verify header with next height: update header mismatches nextValSetHash", + malleate: func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + // this will err as altValSet.Hash() != consState.NextValidatorsHash + header = suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height+1, trustedHeight, suite.chainB.CurrentHeader.Time, altValSet, altValSet, trustedVals, altSigners) + }, + expPass: false, + }, + { + name: "unsuccessful update with future height: too much change in validator set", + malleate: func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + header = suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height+1, trustedHeight, suite.chainB.CurrentHeader.Time, altValSet, altValSet, trustedVals, altSigners) + }, + expPass: false, + }, + { + name: "unsuccessful verify header: header height revision and trusted height revision mismatch", + malleate: func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + header = suite.chainB.CreateTMClientHeader(chainIDRevision1, 3, trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers) + }, + expPass: false, + }, + { + name: "unsuccessful verify header: header height < consensus height", + malleate: func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + heightMinus1 := clienttypes.NewHeight(trustedHeight.RevisionNumber, trustedHeight.RevisionHeight-1) + + // Make new header at height less than latest client state + header = suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(heightMinus1.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers) + }, + expPass: false, + }, + { + name: "unsuccessful verify header: header basic validation failed", + malleate: func() { + // cause header to fail validatebasic by changing commit height to mismatch header height + header.SignedHeader.Commit.Height = revisionHeight - 1 + }, + expPass: false, + }, + { + name: "unsuccessful verify header: header timestamp is not past last client timestamp", + malleate: func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight)) + suite.Require().True(found) + + header = suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height+1, trustedHeight, suite.chainB.CurrentHeader.Time.Add(-time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers) + }, + expPass: false, + }, + { + name: "unsuccessful verify header: header with incorrect header chain-id", + malleate: func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight)) + suite.Require().True(found) + + header = suite.chainB.CreateTMClientHeader(chainID, suite.chainB.CurrentHeader.Height+1, trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers) + }, + expPass: false, + }, + { + name: "unsuccessful update: trusting period has passed since last client timestamp", + malleate: func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight)) + suite.Require().True(found) + + header = suite.chainA.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height+1, trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers) + + suite.chainB.ExpireClient(ibctesting.TrustingPeriod) + }, + expPass: false, + }, + { + name: "unsuccessful update for a previous revision", + malleate: func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + // passing the CurrentHeader.Height as the block height as it will become an update to previous revision once we upgrade the client + header = suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height, trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers) + + // increment the revision of the chain + err = path.EndpointB.UpgradeChain() + suite.Require().NoError(err) + }, + expPass: false, + }, + { + name: "successful update with identical header to a previous update", + malleate: func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + // passing the CurrentHeader.Height as the block height as it will become a previous height once we commit N blocks + header = suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height, trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers) + + // update client so the header constructed becomes a duplicate + err = path.EndpointA.UpdateClient() + suite.Require().NoError(err) + }, + expPass: true, + }, + + { + name: "unsuccessful update to a future revision", + malleate: func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + header = suite.chainB.CreateTMClientHeader(suite.chainB.ChainID+"-1", suite.chainB.CurrentHeader.Height+5, trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers) + }, + expPass: false, + }, + + { + name: "unsuccessful update: header height revision and trusted height revision mismatch", + malleate: func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + // increment the revision of the chain + err = path.EndpointB.UpgradeChain() + suite.Require().NoError(err) + + header = suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height, trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers) + }, + expPass: false, + }, + } + + for _, tc := range testCases { + tc := tc + suite.SetupTest() + path = ibctesting.NewPath(suite.chainA, suite.chainB) + + err := path.EndpointA.CreateClient() + suite.Require().NoError(err) + + // ensure counterparty state is committed + suite.coordinator.CommitBlock(suite.chainB) + header, err = path.EndpointA.Chain.ConstructUpdateTMClientHeader(path.EndpointA.Counterparty.Chain, path.EndpointA.ClientID) + suite.Require().NoError(err) + + tc.malleate() + + clientState := path.EndpointA.GetClientState() + + clientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) + + err = clientState.VerifyClientMessage(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), clientStore, header) + + if tc.expPass { + suite.Require().NoError(err, tc.name) + } else { + suite.Require().Error(err) + } + } +} + +func (suite *TendermintTestSuite) TestUpdateState() { + var ( + path *ibctesting.Path + clientMessage exported.ClientMessage + clientStore sdk.KVStore + consensusHeights []exported.Height + pruneHeight clienttypes.Height + prevClientState exported.ClientState + prevConsensusState exported.ConsensusState + ) + + testCases := []struct { + name string + malleate func() + expResult func() + expPass bool + }{ + { + "success with height later than latest height", func() { + tmHeader, ok := clientMessage.(*ibctm.Header) + suite.Require().True(ok) + suite.Require().True(path.EndpointA.GetClientState().GetLatestHeight().LT(tmHeader.GetHeight())) + }, + func() { + tmHeader, ok := clientMessage.(*ibctm.Header) + suite.Require().True(ok) + + clientState := path.EndpointA.GetClientState() + suite.Require().True(clientState.GetLatestHeight().EQ(tmHeader.GetHeight())) // new update, updated client state should have changed + suite.Require().True(clientState.GetLatestHeight().EQ(consensusHeights[0])) + }, true, + }, + { + "success with height earlier than latest height", func() { + // commit a block so the pre-created ClientMessage + // isn't used to update the client to a newer height + suite.coordinator.CommitBlock(suite.chainB) + err := path.EndpointA.UpdateClient() + suite.Require().NoError(err) + + tmHeader, ok := clientMessage.(*ibctm.Header) + suite.Require().True(ok) + suite.Require().True(path.EndpointA.GetClientState().GetLatestHeight().GT(tmHeader.GetHeight())) + + prevClientState = path.EndpointA.GetClientState() + }, + func() { + clientState := path.EndpointA.GetClientState() + suite.Require().Equal(clientState, prevClientState) // fill in height, no change to client state + suite.Require().True(clientState.GetLatestHeight().GT(consensusHeights[0])) + }, true, + }, + { + "success with duplicate header", func() { + // update client in advance + err := path.EndpointA.UpdateClient() + suite.Require().NoError(err) + + // use the same header which just updated the client + clientMessage, err = path.EndpointA.Chain.ConstructUpdateTMClientHeader(path.EndpointA.Counterparty.Chain, path.EndpointA.ClientID) + suite.Require().NoError(err) + + tmHeader, ok := clientMessage.(*ibctm.Header) + suite.Require().True(ok) + suite.Require().Equal(path.EndpointA.GetClientState().GetLatestHeight(), tmHeader.GetHeight()) + + prevClientState = path.EndpointA.GetClientState() + prevConsensusState = path.EndpointA.GetConsensusState(tmHeader.GetHeight()) + }, + func() { + clientState := path.EndpointA.GetClientState() + suite.Require().Equal(clientState, prevClientState) + suite.Require().True(clientState.GetLatestHeight().EQ(consensusHeights[0])) + + tmHeader, ok := clientMessage.(*ibctm.Header) + suite.Require().True(ok) + suite.Require().Equal(path.EndpointA.GetConsensusState(tmHeader.GetHeight()), prevConsensusState) + }, true, + }, + { + "success with pruned consensus state", func() { + // this height will be expired and pruned + err := path.EndpointA.UpdateClient() + suite.Require().NoError(err) + pruneHeight = path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + // Increment the time by a week + suite.coordinator.IncrementTimeBy(7 * 24 * time.Hour) + + // create the consensus state that can be used as trusted height for next update + err = path.EndpointA.UpdateClient() + suite.Require().NoError(err) + + // Increment the time by another week, then update the client. + // This will cause the first two consensus states to become expired. + suite.coordinator.IncrementTimeBy(7 * 24 * time.Hour) + err = path.EndpointA.UpdateClient() + suite.Require().NoError(err) + + // ensure counterparty state is committed + suite.coordinator.CommitBlock(suite.chainB) + clientMessage, err = path.EndpointA.Chain.ConstructUpdateTMClientHeader(path.EndpointA.Counterparty.Chain, path.EndpointA.ClientID) + suite.Require().NoError(err) + }, + func() { + tmHeader, ok := clientMessage.(*ibctm.Header) + suite.Require().True(ok) + + clientState := path.EndpointA.GetClientState() + suite.Require().True(clientState.GetLatestHeight().EQ(tmHeader.GetHeight())) // new update, updated client state should have changed + suite.Require().True(clientState.GetLatestHeight().EQ(consensusHeights[0])) + + // ensure consensus state was pruned + _, found := path.EndpointA.Chain.GetConsensusState(path.EndpointA.ClientID, pruneHeight) + suite.Require().False(found) + }, true, + }, + { + "success with pruned consensus state using duplicate header", func() { + // this height will be expired and pruned + err := path.EndpointA.UpdateClient() + suite.Require().NoError(err) + pruneHeight = path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + // assert that a consensus state exists at the prune height + consensusState, found := path.EndpointA.Chain.GetConsensusState(path.EndpointA.ClientID, pruneHeight) + suite.Require().True(found) + suite.Require().NotNil(consensusState) + + // Increment the time by a week + suite.coordinator.IncrementTimeBy(7 * 24 * time.Hour) + + // create the consensus state that can be used as trusted height for next update + err = path.EndpointA.UpdateClient() + suite.Require().NoError(err) + + // Increment the time by another week, then update the client. + // This will cause the first two consensus states to become expired. + suite.coordinator.IncrementTimeBy(7 * 24 * time.Hour) + err = path.EndpointA.UpdateClient() + suite.Require().NoError(err) + + // use the same header which just updated the client + clientMessage, err = path.EndpointA.Chain.ConstructUpdateTMClientHeader(path.EndpointA.Counterparty.Chain, path.EndpointA.ClientID) + suite.Require().NoError(err) + }, + func() { + tmHeader, ok := clientMessage.(*ibctm.Header) + suite.Require().True(ok) + + clientState := path.EndpointA.GetClientState() + suite.Require().True(clientState.GetLatestHeight().EQ(tmHeader.GetHeight())) // new update, updated client state should have changed + suite.Require().True(clientState.GetLatestHeight().EQ(consensusHeights[0])) + + // ensure consensus state was pruned + _, found := path.EndpointA.Chain.GetConsensusState(path.EndpointA.ClientID, pruneHeight) + suite.Require().False(found) + }, true, + }, + { + "invalid ClientMessage type", func() { + clientMessage = &ibctm.Misbehaviour{} + }, + func() {}, + false, + }, + } + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + suite.SetupTest() // reset + pruneHeight = clienttypes.ZeroHeight() + path = ibctesting.NewPath(suite.chainA, suite.chainB) + + err := path.EndpointA.CreateClient() + suite.Require().NoError(err) + + // ensure counterparty state is committed + suite.coordinator.CommitBlock(suite.chainB) + clientMessage, err = path.EndpointA.Chain.ConstructUpdateTMClientHeader(path.EndpointA.Counterparty.Chain, path.EndpointA.ClientID) + suite.Require().NoError(err) + + tc.malleate() + + clientState := path.EndpointA.GetClientState() + clientStore = suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) + + if tc.expPass { + consensusHeights = clientState.UpdateState(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), clientStore, clientMessage) + + header := clientMessage.(*ibctm.Header) + expConsensusState := &ibctm.ConsensusState{ + Timestamp: header.GetTime(), + Root: commitmenttypes.NewMerkleRoot(header.Header.GetAppHash()), + NextValidatorsHash: header.Header.NextValidatorsHash, + } + + bz := clientStore.Get(host.ConsensusStateKey(header.GetHeight())) + updatedConsensusState := clienttypes.MustUnmarshalConsensusState(suite.chainA.App.AppCodec(), bz) + + suite.Require().Equal(expConsensusState, updatedConsensusState) + + } else { + suite.Require().Panics(func() { + clientState.UpdateState(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), clientStore, clientMessage) + }) + } + + // perform custom checks + tc.expResult() + }) + } +} + +func (suite *TendermintTestSuite) TestPruneConsensusState() { + // create path and setup clients + path := ibctesting.NewPath(suite.chainA, suite.chainB) + suite.coordinator.SetupClients(path) + + // get the first height as it will be pruned first. + var pruneHeight exported.Height + getFirstHeightCb := func(height exported.Height) bool { + pruneHeight = height + return true + } + ctx := path.EndpointA.Chain.GetContext() + clientStore := path.EndpointA.Chain.App.GetIBCKeeper().ClientKeeper.ClientStore(ctx, path.EndpointA.ClientID) + ibctm.IterateConsensusStateAscending(clientStore, getFirstHeightCb) + + // this height will be expired but not pruned + err := path.EndpointA.UpdateClient() + suite.Require().NoError(err) + expiredHeight := path.EndpointA.GetClientState().GetLatestHeight() + + // expected values that must still remain in store after pruning + expectedConsState, ok := path.EndpointA.Chain.GetConsensusState(path.EndpointA.ClientID, expiredHeight) + suite.Require().True(ok) + ctx = path.EndpointA.Chain.GetContext() + clientStore = path.EndpointA.Chain.App.GetIBCKeeper().ClientKeeper.ClientStore(ctx, path.EndpointA.ClientID) + expectedProcessTime, ok := ibctm.GetProcessedTime(clientStore, expiredHeight) + suite.Require().True(ok) + expectedProcessHeight, ok := ibctm.GetProcessedHeight(clientStore, expiredHeight) + suite.Require().True(ok) + expectedConsKey := ibctm.GetIterationKey(clientStore, expiredHeight) + suite.Require().NotNil(expectedConsKey) + + // Increment the time by a week + suite.coordinator.IncrementTimeBy(7 * 24 * time.Hour) + + // create the consensus state that can be used as trusted height for next update + err = path.EndpointA.UpdateClient() + suite.Require().NoError(err) + + // Increment the time by another week, then update the client. + // This will cause the first two consensus states to become expired. + suite.coordinator.IncrementTimeBy(7 * 24 * time.Hour) + err = path.EndpointA.UpdateClient() + suite.Require().NoError(err) + + ctx = path.EndpointA.Chain.GetContext() + clientStore = path.EndpointA.Chain.App.GetIBCKeeper().ClientKeeper.ClientStore(ctx, path.EndpointA.ClientID) + + // check that the first expired consensus state got deleted along with all associated metadata + consState, ok := path.EndpointA.Chain.GetConsensusState(path.EndpointA.ClientID, pruneHeight) + suite.Require().Nil(consState, "expired consensus state not pruned") + suite.Require().False(ok) + // check processed time metadata is pruned + processTime, ok := ibctm.GetProcessedTime(clientStore, pruneHeight) + suite.Require().Equal(uint64(0), processTime, "processed time metadata not pruned") + suite.Require().False(ok) + processHeight, ok := ibctm.GetProcessedHeight(clientStore, pruneHeight) + suite.Require().Nil(processHeight, "processed height metadata not pruned") + suite.Require().False(ok) + + // check iteration key metadata is pruned + consKey := ibctm.GetIterationKey(clientStore, pruneHeight) + suite.Require().Nil(consKey, "iteration key not pruned") + + // check that second expired consensus state doesn't get deleted + // this ensures that there is a cap on gas cost of UpdateClient + consState, ok = path.EndpointA.Chain.GetConsensusState(path.EndpointA.ClientID, expiredHeight) + suite.Require().Equal(expectedConsState, consState, "consensus state incorrectly pruned") + suite.Require().True(ok) + // check processed time metadata is not pruned + processTime, ok = ibctm.GetProcessedTime(clientStore, expiredHeight) + suite.Require().Equal(expectedProcessTime, processTime, "processed time metadata incorrectly pruned") + suite.Require().True(ok) + + // check processed height metadata is not pruned + processHeight, ok = ibctm.GetProcessedHeight(clientStore, expiredHeight) + suite.Require().Equal(expectedProcessHeight, processHeight, "processed height metadata incorrectly pruned") + suite.Require().True(ok) + + // check iteration key metadata is not pruned + consKey = ibctm.GetIterationKey(clientStore, expiredHeight) + suite.Require().Equal(expectedConsKey, consKey, "iteration key incorrectly pruned") +} + +func (suite *TendermintTestSuite) TestCheckForMisbehaviour() { + var ( + path *ibctesting.Path + clientMessage exported.ClientMessage + ) + + testCases := []struct { + name string + malleate func() + expPass bool + }{ + { + "valid update no misbehaviour", + func() {}, + false, + }, + { + "consensus state already exists, already updated", + func() { + header, ok := clientMessage.(*ibctm.Header) + suite.Require().True(ok) + + consensusState := &ibctm.ConsensusState{ + Timestamp: header.GetTime(), + Root: commitmenttypes.NewMerkleRoot(header.Header.GetAppHash()), + NextValidatorsHash: header.Header.NextValidatorsHash, + } + + tmHeader, ok := clientMessage.(*ibctm.Header) + suite.Require().True(ok) + suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(suite.chainA.GetContext(), path.EndpointA.ClientID, tmHeader.GetHeight(), consensusState) + }, + false, + }, + { + "invalid fork misbehaviour: identical headers", func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + err := path.EndpointA.UpdateClient() + suite.Require().NoError(err) + + height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + misbehaviourHeader := suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers) + clientMessage = &ibctm.Misbehaviour{ + Header1: misbehaviourHeader, + Header2: misbehaviourHeader, + } + }, false, + }, + { + "invalid time misbehaviour: monotonically increasing time", func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + clientMessage = &ibctm.Misbehaviour{ + Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height+3, trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height, trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + } + }, false, + }, + { + "consensus state already exists, app hash mismatch", + func() { + header, ok := clientMessage.(*ibctm.Header) + suite.Require().True(ok) + + consensusState := &ibctm.ConsensusState{ + Timestamp: header.GetTime(), + Root: commitmenttypes.NewMerkleRoot([]byte{}), // empty bytes + NextValidatorsHash: header.Header.NextValidatorsHash, + } + + tmHeader, ok := clientMessage.(*ibctm.Header) + suite.Require().True(ok) + suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(suite.chainA.GetContext(), path.EndpointA.ClientID, tmHeader.GetHeight(), consensusState) + }, + true, + }, + { + "previous consensus state exists and header time is before previous consensus state time", + func() { + header, ok := clientMessage.(*ibctm.Header) + suite.Require().True(ok) + + // offset header timestamp before previous consensus state timestamp + header.Header.Time = header.GetTime().Add(-time.Hour) + }, + true, + }, + { + "next consensus state exists and header time is after next consensus state time", + func() { + header, ok := clientMessage.(*ibctm.Header) + suite.Require().True(ok) + + // commit block and update client, adding a new consensus state + suite.coordinator.CommitBlock(suite.chainB) + err := path.EndpointA.UpdateClient() + suite.Require().NoError(err) + + // increase timestamp of current header + header.Header.Time = header.Header.Time.Add(time.Hour) + }, + true, + }, + { + "valid fork misbehaviour returns true", + func() { + header1, err := path.EndpointA.Chain.ConstructUpdateTMClientHeader(path.EndpointA.Counterparty.Chain, path.EndpointA.ClientID) + suite.Require().NoError(err) + + // commit block and update client + suite.coordinator.CommitBlock(suite.chainB) + err = path.EndpointA.UpdateClient() + suite.Require().NoError(err) + + header2, err := path.EndpointA.Chain.ConstructUpdateTMClientHeader(path.EndpointA.Counterparty.Chain, path.EndpointA.ClientID) + suite.Require().NoError(err) + + // assign the same height, each header will have a different commit hash + header1.Header.Height = header2.Header.Height + + clientMessage = &ibctm.Misbehaviour{ + Header1: header1, + Header2: header2, + ClientId: path.EndpointA.ClientID, + } + }, + true, + }, + { + "valid time misbehaviour: not monotonically increasing time", func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + clientMessage = &ibctm.Misbehaviour{ + Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height+3, trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height, trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + } + }, true, + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + // reset suite to create fresh application state + suite.SetupTest() + path = ibctesting.NewPath(suite.chainA, suite.chainB) + + err := path.EndpointA.CreateClient() + suite.Require().NoError(err) + + // ensure counterparty state is committed + suite.coordinator.CommitBlock(suite.chainB) + clientMessage, err = path.EndpointA.Chain.ConstructUpdateTMClientHeader(path.EndpointA.Counterparty.Chain, path.EndpointA.ClientID) + suite.Require().NoError(err) + + tc.malleate() + + clientState := path.EndpointA.GetClientState() + clientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) + + foundMisbehaviour := clientState.CheckForMisbehaviour( + suite.chainA.GetContext(), + suite.chainA.App.AppCodec(), + clientStore, // pass in clientID prefixed clientStore + clientMessage, + ) + + if tc.expPass { + suite.Require().True(foundMisbehaviour) + } else { + suite.Require().False(foundMisbehaviour) + } + }) + } +} + +func (suite *TendermintTestSuite) TestUpdateStateOnMisbehaviour() { + var path *ibctesting.Path + + testCases := []struct { + name string + malleate func() + expPass bool + }{ + { + "success", + func() {}, + true, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + // reset suite to create fresh application state + suite.SetupTest() + path = ibctesting.NewPath(suite.chainA, suite.chainB) + + err := path.EndpointA.CreateClient() + suite.Require().NoError(err) + + tc.malleate() + + clientState := path.EndpointA.GetClientState() + clientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) + + clientState.UpdateStateOnMisbehaviour(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), clientStore, nil) + + if tc.expPass { + clientStateBz := clientStore.Get(host.ClientStateKey()) + suite.Require().NotEmpty(clientStateBz) + + newClientState := clienttypes.MustUnmarshalClientState(suite.chainA.Codec, clientStateBz) + suite.Require().Equal(frozenHeight, newClientState.(*ibctm.ClientState).FrozenHeight) + } + }) + } +} diff --git a/modules/light-clients/07-tendermint/types/upgrade.go b/modules/light-clients/07-tendermint/upgrade.go similarity index 70% rename from modules/light-clients/07-tendermint/types/upgrade.go rename to modules/light-clients/07-tendermint/upgrade.go index b5b5ebe40f4..abbeca91b90 100644 --- a/modules/light-clients/07-tendermint/types/upgrade.go +++ b/modules/light-clients/07-tendermint/upgrade.go @@ -1,43 +1,44 @@ -package types +package tendermint import ( "fmt" + errorsmod "cosmossdk.io/errors" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - commitmenttypes "github.com/cosmos/ibc-go/v4/modules/core/23-commitment/types" - "github.com/cosmos/ibc-go/v4/modules/core/exported" + ibcerrors "github.com/cosmos/ibc-go/v7/internal/errors" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + commitmenttypes "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/types" + "github.com/cosmos/ibc-go/v7/modules/core/exported" ) // VerifyUpgradeAndUpdateState checks if the upgraded client has been committed by the current client -// It will zero out all client-specific fields (e.g. TrustingPeriod and verify all data +// It will zero out all client-specific fields (e.g. TrustingPeriod) and verify all data // in client state that must be the same across all valid Tendermint clients for the new chain. // VerifyUpgrade will return an error if: // - the upgradedClient is not a Tendermint ClientState -// - the lastest height of the client state does not have the same revision number or has a greater +// - the latest height of the client state does not have the same revision number or has a greater // height than the committed client. -// - the height of upgraded client is not greater than that of current client -// - the latest height of the new client does not match or is greater than the height in committed client -// - any Tendermint chain specified parameter in upgraded client such as ChainID, UnbondingPeriod, -// and ProofSpecs do not match parameters set by committed client +// - the height of upgraded client is not greater than that of current client +// - the latest height of the new client does not match or is greater than the height in committed client +// - any Tendermint chain specified parameter in upgraded client such as ChainID, UnbondingPeriod, +// and ProofSpecs do not match parameters set by committed client func (cs ClientState) VerifyUpgradeAndUpdateState( ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, upgradedClient exported.ClientState, upgradedConsState exported.ConsensusState, proofUpgradeClient, proofUpgradeConsState []byte, -) (exported.ClientState, exported.ConsensusState, error) { +) error { if len(cs.UpgradePath) == 0 { - return nil, nil, sdkerrors.Wrap(clienttypes.ErrInvalidUpgradeClient, "cannot upgrade client, no upgrade path set") + return errorsmod.Wrap(clienttypes.ErrInvalidUpgradeClient, "cannot upgrade client, no upgrade path set") } // last height of current counterparty chain must be client's latest height lastHeight := cs.GetLatestHeight() if !upgradedClient.GetLatestHeight().GT(lastHeight) { - return nil, nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidHeight, "upgraded client height %s must be at greater than current client height %s", + return errorsmod.Wrapf(ibcerrors.ErrInvalidHeight, "upgraded client height %s must be at greater than current client height %s", upgradedClient.GetLatestHeight(), lastHeight) } @@ -46,52 +47,52 @@ func (cs ClientState) VerifyUpgradeAndUpdateState( // counterparty must also commit to the upgraded consensus state at a sub-path under the upgrade path specified tmUpgradeClient, ok := upgradedClient.(*ClientState) if !ok { - return nil, nil, sdkerrors.Wrapf(clienttypes.ErrInvalidClientType, "upgraded client must be Tendermint client. expected: %T got: %T", + return errorsmod.Wrapf(clienttypes.ErrInvalidClientType, "upgraded client must be Tendermint client. expected: %T got: %T", &ClientState{}, upgradedClient) } tmUpgradeConsState, ok := upgradedConsState.(*ConsensusState) if !ok { - return nil, nil, sdkerrors.Wrapf(clienttypes.ErrInvalidConsensus, "upgraded consensus state must be Tendermint consensus state. expected %T, got: %T", + return errorsmod.Wrapf(clienttypes.ErrInvalidConsensus, "upgraded consensus state must be Tendermint consensus state. expected %T, got: %T", &ConsensusState{}, upgradedConsState) } // unmarshal proofs var merkleProofClient, merkleProofConsState commitmenttypes.MerkleProof if err := cdc.Unmarshal(proofUpgradeClient, &merkleProofClient); err != nil { - return nil, nil, sdkerrors.Wrapf(commitmenttypes.ErrInvalidProof, "could not unmarshal client merkle proof: %v", err) + return errorsmod.Wrapf(commitmenttypes.ErrInvalidProof, "could not unmarshal client merkle proof: %v", err) } if err := cdc.Unmarshal(proofUpgradeConsState, &merkleProofConsState); err != nil { - return nil, nil, sdkerrors.Wrapf(commitmenttypes.ErrInvalidProof, "could not unmarshal consensus state merkle proof: %v", err) + return errorsmod.Wrapf(commitmenttypes.ErrInvalidProof, "could not unmarshal consensus state merkle proof: %v", err) } // Must prove against latest consensus state to ensure we are verifying against latest upgrade plan // This verifies that upgrade is intended for the provided revision, since committed client must exist // at this consensus state - consState, err := GetConsensusState(clientStore, cdc, lastHeight) - if err != nil { - return nil, nil, sdkerrors.Wrap(err, "could not retrieve consensus state for lastHeight") + consState, found := GetConsensusState(clientStore, cdc, lastHeight) + if !found { + return errorsmod.Wrap(clienttypes.ErrConsensusStateNotFound, "could not retrieve consensus state for lastHeight") } // Verify client proof - bz, err := cdc.MarshalInterface(upgradedClient) + bz, err := cdc.MarshalInterface(upgradedClient.ZeroCustomFields()) if err != nil { - return nil, nil, sdkerrors.Wrapf(clienttypes.ErrInvalidClient, "could not marshal client state: %v", err) + return errorsmod.Wrapf(clienttypes.ErrInvalidClient, "could not marshal client state: %v", err) } // construct clientState Merkle path upgradeClientPath := constructUpgradeClientMerklePath(cs.UpgradePath, lastHeight) if err := merkleProofClient.VerifyMembership(cs.ProofSpecs, consState.GetRoot(), upgradeClientPath, bz); err != nil { - return nil, nil, sdkerrors.Wrapf(err, "client state proof failed. Path: %s", upgradeClientPath.Pretty()) + return errorsmod.Wrapf(err, "client state proof failed. Path: %s", upgradeClientPath.Pretty()) } // Verify consensus state proof bz, err = cdc.MarshalInterface(upgradedConsState) if err != nil { - return nil, nil, sdkerrors.Wrapf(clienttypes.ErrInvalidConsensus, "could not marshal consensus state: %v", err) + return errorsmod.Wrapf(clienttypes.ErrInvalidConsensus, "could not marshal consensus state: %v", err) } // construct consensus state Merkle path upgradeConsStatePath := constructUpgradeConsStateMerklePath(cs.UpgradePath, lastHeight) if err := merkleProofConsState.VerifyMembership(cs.ProofSpecs, consState.GetRoot(), upgradeConsStatePath, bz); err != nil { - return nil, nil, sdkerrors.Wrapf(err, "consensus state proof failed. Path: %s", upgradeConsStatePath.Pretty()) + return errorsmod.Wrapf(err, "consensus state proof failed. Path: %s", upgradeConsStatePath.Pretty()) } // Construct new client state and consensus state @@ -101,11 +102,10 @@ func (cs ClientState) VerifyUpgradeAndUpdateState( newClientState := NewClientState( tmUpgradeClient.ChainId, cs.TrustLevel, cs.TrustingPeriod, tmUpgradeClient.UnbondingPeriod, cs.MaxClockDrift, tmUpgradeClient.LatestHeight, tmUpgradeClient.ProofSpecs, tmUpgradeClient.UpgradePath, - cs.AllowUpdateAfterExpiry, cs.AllowUpdateAfterMisbehaviour, ) if err := newClientState.Validate(); err != nil { - return nil, nil, sdkerrors.Wrap(err, "updated client state failed basic validation") + return errorsmod.Wrap(err, "updated client state failed basic validation") } // The new consensus state is merely used as a trusted kernel against which headers on the new @@ -119,10 +119,11 @@ func (cs ClientState) VerifyUpgradeAndUpdateState( tmUpgradeConsState.Timestamp, commitmenttypes.NewMerkleRoot([]byte(SentinelRoot)), tmUpgradeConsState.NextValidatorsHash, ) - // set metadata for this consensus state + setClientState(clientStore, cdc, newClientState) + setConsensusState(clientStore, cdc, newConsState, newClientState.LatestHeight) setConsensusMetadata(ctx, clientStore, tmUpgradeClient.LatestHeight) - return newClientState, newConsState, nil + return nil } // construct MerklePath for the committed client from upgradePath diff --git a/modules/light-clients/07-tendermint/types/upgrade_test.go b/modules/light-clients/07-tendermint/upgrade_test.go similarity index 85% rename from modules/light-clients/07-tendermint/types/upgrade_test.go rename to modules/light-clients/07-tendermint/upgrade_test.go index c5f1216e8b8..52c2b7841b8 100644 --- a/modules/light-clients/07-tendermint/types/upgrade_test.go +++ b/modules/light-clients/07-tendermint/upgrade_test.go @@ -1,19 +1,18 @@ -package types_test +package tendermint_test import ( upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - commitmenttypes "github.com/cosmos/ibc-go/v4/modules/core/23-commitment/types" - "github.com/cosmos/ibc-go/v4/modules/core/exported" - "github.com/cosmos/ibc-go/v4/modules/light-clients/07-tendermint/types" - ibctesting "github.com/cosmos/ibc-go/v4/testing" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + commitmenttypes "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/types" + "github.com/cosmos/ibc-go/v7/modules/core/exported" + ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" + ibctesting "github.com/cosmos/ibc-go/v7/testing" ) -var newChainId = "newChainId-1" - func (suite *TendermintTestSuite) TestVerifyUpgrade() { var ( + newChainID string upgradedClient exported.ClientState upgradedConsState exported.ConsensusState lastHeight clienttypes.Height @@ -35,8 +34,8 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { lastHeight = clienttypes.NewHeight(0, uint64(suite.chainB.GetContext().BlockHeight()+1)) // zero custom fields and store in upgrade store - suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) - suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) + suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) //nolint:errcheck // ignore error for test + suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) //nolint:errcheck // ignore error for test // commit upgrade store changes and update clients @@ -55,9 +54,7 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { { name: "successful upgrade to same revision", setup: func() { - upgradedHeight := clienttypes.NewHeight(0, uint64(suite.chainB.GetContext().BlockHeight()+2)) - // don't use -1 suffix in chain id - upgradedClient = types.NewClientState("newChainId", types.DefaultTrustLevel, trustingPeriod, ubdPeriod+trustingPeriod, maxClockDrift, upgradedHeight, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) + upgradedClient = ibctm.NewClientState(suite.chainB.ChainID, ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod+trustingPeriod, maxClockDrift, clienttypes.NewHeight(clienttypes.ParseChainID(suite.chainB.ChainID), upgradedClient.GetLatestHeight().GetRevisionHeight()+10), commitmenttypes.GetSDKSpecs(), upgradePath) upgradedClient = upgradedClient.ZeroCustomFields() upgradedClientBz, err = clienttypes.MarshalClientState(suite.chainA.App.AppCodec(), upgradedClient) suite.Require().NoError(err) @@ -66,8 +63,8 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { lastHeight = clienttypes.NewHeight(0, uint64(suite.chainB.GetContext().BlockHeight()+1)) // zero custom fields and store in upgrade store - suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) - suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) + suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) //nolint:errcheck // ignore error for test + suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) //nolint:errcheck // ignore error for test // commit upgrade store changes and update clients @@ -91,8 +88,8 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { lastHeight = clienttypes.NewHeight(0, uint64(suite.chainB.GetContext().BlockHeight()+10)) // zero custom fields and store in upgrade store - suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) - suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) + suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) //nolint:errcheck // ignore error for test + suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) //nolint:errcheck // ignore error for test // commit upgrade store changes and update clients @@ -112,7 +109,7 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { name: "unsuccessful upgrade: committed client does not have zeroed custom fields", setup: func() { // non-zeroed upgrade client - upgradedClient = types.NewClientState(newChainId, types.DefaultTrustLevel, trustingPeriod, ubdPeriod+trustingPeriod, maxClockDrift, newClientHeight, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) + upgradedClient = ibctm.NewClientState(newChainID, ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod+trustingPeriod, maxClockDrift, newClientHeight, commitmenttypes.GetSDKSpecs(), upgradePath) upgradedClientBz, err = clienttypes.MarshalClientState(suite.chainA.App.AppCodec(), upgradedClient) suite.Require().NoError(err) @@ -120,8 +117,8 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { lastHeight = clienttypes.NewHeight(0, uint64(suite.chainB.GetContext().BlockHeight()+1)) // zero custom fields and store in upgrade store - suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) - suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) + suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) //nolint:errcheck // ignore error for test + suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) //nolint:errcheck // ignore error for test // commit upgrade store changes and update clients @@ -144,11 +141,11 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { lastHeight = clienttypes.NewHeight(0, uint64(suite.chainB.GetContext().BlockHeight()+1)) // zero custom fields and store in upgrade store - suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) - suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) + suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) //nolint:errcheck // ignore error for test + suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) //nolint:errcheck // ignore error for test // change upgradedClient client-specified parameters - upgradedClient = types.NewClientState("wrongchainID", types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, newClientHeight, commitmenttypes.GetSDKSpecs(), upgradePath, true, true) + upgradedClient = ibctm.NewClientState("wrongchainID", ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, newClientHeight, commitmenttypes.GetSDKSpecs(), upgradePath) suite.coordinator.CommitBlock(suite.chainB) err := path.EndpointA.UpdateClient() @@ -166,11 +163,11 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { name: "unsuccessful upgrade: client-specified parameters do not match previous client", setup: func() { // zero custom fields and store in upgrade store - suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) - suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) + suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) //nolint:errcheck // ignore error for test + suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) //nolint:errcheck // ignore error for test // change upgradedClient client-specified parameters - upgradedClient = types.NewClientState(newChainId, types.DefaultTrustLevel, ubdPeriod, ubdPeriod+trustingPeriod, maxClockDrift+5, lastHeight, commitmenttypes.GetSDKSpecs(), upgradePath, true, false) + upgradedClient = ibctm.NewClientState(newChainID, ibctm.DefaultTrustLevel, ubdPeriod, ubdPeriod+trustingPeriod, maxClockDrift+5, lastHeight, commitmenttypes.GetSDKSpecs(), upgradePath) suite.coordinator.CommitBlock(suite.chainB) err := path.EndpointA.UpdateClient() @@ -191,11 +188,11 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { lastHeight = clienttypes.NewHeight(0, uint64(suite.chainB.GetContext().BlockHeight()+1)) // zero custom fields and store in upgrade store - suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) - suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) + suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) //nolint:errcheck // ignore error for test + suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) //nolint:errcheck // ignore error for test // change submitted upgradedConsensusState - upgradedConsState = &types.ConsensusState{ + upgradedConsState = &ibctm.ConsensusState{ NextValidatorsHash: []byte("maliciousValidators"), } @@ -216,7 +213,7 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { { name: "unsuccessful upgrade: client proof unmarshal failed", setup: func() { - suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) + suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) //nolint:errcheck // ignore error for test cs, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.GetClientState(suite.chainA.GetContext(), path.EndpointA.ClientID) suite.Require().True(found) @@ -230,7 +227,7 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { { name: "unsuccessful upgrade: consensus state proof unmarshal failed", setup: func() { - suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) + suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) //nolint:errcheck // ignore error for test cs, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.GetClientState(suite.chainA.GetContext(), path.EndpointA.ClientID) suite.Require().True(found) @@ -249,7 +246,7 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { // upgrade Height is at next block lastHeight = clienttypes.NewHeight(0, uint64(suite.chainB.GetContext().BlockHeight()+1)) - suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) + suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) //nolint:errcheck // ignore error for test cs, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.GetClientState(suite.chainA.GetContext(), path.EndpointA.ClientID) suite.Require().True(found) @@ -267,7 +264,7 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { // upgrade Height is at next block lastHeight = clienttypes.NewHeight(0, uint64(suite.chainB.GetContext().BlockHeight()+1)) - suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) + suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) //nolint:errcheck // ignore error for test cs, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.GetClientState(suite.chainA.GetContext(), path.EndpointA.ClientID) suite.Require().True(found) @@ -284,7 +281,7 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { lastHeight = clienttypes.NewHeight(0, uint64(suite.chainB.GetContext().BlockHeight()+1)) // zero custom fields and store in upgrade store - suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) + suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) //nolint:errcheck // ignore error for test // commit upgrade store changes and update clients @@ -299,7 +296,7 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { proofUpgradedConsState, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedConsStateKey(int64(lastHeight.GetRevisionHeight())), cs.GetLatestHeight().GetRevisionHeight()) // SetClientState with empty upgrade path - tmClient, _ := cs.(*types.ClientState) + tmClient, _ := cs.(*ibctm.ClientState) tmClient.UpgradePath = []string{""} suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), path.EndpointA.ClientID, tmClient) }, @@ -312,7 +309,7 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { lastHeight = clienttypes.NewHeight(0, uint64(suite.chainB.GetContext().BlockHeight()+1)) // zero custom fields and store in upgrade store - suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) + suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) //nolint:errcheck // ignore error for test // commit upgrade store changes and update clients @@ -335,7 +332,7 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { lastHeight = clienttypes.NewHeight(0, uint64(suite.chainB.GetContext().BlockHeight()+100)) // zero custom fields and store in upgrade store - suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) + suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) //nolint:errcheck // ignore error for // commit upgrade store changes and update clients @@ -355,7 +352,7 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { name: "unsuccessful upgrade: client is expired", setup: func() { // zero custom fields and store in upgrade store - suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) + suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) //nolint:errcheck // ignore error for test // commit upgrade store changes and update clients @@ -381,7 +378,7 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { lastHeight = clienttypes.NewHeight(0, uint64(suite.chainB.GetContext().BlockHeight()+1)) // zero custom fields and store in upgrade store - suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) + suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) //nolint:errcheck // ignore error for test // commit upgrade store changes and update clients @@ -401,7 +398,7 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { name: "unsuccessful upgrade: final client is not valid", setup: func() { // new client has smaller unbonding period such that old trusting period is no longer valid - upgradedClient = types.NewClientState(newChainId, types.DefaultTrustLevel, trustingPeriod, trustingPeriod, maxClockDrift, newClientHeight, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) + upgradedClient = ibctm.NewClientState(newChainID, ibctm.DefaultTrustLevel, trustingPeriod, trustingPeriod, maxClockDrift, newClientHeight, commitmenttypes.GetSDKSpecs(), upgradePath) upgradedClientBz, err = clienttypes.MarshalClientState(suite.chainA.App.AppCodec(), upgradedClient) suite.Require().NoError(err) @@ -409,8 +406,8 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { lastHeight = clienttypes.NewHeight(0, uint64(suite.chainB.GetContext().BlockHeight()+1)) // zero custom fields and store in upgrade store - suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) - suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) + suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) //nolint:errcheck // ignore error for testing + suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) //nolint:errcheck // ignore error for testing // commit upgrade store changes and update clients @@ -436,12 +433,20 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { path = ibctesting.NewPath(suite.chainA, suite.chainB) suite.coordinator.SetupClients(path) - upgradedClient = types.NewClientState(newChainId, types.DefaultTrustLevel, trustingPeriod, ubdPeriod+trustingPeriod, maxClockDrift, newClientHeight, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) + + clientState := path.EndpointA.GetClientState().(*ibctm.ClientState) + revisionNumber := clienttypes.ParseChainID(clientState.ChainId) + + var err error + newChainID, err = clienttypes.SetRevisionNumber(clientState.ChainId, revisionNumber+1) + suite.Require().NoError(err) + + upgradedClient = ibctm.NewClientState(newChainID, ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod+trustingPeriod, maxClockDrift, clienttypes.NewHeight(revisionNumber+1, clientState.GetLatestHeight().GetRevisionHeight()+1), commitmenttypes.GetSDKSpecs(), upgradePath) upgradedClient = upgradedClient.ZeroCustomFields() upgradedClientBz, err = clienttypes.MarshalClientState(suite.chainA.App.AppCodec(), upgradedClient) suite.Require().NoError(err) - upgradedConsState = &types.ConsensusState{ + upgradedConsState = &ibctm.ConsensusState{ NextValidatorsHash: []byte("nextValsHash"), } upgradedConsStateBz, err = clienttypes.MarshalConsensusState(suite.chainA.App.AppCodec(), upgradedConsState) @@ -455,7 +460,7 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { // Call ZeroCustomFields on upgraded clients to clear any client-chosen parameters in test-case upgradedClient upgradedClient = upgradedClient.ZeroCustomFields() - clientState, consensusState, err := cs.VerifyUpgradeAndUpdateState( + err = cs.VerifyUpgradeAndUpdateState( suite.chainA.GetContext(), suite.cdc, clientStore, @@ -467,14 +472,15 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { if tc.expPass { suite.Require().NoError(err, "verify upgrade failed on valid case: %s", tc.name) + + clientState := suite.chainA.GetClientState(path.EndpointA.ClientID) suite.Require().NotNil(clientState, "verify upgrade failed on valid case: %s", tc.name) + + consensusState, found := suite.chainA.GetConsensusState(path.EndpointA.ClientID, clientState.GetLatestHeight()) suite.Require().NotNil(consensusState, "verify upgrade failed on valid case: %s", tc.name) + suite.Require().True(found) } else { suite.Require().Error(err, "verify upgrade passed on invalid case: %s", tc.name) - suite.Require().Nil(clientState, "verify upgrade passed on invalid case: %s", tc.name) - - suite.Require().Nil(consensusState, "verify upgrade passed on invalid case: %s", tc.name) - } } } diff --git a/modules/light-clients/09-localhost/doc.go b/modules/light-clients/09-localhost/doc.go deleted file mode 100644 index 40a0f060867..00000000000 --- a/modules/light-clients/09-localhost/doc.go +++ /dev/null @@ -1,5 +0,0 @@ -/* -Package localhost implements a concrete `ConsensusState`, `Header`, -`Misbehaviour` and `Equivocation` types for the loop-back client. -*/ -package localhost diff --git a/modules/light-clients/09-localhost/module.go b/modules/light-clients/09-localhost/module.go deleted file mode 100644 index fd77e2e62b5..00000000000 --- a/modules/light-clients/09-localhost/module.go +++ /dev/null @@ -1,10 +0,0 @@ -package localhost - -import ( - "github.com/cosmos/ibc-go/v4/modules/light-clients/09-localhost/types" -) - -// Name returns the IBC client name -func Name() string { - return types.SubModuleName -} diff --git a/modules/light-clients/09-localhost/types/client_state.go b/modules/light-clients/09-localhost/types/client_state.go deleted file mode 100644 index d75690bee7c..00000000000 --- a/modules/light-clients/09-localhost/types/client_state.go +++ /dev/null @@ -1,340 +0,0 @@ -package types - -import ( - "bytes" - "encoding/binary" - "reflect" - "strings" - - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - connectiontypes "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types" - channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" - "github.com/cosmos/ibc-go/v4/modules/core/exported" -) - -var _ exported.ClientState = (*ClientState)(nil) - -// NewClientState creates a new ClientState instance -func NewClientState(chainID string, height clienttypes.Height) *ClientState { - return &ClientState{ - ChainId: chainID, - Height: height, - } -} - -// GetChainID returns an empty string -func (cs ClientState) GetChainID() string { - return cs.ChainId -} - -// ClientType is localhost. -func (cs ClientState) ClientType() string { - return exported.Localhost -} - -// GetLatestHeight returns the latest height stored. -func (cs ClientState) GetLatestHeight() exported.Height { - return cs.Height -} - -// Status always returns Active. The localhost status cannot be changed. -func (cs ClientState) Status(_ sdk.Context, _ sdk.KVStore, _ codec.BinaryCodec, -) exported.Status { - return exported.Active -} - -// Validate performs a basic validation of the client state fields. -func (cs ClientState) Validate() error { - if strings.TrimSpace(cs.ChainId) == "" { - return sdkerrors.Wrap(sdkerrors.ErrInvalidChainID, "chain id cannot be blank") - } - if cs.Height.RevisionHeight == 0 { - return sdkerrors.Wrapf(sdkerrors.ErrInvalidHeight, "local revision height cannot be zero") - } - return nil -} - -// ZeroCustomFields returns the same client state since there are no custom fields in localhost -func (cs ClientState) ZeroCustomFields() exported.ClientState { - return &cs -} - -// Initialize ensures that initial consensus state for localhost is nil -func (cs ClientState) Initialize(_ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, consState exported.ConsensusState) error { - if consState != nil { - return sdkerrors.Wrap(clienttypes.ErrInvalidConsensus, "initial consensus state for localhost must be nil.") - } - return nil -} - -// ExportMetadata is a no-op for localhost client -func (cs ClientState) ExportMetadata(_ sdk.KVStore) []exported.GenesisMetadata { - return nil -} - -// CheckHeaderAndUpdateState updates the localhost client. It only needs access to the context -func (cs *ClientState) CheckHeaderAndUpdateState( - ctx sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, _ exported.Header, -) (exported.ClientState, exported.ConsensusState, error) { - // use the chain ID from context since the localhost client is from the running chain (i.e self). - cs.ChainId = ctx.ChainID() - revision := clienttypes.ParseChainID(cs.ChainId) - cs.Height = clienttypes.NewHeight(revision, uint64(ctx.BlockHeight())) - return cs, nil, nil -} - -// CheckMisbehaviourAndUpdateState implements ClientState -// Since localhost is the client of the running chain, misbehaviour cannot be submitted to it -// Thus, CheckMisbehaviourAndUpdateState returns an error for localhost -func (cs ClientState) CheckMisbehaviourAndUpdateState( - _ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, _ exported.Misbehaviour, -) (exported.ClientState, error) { - return nil, sdkerrors.Wrap(clienttypes.ErrInvalidMisbehaviour, "cannot submit misbehaviour to localhost client") -} - -// CheckSubstituteAndUpdateState returns an error. The localhost cannot be modified by -// proposals. -func (cs ClientState) CheckSubstituteAndUpdateState( - ctx sdk.Context, _ codec.BinaryCodec, _, _ sdk.KVStore, - _ exported.ClientState, -) (exported.ClientState, error) { - return nil, sdkerrors.Wrap(clienttypes.ErrUpdateClientFailed, "cannot update localhost client with a proposal") -} - -// VerifyUpgradeAndUpdateState returns an error since localhost cannot be upgraded -func (cs ClientState) VerifyUpgradeAndUpdateState( - _ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, - _ exported.ClientState, _ exported.ConsensusState, _, _ []byte, -) (exported.ClientState, exported.ConsensusState, error) { - return nil, nil, sdkerrors.Wrap(clienttypes.ErrInvalidUpgradeClient, "cannot upgrade localhost client") -} - -// VerifyClientState verifies that the localhost client state is stored locally -func (cs ClientState) VerifyClientState( - store sdk.KVStore, cdc codec.BinaryCodec, - _ exported.Height, _ exported.Prefix, _ string, _ []byte, clientState exported.ClientState, -) error { - path := host.KeyClientState - bz := store.Get([]byte(path)) - if bz == nil { - return sdkerrors.Wrapf(clienttypes.ErrFailedClientStateVerification, - "not found for path: %s", path) - } - - selfClient := clienttypes.MustUnmarshalClientState(cdc, bz) - - if !reflect.DeepEqual(selfClient, clientState) { - return sdkerrors.Wrapf(clienttypes.ErrFailedClientStateVerification, - "stored clientState != provided clientState: \n%v\n≠\n%v", - selfClient, clientState, - ) - } - return nil -} - -// VerifyClientConsensusState returns nil since a local host client does not store consensus -// states. -func (cs ClientState) VerifyClientConsensusState( - sdk.KVStore, codec.BinaryCodec, - exported.Height, string, exported.Height, exported.Prefix, - []byte, exported.ConsensusState, -) error { - return nil -} - -// VerifyConnectionState verifies a proof of the connection state of the -// specified connection end stored locally. -func (cs ClientState) VerifyConnectionState( - store sdk.KVStore, - cdc codec.BinaryCodec, - _ exported.Height, - _ exported.Prefix, - _ []byte, - connectionID string, - connectionEnd exported.ConnectionI, -) error { - path := host.ConnectionKey(connectionID) - bz := store.Get(path) - if bz == nil { - return sdkerrors.Wrapf(clienttypes.ErrFailedConnectionStateVerification, "not found for path %s", path) - } - - var prevConnection connectiontypes.ConnectionEnd - err := cdc.Unmarshal(bz, &prevConnection) - if err != nil { - return err - } - - if !reflect.DeepEqual(&prevConnection, connectionEnd) { - return sdkerrors.Wrapf( - clienttypes.ErrFailedConnectionStateVerification, - "connection end ≠ previous stored connection: \n%v\n≠\n%v", connectionEnd, prevConnection, - ) - } - - return nil -} - -// VerifyChannelState verifies a proof of the channel state of the specified -// channel end, under the specified port, stored on the local machine. -func (cs ClientState) VerifyChannelState( - store sdk.KVStore, - cdc codec.BinaryCodec, - _ exported.Height, - prefix exported.Prefix, - _ []byte, - portID, - channelID string, - channel exported.ChannelI, -) error { - path := host.ChannelKey(portID, channelID) - bz := store.Get(path) - if bz == nil { - return sdkerrors.Wrapf(clienttypes.ErrFailedChannelStateVerification, "not found for path %s", path) - } - - var prevChannel channeltypes.Channel - err := cdc.Unmarshal(bz, &prevChannel) - if err != nil { - return err - } - - if !reflect.DeepEqual(&prevChannel, channel) { - return sdkerrors.Wrapf( - clienttypes.ErrFailedChannelStateVerification, - "channel end ≠ previous stored channel: \n%v\n≠\n%v", channel, prevChannel, - ) - } - - return nil -} - -// VerifyPacketCommitment verifies a proof of an outgoing packet commitment at -// the specified port, specified channel, and specified sequence. -func (cs ClientState) VerifyPacketCommitment( - ctx sdk.Context, - store sdk.KVStore, - _ codec.BinaryCodec, - _ exported.Height, - _ uint64, - _ uint64, - _ exported.Prefix, - _ []byte, - portID, - channelID string, - sequence uint64, - commitmentBytes []byte, -) error { - path := host.PacketCommitmentKey(portID, channelID, sequence) - - data := store.Get(path) - if len(data) == 0 { - return sdkerrors.Wrapf(clienttypes.ErrFailedPacketCommitmentVerification, "not found for path %s", path) - } - - if !bytes.Equal(data, commitmentBytes) { - return sdkerrors.Wrapf( - clienttypes.ErrFailedPacketCommitmentVerification, - "commitment ≠ previous commitment: \n%X\n≠\n%X", commitmentBytes, data, - ) - } - - return nil -} - -// VerifyPacketAcknowledgement verifies a proof of an incoming packet -// acknowledgement at the specified port, specified channel, and specified sequence. -func (cs ClientState) VerifyPacketAcknowledgement( - ctx sdk.Context, - store sdk.KVStore, - _ codec.BinaryCodec, - _ exported.Height, - _ uint64, - _ uint64, - _ exported.Prefix, - _ []byte, - portID, - channelID string, - sequence uint64, - acknowledgement []byte, -) error { - path := host.PacketAcknowledgementKey(portID, channelID, sequence) - - data := store.Get(path) - if len(data) == 0 { - return sdkerrors.Wrapf(clienttypes.ErrFailedPacketAckVerification, "not found for path %s", path) - } - - if !bytes.Equal(data, acknowledgement) { - return sdkerrors.Wrapf( - clienttypes.ErrFailedPacketAckVerification, - "ak bytes ≠ previous ack: \n%X\n≠\n%X", acknowledgement, data, - ) - } - - return nil -} - -// VerifyPacketReceiptAbsence verifies a proof of the absence of an -// incoming packet receipt at the specified port, specified channel, and -// specified sequence. -func (cs ClientState) VerifyPacketReceiptAbsence( - ctx sdk.Context, - store sdk.KVStore, - _ codec.BinaryCodec, - _ exported.Height, - _ uint64, - _ uint64, - _ exported.Prefix, - _ []byte, - portID, - channelID string, - sequence uint64, -) error { - path := host.PacketReceiptKey(portID, channelID, sequence) - - data := store.Get(path) - if data != nil { - return sdkerrors.Wrap(clienttypes.ErrFailedPacketReceiptVerification, "expected no packet receipt") - } - - return nil -} - -// VerifyNextSequenceRecv verifies a proof of the next sequence number to be -// received of the specified channel at the specified port. -func (cs ClientState) VerifyNextSequenceRecv( - ctx sdk.Context, - store sdk.KVStore, - _ codec.BinaryCodec, - _ exported.Height, - _ uint64, - _ uint64, - _ exported.Prefix, - _ []byte, - portID, - channelID string, - nextSequenceRecv uint64, -) error { - path := host.NextSequenceRecvKey(portID, channelID) - - data := store.Get(path) - if len(data) == 0 { - return sdkerrors.Wrapf(clienttypes.ErrFailedNextSeqRecvVerification, "not found for path %s", path) - } - - prevSequenceRecv := binary.BigEndian.Uint64(data) - if prevSequenceRecv != nextSequenceRecv { - return sdkerrors.Wrapf( - clienttypes.ErrFailedNextSeqRecvVerification, - "next sequence receive ≠ previous stored sequence (%d ≠ %d)", nextSequenceRecv, prevSequenceRecv, - ) - } - - return nil -} diff --git a/modules/light-clients/09-localhost/types/client_state_test.go b/modules/light-clients/09-localhost/types/client_state_test.go deleted file mode 100644 index 75d827f3ae8..00000000000 --- a/modules/light-clients/09-localhost/types/client_state_test.go +++ /dev/null @@ -1,526 +0,0 @@ -package types_test - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" - - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - connectiontypes "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types" - channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" - commitmenttypes "github.com/cosmos/ibc-go/v4/modules/core/23-commitment/types" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" - "github.com/cosmos/ibc-go/v4/modules/core/exported" - ibctmtypes "github.com/cosmos/ibc-go/v4/modules/light-clients/07-tendermint/types" - "github.com/cosmos/ibc-go/v4/modules/light-clients/09-localhost/types" -) - -const ( - testConnectionID = "connectionid" - testPortID = "testportid" - testChannelID = "testchannelid" - testSequence = 1 -) - -func (suite *LocalhostTestSuite) TestStatus() { - clientState := types.NewClientState("chainID", clienttypes.NewHeight(3, 10)) - - // localhost should always return active - status := clientState.Status(suite.ctx, nil, nil) - suite.Require().Equal(exported.Active, status) -} - -func (suite *LocalhostTestSuite) TestValidate() { - testCases := []struct { - name string - clientState *types.ClientState - expPass bool - }{ - { - name: "valid client", - clientState: types.NewClientState("chainID", clienttypes.NewHeight(3, 10)), - expPass: true, - }, - { - name: "invalid chain id", - clientState: types.NewClientState(" ", clienttypes.NewHeight(3, 10)), - expPass: false, - }, - { - name: "invalid height", - clientState: types.NewClientState("chainID", clienttypes.ZeroHeight()), - expPass: false, - }, - } - - for _, tc := range testCases { - err := tc.clientState.Validate() - if tc.expPass { - suite.Require().NoError(err, tc.name) - } else { - suite.Require().Error(err, tc.name) - } - } -} - -func (suite *LocalhostTestSuite) TestInitialize() { - testCases := []struct { - name string - consState exported.ConsensusState - expPass bool - }{ - { - "valid initialization", - nil, - true, - }, - { - "invalid consenus state", - &ibctmtypes.ConsensusState{}, - false, - }, - } - - clientState := types.NewClientState("chainID", clienttypes.NewHeight(3, 10)) - - for _, tc := range testCases { - err := clientState.Initialize(suite.ctx, suite.cdc, suite.store, tc.consState) - - if tc.expPass { - suite.Require().NoError(err, "valid testcase: %s failed", tc.name) - } else { - suite.Require().Error(err, "invalid testcase: %s passed", tc.name) - } - } -} - -func (suite *LocalhostTestSuite) TestVerifyClientState() { - clientState := types.NewClientState("chainID", clientHeight) - invalidClient := types.NewClientState("chainID", clienttypes.NewHeight(0, 12)) - - testCases := []struct { - name string - clientState *types.ClientState - malleate func() - counterparty *types.ClientState - expPass bool - }{ - { - name: "proof verification success", - clientState: clientState, - malleate: func() { - bz := clienttypes.MustMarshalClientState(suite.cdc, clientState) - suite.store.Set(host.ClientStateKey(), bz) - }, - counterparty: clientState, - expPass: true, - }, - { - name: "proof verification failed: invalid client", - clientState: clientState, - malleate: func() { - bz := clienttypes.MustMarshalClientState(suite.cdc, clientState) - suite.store.Set(host.ClientStateKey(), bz) - }, - counterparty: invalidClient, - expPass: false, - }, - { - name: "proof verification failed: client not stored", - clientState: clientState, - malleate: func() {}, - counterparty: clientState, - expPass: false, - }, - } - - for _, tc := range testCases { - tc := tc - - suite.Run(tc.name, func() { - suite.SetupTest() - tc.malleate() - - err := tc.clientState.VerifyClientState( - suite.store, suite.cdc, clienttypes.NewHeight(0, 10), nil, "", []byte{}, tc.counterparty, - ) - - if tc.expPass { - suite.Require().NoError(err) - } else { - suite.Require().Error(err) - } - }) - } -} - -func (suite *LocalhostTestSuite) TestVerifyClientConsensusState() { - clientState := types.NewClientState("chainID", clientHeight) - err := clientState.VerifyClientConsensusState( - nil, nil, nil, "", nil, nil, nil, nil, - ) - suite.Require().NoError(err) -} - -func (suite *LocalhostTestSuite) TestCheckHeaderAndUpdateState() { - clientState := types.NewClientState("chainID", clientHeight) - cs, _, err := clientState.CheckHeaderAndUpdateState(suite.ctx, nil, nil, nil) - suite.Require().NoError(err) - suite.Require().Equal(uint64(0), cs.GetLatestHeight().GetRevisionNumber()) - suite.Require().Equal(suite.ctx.BlockHeight(), int64(cs.GetLatestHeight().GetRevisionHeight())) - suite.Require().Equal(suite.ctx.BlockHeader().ChainID, clientState.ChainId) -} - -func (suite *LocalhostTestSuite) TestMisbehaviourAndUpdateState() { - clientState := types.NewClientState("chainID", clientHeight) - cs, err := clientState.CheckMisbehaviourAndUpdateState(suite.ctx, nil, nil, nil) - suite.Require().Error(err) - suite.Require().Nil(cs) -} - -func (suite *LocalhostTestSuite) TestProposedHeaderAndUpdateState() { - clientState := types.NewClientState("chainID", clientHeight) - cs, err := clientState.CheckSubstituteAndUpdateState(suite.ctx, nil, nil, nil, nil) - suite.Require().Error(err) - suite.Require().Nil(cs) -} - -func (suite *LocalhostTestSuite) TestVerifyConnectionState() { - counterparty := connectiontypes.NewCounterparty("clientB", testConnectionID, commitmenttypes.NewMerklePrefix([]byte("ibc"))) - conn1 := connectiontypes.NewConnectionEnd(connectiontypes.OPEN, "clientA", counterparty, []*connectiontypes.Version{connectiontypes.NewVersion("1", nil)}, 0) - conn2 := connectiontypes.NewConnectionEnd(connectiontypes.OPEN, "clientA", counterparty, []*connectiontypes.Version{connectiontypes.NewVersion("2", nil)}, 0) - - testCases := []struct { - name string - clientState *types.ClientState - malleate func() - connection connectiontypes.ConnectionEnd - expPass bool - }{ - { - name: "proof verification success", - clientState: types.NewClientState("chainID", clientHeight), - malleate: func() { - bz, err := suite.cdc.Marshal(&conn1) - suite.Require().NoError(err) - suite.store.Set(host.ConnectionKey(testConnectionID), bz) - }, - connection: conn1, - expPass: true, - }, - { - name: "proof verification failed: connection not stored", - clientState: types.NewClientState("chainID", clientHeight), - malleate: func() {}, - connection: conn1, - expPass: false, - }, - { - name: "proof verification failed: unmarshal error", - clientState: types.NewClientState("chainID", clientHeight), - malleate: func() { - suite.store.Set(host.ConnectionKey(testConnectionID), []byte("connection")) - }, - connection: conn1, - expPass: false, - }, - { - name: "proof verification failed: different connection stored", - clientState: types.NewClientState("chainID", clientHeight), - malleate: func() { - bz, err := suite.cdc.Marshal(&conn2) - suite.Require().NoError(err) - suite.store.Set(host.ConnectionKey(testConnectionID), bz) - }, - connection: conn1, - expPass: false, - }, - } - - for _, tc := range testCases { - tc := tc - - suite.Run(tc.name, func() { - suite.SetupTest() - tc.malleate() - - err := tc.clientState.VerifyConnectionState( - suite.store, suite.cdc, clientHeight, nil, []byte{}, testConnectionID, &tc.connection, - ) - - if tc.expPass { - suite.Require().NoError(err) - } else { - suite.Require().Error(err) - } - }) - } -} - -func (suite *LocalhostTestSuite) TestVerifyChannelState() { - counterparty := channeltypes.NewCounterparty(testPortID, testChannelID) - ch1 := channeltypes.NewChannel(channeltypes.OPEN, channeltypes.ORDERED, counterparty, []string{testConnectionID}, "1.0.0") - ch2 := channeltypes.NewChannel(channeltypes.OPEN, channeltypes.ORDERED, counterparty, []string{testConnectionID}, "2.0.0") - - testCases := []struct { - name string - clientState *types.ClientState - malleate func() - channel channeltypes.Channel - expPass bool - }{ - { - name: "proof verification success", - clientState: types.NewClientState("chainID", clientHeight), - malleate: func() { - bz, err := suite.cdc.Marshal(&ch1) - suite.Require().NoError(err) - suite.store.Set(host.ChannelKey(testPortID, testChannelID), bz) - }, - channel: ch1, - expPass: true, - }, - { - name: "proof verification failed: channel not stored", - clientState: types.NewClientState("chainID", clientHeight), - malleate: func() {}, - channel: ch1, - expPass: false, - }, - { - name: "proof verification failed: unmarshal failed", - clientState: types.NewClientState("chainID", clientHeight), - malleate: func() { - suite.store.Set(host.ChannelKey(testPortID, testChannelID), []byte("channel")) - }, - channel: ch1, - expPass: false, - }, - { - name: "proof verification failed: different channel stored", - clientState: types.NewClientState("chainID", clientHeight), - malleate: func() { - bz, err := suite.cdc.Marshal(&ch2) - suite.Require().NoError(err) - suite.store.Set(host.ChannelKey(testPortID, testChannelID), bz) - }, - channel: ch1, - expPass: false, - }, - } - - for _, tc := range testCases { - tc := tc - - suite.Run(tc.name, func() { - suite.SetupTest() - tc.malleate() - - err := tc.clientState.VerifyChannelState( - suite.store, suite.cdc, clientHeight, nil, []byte{}, testPortID, testChannelID, &tc.channel, - ) - - if tc.expPass { - suite.Require().NoError(err) - } else { - suite.Require().Error(err) - } - }) - } -} - -func (suite *LocalhostTestSuite) TestVerifyPacketCommitment() { - testCases := []struct { - name string - clientState *types.ClientState - malleate func() - commitment []byte - expPass bool - }{ - { - name: "proof verification success", - clientState: types.NewClientState("chainID", clientHeight), - malleate: func() { - suite.store.Set( - host.PacketCommitmentKey(testPortID, testChannelID, testSequence), []byte("commitment"), - ) - }, - commitment: []byte("commitment"), - expPass: true, - }, - { - name: "proof verification failed: different commitment stored", - clientState: types.NewClientState("chainID", clientHeight), - malleate: func() { - suite.store.Set( - host.PacketCommitmentKey(testPortID, testChannelID, testSequence), []byte("different"), - ) - }, - commitment: []byte("commitment"), - expPass: false, - }, - { - name: "proof verification failed: no commitment stored", - clientState: types.NewClientState("chainID", clientHeight), - malleate: func() {}, - commitment: []byte{}, - expPass: false, - }, - } - - for _, tc := range testCases { - tc := tc - - suite.Run(tc.name, func() { - suite.SetupTest() - tc.malleate() - - err := tc.clientState.VerifyPacketCommitment( - suite.ctx, suite.store, suite.cdc, clientHeight, 0, 0, nil, []byte{}, testPortID, testChannelID, testSequence, tc.commitment, - ) - - if tc.expPass { - suite.Require().NoError(err) - } else { - suite.Require().Error(err) - } - }) - } -} - -func (suite *LocalhostTestSuite) TestVerifyPacketAcknowledgement() { - testCases := []struct { - name string - clientState *types.ClientState - malleate func() - ack []byte - expPass bool - }{ - { - name: "proof verification success", - clientState: types.NewClientState("chainID", clientHeight), - malleate: func() { - suite.store.Set( - host.PacketAcknowledgementKey(testPortID, testChannelID, testSequence), []byte("acknowledgement"), - ) - }, - ack: []byte("acknowledgement"), - expPass: true, - }, - { - name: "proof verification failed: different ack stored", - clientState: types.NewClientState("chainID", clientHeight), - malleate: func() { - suite.store.Set( - host.PacketAcknowledgementKey(testPortID, testChannelID, testSequence), []byte("different"), - ) - }, - ack: []byte("acknowledgement"), - expPass: false, - }, - { - name: "proof verification failed: no commitment stored", - clientState: types.NewClientState("chainID", clientHeight), - malleate: func() {}, - ack: []byte{}, - expPass: false, - }, - } - - for _, tc := range testCases { - tc := tc - - suite.Run(tc.name, func() { - suite.SetupTest() - tc.malleate() - - err := tc.clientState.VerifyPacketAcknowledgement( - suite.ctx, suite.store, suite.cdc, clientHeight, 0, 0, nil, []byte{}, testPortID, testChannelID, testSequence, tc.ack, - ) - - if tc.expPass { - suite.Require().NoError(err) - } else { - suite.Require().Error(err) - } - }) - } -} - -func (suite *LocalhostTestSuite) TestVerifyPacketReceiptAbsence() { - clientState := types.NewClientState("chainID", clientHeight) - - err := clientState.VerifyPacketReceiptAbsence( - suite.ctx, suite.store, suite.cdc, clientHeight, 0, 0, nil, nil, testPortID, testChannelID, testSequence, - ) - - suite.Require().NoError(err, "receipt absence failed") - - suite.store.Set(host.PacketReceiptKey(testPortID, testChannelID, testSequence), []byte("receipt")) - - err = clientState.VerifyPacketReceiptAbsence( - suite.ctx, suite.store, suite.cdc, clientHeight, 0, 0, nil, nil, testPortID, testChannelID, testSequence, - ) - suite.Require().Error(err, "receipt exists in store") -} - -func (suite *LocalhostTestSuite) TestVerifyNextSeqRecv() { - nextSeqRecv := uint64(5) - - testCases := []struct { - name string - clientState *types.ClientState - malleate func() - nextSeqRecv uint64 - expPass bool - }{ - { - name: "proof verification success", - clientState: types.NewClientState("chainID", clientHeight), - malleate: func() { - suite.store.Set( - host.NextSequenceRecvKey(testPortID, testChannelID), - sdk.Uint64ToBigEndian(nextSeqRecv), - ) - }, - nextSeqRecv: nextSeqRecv, - expPass: true, - }, - { - name: "proof verification failed: different nextSeqRecv stored", - clientState: types.NewClientState("chainID", clientHeight), - malleate: func() { - suite.store.Set( - host.NextSequenceRecvKey(testPortID, testChannelID), - sdk.Uint64ToBigEndian(3), - ) - }, - nextSeqRecv: nextSeqRecv, - expPass: false, - }, - { - name: "proof verification failed: no nextSeqRecv stored", - clientState: types.NewClientState("chainID", clientHeight), - malleate: func() {}, - nextSeqRecv: nextSeqRecv, - expPass: false, - }, - } - - for _, tc := range testCases { - tc := tc - - suite.Run(tc.name, func() { - suite.SetupTest() - tc.malleate() - - err := tc.clientState.VerifyNextSequenceRecv( - suite.ctx, suite.store, suite.cdc, clientHeight, 0, 0, nil, []byte{}, testPortID, testChannelID, nextSeqRecv, - ) - - if tc.expPass { - suite.Require().NoError(err) - } else { - suite.Require().Error(err) - } - }) - } -} diff --git a/modules/light-clients/09-localhost/types/codec.go b/modules/light-clients/09-localhost/types/codec.go deleted file mode 100644 index 96e414a95c4..00000000000 --- a/modules/light-clients/09-localhost/types/codec.go +++ /dev/null @@ -1,16 +0,0 @@ -package types - -import ( - codectypes "github.com/cosmos/cosmos-sdk/codec/types" - - "github.com/cosmos/ibc-go/v4/modules/core/exported" -) - -// RegisterInterfaces register the ibc interfaces submodule implementations to protobuf -// Any. -func RegisterInterfaces(registry codectypes.InterfaceRegistry) { - registry.RegisterImplementations( - (*exported.ClientState)(nil), - &ClientState{}, - ) -} diff --git a/modules/light-clients/09-localhost/types/errors.go b/modules/light-clients/09-localhost/types/errors.go deleted file mode 100644 index 57ad7c1f6a6..00000000000 --- a/modules/light-clients/09-localhost/types/errors.go +++ /dev/null @@ -1,10 +0,0 @@ -package types - -import ( - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" -) - -// Localhost sentinel errors -var ( - ErrConsensusStatesNotStored = sdkerrors.Register(SubModuleName, 2, "localhost does not store consensus states") -) diff --git a/modules/light-clients/09-localhost/types/keys.go b/modules/light-clients/09-localhost/types/keys.go deleted file mode 100644 index 2fe7c7e48f5..00000000000 --- a/modules/light-clients/09-localhost/types/keys.go +++ /dev/null @@ -1,6 +0,0 @@ -package types - -const ( - // SubModuleName for the localhost (loopback) client - SubModuleName = "localhost" -) diff --git a/modules/light-clients/09-localhost/types/localhost.pb.go b/modules/light-clients/09-localhost/types/localhost.pb.go deleted file mode 100644 index 15ef33136e9..00000000000 --- a/modules/light-clients/09-localhost/types/localhost.pb.go +++ /dev/null @@ -1,369 +0,0 @@ -// Code generated by protoc-gen-gogo. DO NOT EDIT. -// source: ibc/lightclients/localhost/v1/localhost.proto - -package types - -import ( - fmt "fmt" - types "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - _ "github.com/gogo/protobuf/gogoproto" - proto "github.com/gogo/protobuf/proto" - io "io" - math "math" - math_bits "math/bits" -) - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package - -// ClientState defines a loopback (localhost) client. It requires (read-only) -// access to keys outside the client prefix. -type ClientState struct { - // self chain ID - ChainId string `protobuf:"bytes,1,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty" yaml:"chain_id"` - // self latest block height - Height types.Height `protobuf:"bytes,2,opt,name=height,proto3" json:"height"` -} - -func (m *ClientState) Reset() { *m = ClientState{} } -func (m *ClientState) String() string { return proto.CompactTextString(m) } -func (*ClientState) ProtoMessage() {} -func (*ClientState) Descriptor() ([]byte, []int) { - return fileDescriptor_acd9f5b22d41bf6d, []int{0} -} -func (m *ClientState) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *ClientState) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_ClientState.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalToSizedBuffer(b) - if err != nil { - return nil, err - } - return b[:n], nil - } -} -func (m *ClientState) XXX_Merge(src proto.Message) { - xxx_messageInfo_ClientState.Merge(m, src) -} -func (m *ClientState) XXX_Size() int { - return m.Size() -} -func (m *ClientState) XXX_DiscardUnknown() { - xxx_messageInfo_ClientState.DiscardUnknown(m) -} - -var xxx_messageInfo_ClientState proto.InternalMessageInfo - -func init() { - proto.RegisterType((*ClientState)(nil), "ibc.lightclients.localhost.v1.ClientState") -} - -func init() { - proto.RegisterFile("ibc/lightclients/localhost/v1/localhost.proto", fileDescriptor_acd9f5b22d41bf6d) -} - -var fileDescriptor_acd9f5b22d41bf6d = []byte{ - // 288 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xd2, 0xcd, 0x4c, 0x4a, 0xd6, - 0xcf, 0xc9, 0x4c, 0xcf, 0x28, 0x49, 0xce, 0xc9, 0x4c, 0xcd, 0x2b, 0x29, 0xd6, 0xcf, 0xc9, 0x4f, - 0x4e, 0xcc, 0xc9, 0xc8, 0x2f, 0x2e, 0xd1, 0x2f, 0x33, 0x44, 0x70, 0xf4, 0x0a, 0x8a, 0xf2, 0x4b, - 0xf2, 0x85, 0x64, 0x33, 0x93, 0x92, 0xf5, 0x90, 0x95, 0xeb, 0x21, 0x54, 0x94, 0x19, 0x4a, 0x89, - 0xa4, 0xe7, 0xa7, 0xe7, 0x83, 0x55, 0xea, 0x83, 0x58, 0x10, 0x4d, 0x52, 0xf2, 0x20, 0x3b, 0x92, - 0xf3, 0x8b, 0x52, 0xf5, 0x21, 0x9a, 0x40, 0x06, 0x43, 0x58, 0x10, 0x05, 0x4a, 0xb5, 0x5c, 0xdc, - 0xce, 0x60, 0x7e, 0x70, 0x49, 0x62, 0x49, 0xaa, 0x90, 0x1e, 0x17, 0x47, 0x72, 0x46, 0x62, 0x66, - 0x5e, 0x7c, 0x66, 0x8a, 0x04, 0xa3, 0x02, 0xa3, 0x06, 0xa7, 0x93, 0xf0, 0xa7, 0x7b, 0xf2, 0xfc, - 0x95, 0x89, 0xb9, 0x39, 0x56, 0x4a, 0x30, 0x19, 0xa5, 0x20, 0x76, 0x30, 0xd3, 0x33, 0x45, 0xc8, - 0x82, 0x8b, 0x2d, 0x23, 0x15, 0xe4, 0x26, 0x09, 0x26, 0x05, 0x46, 0x0d, 0x6e, 0x23, 0x29, 0x3d, - 0x90, 0x2b, 0x41, 0x16, 0xea, 0x41, 0xad, 0x29, 0x33, 0xd4, 0xf3, 0x00, 0xab, 0x70, 0x62, 0x39, - 0x71, 0x4f, 0x9e, 0x21, 0x08, 0xaa, 0xde, 0x8a, 0xa5, 0x63, 0x81, 0x3c, 0x83, 0x53, 0xdc, 0x89, - 0x47, 0x72, 0x8c, 0x17, 0x1e, 0xc9, 0x31, 0x3e, 0x78, 0x24, 0xc7, 0x38, 0xe1, 0xb1, 0x1c, 0xc3, - 0x85, 0xc7, 0x72, 0x0c, 0x37, 0x1e, 0xcb, 0x31, 0x44, 0xb9, 0xa4, 0x67, 0x96, 0x64, 0x94, 0x26, - 0xe9, 0x25, 0xe7, 0xe7, 0xea, 0x27, 0xe7, 0x17, 0xe7, 0xe6, 0x17, 0xeb, 0x67, 0x26, 0x25, 0xeb, - 0xa6, 0xe7, 0xeb, 0x97, 0x99, 0xe8, 0xe7, 0xe6, 0xa7, 0x94, 0xe6, 0xa4, 0x16, 0x43, 0x42, 0x4f, - 0x17, 0x16, 0x7c, 0x06, 0x96, 0xba, 0x88, 0x10, 0x2c, 0xa9, 0x2c, 0x48, 0x2d, 0x4e, 0x62, 0x03, - 0xfb, 0xd2, 0x18, 0x10, 0x00, 0x00, 0xff, 0xff, 0x84, 0xfe, 0x1f, 0x95, 0x6c, 0x01, 0x00, 0x00, -} - -func (m *ClientState) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *ClientState) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *ClientState) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - { - size, err := m.Height.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintLocalhost(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x12 - if len(m.ChainId) > 0 { - i -= len(m.ChainId) - copy(dAtA[i:], m.ChainId) - i = encodeVarintLocalhost(dAtA, i, uint64(len(m.ChainId))) - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - -func encodeVarintLocalhost(dAtA []byte, offset int, v uint64) int { - offset -= sovLocalhost(v) - base := offset - for v >= 1<<7 { - dAtA[offset] = uint8(v&0x7f | 0x80) - v >>= 7 - offset++ - } - dAtA[offset] = uint8(v) - return base -} -func (m *ClientState) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.ChainId) - if l > 0 { - n += 1 + l + sovLocalhost(uint64(l)) - } - l = m.Height.Size() - n += 1 + l + sovLocalhost(uint64(l)) - return n -} - -func sovLocalhost(x uint64) (n int) { - return (math_bits.Len64(x|1) + 6) / 7 -} -func sozLocalhost(x uint64) (n int) { - return sovLocalhost(uint64((x << 1) ^ uint64((int64(x) >> 63)))) -} -func (m *ClientState) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowLocalhost - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: ClientState: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: ClientState: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ChainId", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowLocalhost - } - 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 ErrInvalidLengthLocalhost - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthLocalhost - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.ChainId = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Height", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowLocalhost - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthLocalhost - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthLocalhost - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if err := m.Height.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipLocalhost(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthLocalhost - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func skipLocalhost(dAtA []byte) (n int, err error) { - l := len(dAtA) - iNdEx := 0 - depth := 0 - for iNdEx < l { - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowLocalhost - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - wireType := int(wire & 0x7) - switch wireType { - case 0: - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowLocalhost - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - iNdEx++ - if dAtA[iNdEx-1] < 0x80 { - break - } - } - case 1: - iNdEx += 8 - case 2: - var length int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowLocalhost - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - length |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - if length < 0 { - return 0, ErrInvalidLengthLocalhost - } - iNdEx += length - case 3: - depth++ - case 4: - if depth == 0 { - return 0, ErrUnexpectedEndOfGroupLocalhost - } - depth-- - case 5: - iNdEx += 4 - default: - return 0, fmt.Errorf("proto: illegal wireType %d", wireType) - } - if iNdEx < 0 { - return 0, ErrInvalidLengthLocalhost - } - if depth == 0 { - return iNdEx, nil - } - } - return 0, io.ErrUnexpectedEOF -} - -var ( - ErrInvalidLengthLocalhost = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowLocalhost = fmt.Errorf("proto: integer overflow") - ErrUnexpectedEndOfGroupLocalhost = fmt.Errorf("proto: unexpected end of group") -) diff --git a/modules/light-clients/09-localhost/types/localhost_test.go b/modules/light-clients/09-localhost/types/localhost_test.go deleted file mode 100644 index 56bf9379fbe..00000000000 --- a/modules/light-clients/09-localhost/types/localhost_test.go +++ /dev/null @@ -1,41 +0,0 @@ -package types_test - -import ( - "testing" - - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/stretchr/testify/suite" - tmproto "github.com/tendermint/tendermint/proto/tendermint/types" - - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v4/modules/core/exported" - "github.com/cosmos/ibc-go/v4/testing/simapp" -) - -const ( - height = 4 -) - -var clientHeight = clienttypes.NewHeight(0, 10) - -type LocalhostTestSuite struct { - suite.Suite - - cdc codec.Codec - ctx sdk.Context - store sdk.KVStore -} - -func (suite *LocalhostTestSuite) SetupTest() { - isCheckTx := false - app := simapp.Setup(isCheckTx) - - suite.cdc = app.AppCodec() - suite.ctx = app.BaseApp.NewContext(isCheckTx, tmproto.Header{Height: 1, ChainID: "ibc-chain"}) - suite.store = app.IBCKeeper.ClientKeeper.ClientStore(suite.ctx, exported.Localhost) -} - -func TestLocalhostTestSuite(t *testing.T) { - suite.Run(t, new(LocalhostTestSuite)) -} diff --git a/proto/buf.gen.gogo.yaml b/proto/buf.gen.gogo.yaml new file mode 100644 index 00000000000..9c8ba0a4b1f --- /dev/null +++ b/proto/buf.gen.gogo.yaml @@ -0,0 +1,8 @@ +version: v1 +plugins: + - name: gocosmos + out: .. + opt: plugins=grpc,Mgoogle/protobuf/any.proto=github.com/cosmos/cosmos-sdk/codec/types + - name: grpc-gateway + out: .. + opt: logtostderr=true,allow_colon_final_segments=true diff --git a/proto/buf.gen.swagger.yaml b/proto/buf.gen.swagger.yaml new file mode 100644 index 00000000000..d0f7535b1a4 --- /dev/null +++ b/proto/buf.gen.swagger.yaml @@ -0,0 +1,5 @@ +version: v1 +plugins: + - name: swagger + out: ../tmp-swagger-gen + opt: logtostderr=true,fqn_for_swagger_name=true,simple_operation_ids=true diff --git a/proto/buf.lock b/proto/buf.lock new file mode 100644 index 00000000000..f36df5f2658 --- /dev/null +++ b/proto/buf.lock @@ -0,0 +1,23 @@ +# Generated by buf. DO NOT EDIT. +version: v1 +deps: + - remote: buf.build + owner: cosmos + repository: cosmos-proto + commit: 1935555c206d4afb9e94615dfd0fad31 + - remote: buf.build + owner: cosmos + repository: cosmos-sdk + commit: 8c515ebc07ee4514aabcaf3a584a1d1a + - remote: buf.build + owner: cosmos + repository: gogo-proto + commit: 34d970b699f84aa382f3c29773a60836 + - remote: buf.build + owner: cosmos + repository: ics23 + commit: 55085f7c710a45f58fa09947208eb70b + - remote: buf.build + owner: googleapis + repository: googleapis + commit: 8d7204855ec14631a499bd7393ce1970 diff --git a/proto/buf.yaml b/proto/buf.yaml index aae636f0a14..79a7bf0012f 100644 --- a/proto/buf.yaml +++ b/proto/buf.yaml @@ -1,9 +1,13 @@ -# Generated by "buf config migrate-v1beta1". Edit as necessary, and -# remove this comment when you're finished. -# -# This module represents the "proto" root found in -# the previous configuration. version: v1 +name: buf.build/cosmos/ibc +deps: + # TODO: update sdk buf dependency when v0.47.0 is tagged and pushed to BSR + # see: (https://github.com/cosmos/cosmos-sdk/tree/main/proto#sdk-x-buf) + - buf.build/cosmos/cosmos-sdk:8c515ebc07ee4514aabcaf3a584a1d1a + - buf.build/cosmos/cosmos-proto:1935555c206d4afb9e94615dfd0fad31 + - buf.build/cosmos/gogo-proto:a14993478f40695898ed8a86931094b6656e8a5d + - buf.build/googleapis/googleapis:8d7204855ec14631a499bd7393ce1970 + - buf.build/cosmos/ics23:b1abd8678aab07165efd453c96796a179eb3131f breaking: use: - FILE diff --git a/proto/ibc/applications/fee/v1/ack.proto b/proto/ibc/applications/fee/v1/ack.proto index b6c20acf74e..ec1342bbf23 100644 --- a/proto/ibc/applications/fee/v1/ack.proto +++ b/proto/ibc/applications/fee/v1/ack.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package ibc.applications.fee.v1; -option go_package = "github.com/cosmos/ibc-go/v4/modules/apps/29-fee/types"; +option go_package = "github.com/cosmos/ibc-go/v7/modules/apps/29-fee/types"; import "gogoproto/gogo.proto"; diff --git a/proto/ibc/applications/fee/v1/fee.proto b/proto/ibc/applications/fee/v1/fee.proto index d85ebcfeb31..1a025ddca1c 100644 --- a/proto/ibc/applications/fee/v1/fee.proto +++ b/proto/ibc/applications/fee/v1/fee.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package ibc.applications.fee.v1; -option go_package = "github.com/cosmos/ibc-go/v4/modules/apps/29-fee/types"; +option go_package = "github.com/cosmos/ibc-go/v7/modules/apps/29-fee/types"; import "cosmos/base/v1beta1/coin.proto"; import "gogoproto/gogo.proto"; diff --git a/proto/ibc/applications/fee/v1/genesis.proto b/proto/ibc/applications/fee/v1/genesis.proto index 9bce6ac599b..6bba6cff632 100644 --- a/proto/ibc/applications/fee/v1/genesis.proto +++ b/proto/ibc/applications/fee/v1/genesis.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package ibc.applications.fee.v1; -option go_package = "github.com/cosmos/ibc-go/v4/modules/apps/29-fee/types"; +option go_package = "github.com/cosmos/ibc-go/v7/modules/apps/29-fee/types"; import "gogoproto/gogo.proto"; import "ibc/applications/fee/v1/fee.proto"; diff --git a/proto/ibc/applications/fee/v1/metadata.proto b/proto/ibc/applications/fee/v1/metadata.proto index e1ec1b12bfd..0c0a72c9ca5 100644 --- a/proto/ibc/applications/fee/v1/metadata.proto +++ b/proto/ibc/applications/fee/v1/metadata.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package ibc.applications.fee.v1; -option go_package = "github.com/cosmos/ibc-go/v4/modules/apps/29-fee/types"; +option go_package = "github.com/cosmos/ibc-go/v7/modules/apps/29-fee/types"; import "gogoproto/gogo.proto"; diff --git a/proto/ibc/applications/fee/v1/query.proto b/proto/ibc/applications/fee/v1/query.proto index 41b8c6ba5b6..bcf6622b34b 100644 --- a/proto/ibc/applications/fee/v1/query.proto +++ b/proto/ibc/applications/fee/v1/query.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package ibc.applications.fee.v1; -option go_package = "github.com/cosmos/ibc-go/v4/modules/apps/29-fee/types"; +option go_package = "github.com/cosmos/ibc-go/v7/modules/apps/29-fee/types"; import "gogoproto/gogo.proto"; import "google/api/annotations.proto"; @@ -83,6 +83,8 @@ message QueryIncentivizedPacketsRequest { message QueryIncentivizedPacketsResponse { // list of identified fees for incentivized packets repeated ibc.applications.fee.v1.IdentifiedPacketFees incentivized_packets = 1 [(gogoproto.nullable) = false]; + // pagination defines the pagination in the response. + cosmos.base.query.v1beta1.PageResponse pagination = 2; } // QueryIncentivizedPacketRequest defines the request type for the IncentivizedPacket rpc @@ -114,6 +116,8 @@ message QueryIncentivizedPacketsForChannelRequest { message QueryIncentivizedPacketsForChannelResponse { // Map of all incentivized_packets repeated ibc.applications.fee.v1.IdentifiedPacketFees incentivized_packets = 1; + // pagination defines the pagination in the response. + cosmos.base.query.v1beta1.PageResponse pagination = 2; } // QueryTotalRecvFeesRequest defines the request type for the TotalRecvFees rpc @@ -205,6 +209,8 @@ message QueryFeeEnabledChannelsResponse { // list of fee enabled channels repeated ibc.applications.fee.v1.FeeEnabledChannel fee_enabled_channels = 1 [(gogoproto.moretags) = "yaml:\"fee_enabled_channels\"", (gogoproto.nullable) = false]; + // pagination defines the pagination in the response. + cosmos.base.query.v1beta1.PageResponse pagination = 2; } // QueryFeeEnabledChannelRequest defines the request type for the FeeEnabledChannel rpc diff --git a/proto/ibc/applications/fee/v1/tx.proto b/proto/ibc/applications/fee/v1/tx.proto index 8d6fca58fbb..3a46de74ebe 100644 --- a/proto/ibc/applications/fee/v1/tx.proto +++ b/proto/ibc/applications/fee/v1/tx.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package ibc.applications.fee.v1; -option go_package = "github.com/cosmos/ibc-go/v4/modules/apps/29-fee/types"; +option go_package = "github.com/cosmos/ibc-go/v7/modules/apps/29-fee/types"; import "gogoproto/gogo.proto"; import "ibc/applications/fee/v1/fee.proto"; diff --git a/proto/ibc/applications/interchain_accounts/controller/v1/controller.proto b/proto/ibc/applications/interchain_accounts/controller/v1/controller.proto index c7abd9f3372..a505b4637ef 100644 --- a/proto/ibc/applications/interchain_accounts/controller/v1/controller.proto +++ b/proto/ibc/applications/interchain_accounts/controller/v1/controller.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package ibc.applications.interchain_accounts.controller.v1; -option go_package = "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/controller/types"; +option go_package = "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/controller/types"; import "gogoproto/gogo.proto"; diff --git a/proto/ibc/applications/interchain_accounts/controller/v1/query.proto b/proto/ibc/applications/interchain_accounts/controller/v1/query.proto index 6ccc0456731..8c237a1525f 100644 --- a/proto/ibc/applications/interchain_accounts/controller/v1/query.proto +++ b/proto/ibc/applications/interchain_accounts/controller/v1/query.proto @@ -2,19 +2,37 @@ syntax = "proto3"; package ibc.applications.interchain_accounts.controller.v1; -option go_package = "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/controller/types"; +option go_package = "github.com/cosmos/ibc-go/v7/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/interchain_accounts/controller/v1/tx.proto b/proto/ibc/applications/interchain_accounts/controller/v1/tx.proto new file mode 100644 index 00000000000..284ffe70576 --- /dev/null +++ b/proto/ibc/applications/interchain_accounts/controller/v1/tx.proto @@ -0,0 +1,51 @@ +syntax = "proto3"; + +package ibc.applications.interchain_accounts.controller.v1; + +option go_package = "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/controller/types"; + +import "gogoproto/gogo.proto"; +import "ibc/applications/interchain_accounts/v1/packet.proto"; + +// Msg defines the 27-interchain-accounts/controller Msg service. +service Msg { + // RegisterInterchainAccount defines a rpc handler for MsgRegisterInterchainAccount. + rpc RegisterInterchainAccount(MsgRegisterInterchainAccount) returns (MsgRegisterInterchainAccountResponse); + // SendTx defines a rpc handler for MsgSendTx. + rpc SendTx(MsgSendTx) returns (MsgSendTxResponse); +} + +// MsgRegisterInterchainAccount defines the payload for Msg/RegisterAccount +message MsgRegisterInterchainAccount { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + string owner = 1; + string connection_id = 2 [(gogoproto.moretags) = "yaml:\"connection_id\""]; + string version = 3; +} + +// MsgRegisterInterchainAccountResponse defines the response for Msg/RegisterAccount +message MsgRegisterInterchainAccountResponse { + string channel_id = 1 [(gogoproto.moretags) = "yaml:\"channel_id\""]; + string port_id = 2 [(gogoproto.moretags) = "yaml:\"port_id\""]; +} + +// MsgSendTx defines the payload for Msg/SendTx +message MsgSendTx { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + string owner = 1; + string connection_id = 2 [(gogoproto.moretags) = "yaml:\"connection_id\""]; + ibc.applications.interchain_accounts.v1.InterchainAccountPacketData packet_data = 3 + [(gogoproto.moretags) = "yaml:\"packet_data\"", (gogoproto.nullable) = false]; + // Relative timeout timestamp provided will be added to the current block time during transaction execution. + // The timeout timestamp must be non-zero. + uint64 relative_timeout = 4 [(gogoproto.moretags) = "yaml:\"relative_timeout\""]; +} + +// MsgSendTxResponse defines the response for MsgSendTx +message MsgSendTxResponse { + uint64 sequence = 1; +} diff --git a/proto/ibc/applications/interchain_accounts/v1/genesis.proto b/proto/ibc/applications/interchain_accounts/genesis/v1/genesis.proto similarity index 78% rename from proto/ibc/applications/interchain_accounts/v1/genesis.proto rename to proto/ibc/applications/interchain_accounts/genesis/v1/genesis.proto index a92b6a0f17a..2fed3763ff3 100644 --- a/proto/ibc/applications/interchain_accounts/v1/genesis.proto +++ b/proto/ibc/applications/interchain_accounts/genesis/v1/genesis.proto @@ -1,8 +1,8 @@ syntax = "proto3"; -package ibc.applications.interchain_accounts.v1; +package ibc.applications.interchain_accounts.genesis.v1; -option go_package = "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/types"; +option go_package = "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/genesis/types"; import "gogoproto/gogo.proto"; import "ibc/applications/interchain_accounts/controller/v1/controller.proto"; @@ -36,11 +36,13 @@ message HostGenesisState { ibc.applications.interchain_accounts.host.v1.Params params = 4 [(gogoproto.nullable) = false]; } -// ActiveChannel contains a connection ID, port ID and associated active channel ID +// ActiveChannel contains a connection ID, port ID and associated active channel ID, as well as a boolean flag to +// indicate if the channel is middleware enabled message ActiveChannel { - string connection_id = 1 [(gogoproto.moretags) = "yaml:\"connection_id\""]; - string port_id = 2 [(gogoproto.moretags) = "yaml:\"port_id\""]; - string channel_id = 3 [(gogoproto.moretags) = "yaml:\"channel_id\""]; + string connection_id = 1 [(gogoproto.moretags) = "yaml:\"connection_id\""]; + string port_id = 2 [(gogoproto.moretags) = "yaml:\"port_id\""]; + string channel_id = 3 [(gogoproto.moretags) = "yaml:\"channel_id\""]; + bool is_middleware_enabled = 4 [(gogoproto.moretags) = "yaml:\"is_middleware_enabled\""]; } // RegisteredInterchainAccount contains a connection ID, port ID and associated interchain account address diff --git a/proto/ibc/applications/interchain_accounts/host/v1/host.proto b/proto/ibc/applications/interchain_accounts/host/v1/host.proto index e73510e57f1..18cc1d13da3 100644 --- a/proto/ibc/applications/interchain_accounts/host/v1/host.proto +++ b/proto/ibc/applications/interchain_accounts/host/v1/host.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package ibc.applications.interchain_accounts.host.v1; -option go_package = "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/host/types"; +option go_package = "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/host/types"; import "gogoproto/gogo.proto"; diff --git a/proto/ibc/applications/interchain_accounts/host/v1/query.proto b/proto/ibc/applications/interchain_accounts/host/v1/query.proto index 02d17d314a7..b89ed8ed871 100644 --- a/proto/ibc/applications/interchain_accounts/host/v1/query.proto +++ b/proto/ibc/applications/interchain_accounts/host/v1/query.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package ibc.applications.interchain_accounts.host.v1; -option go_package = "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/host/types"; +option go_package = "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/host/types"; import "google/api/annotations.proto"; import "ibc/applications/interchain_accounts/host/v1/host.proto"; diff --git a/proto/ibc/applications/interchain_accounts/v1/account.proto b/proto/ibc/applications/interchain_accounts/v1/account.proto index 2748b935778..d60c27a9c29 100644 --- a/proto/ibc/applications/interchain_accounts/v1/account.proto +++ b/proto/ibc/applications/interchain_accounts/v1/account.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package ibc.applications.interchain_accounts.v1; -option go_package = "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/types"; +option go_package = "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types"; import "cosmos_proto/cosmos.proto"; import "gogoproto/gogo.proto"; @@ -12,7 +12,7 @@ import "cosmos/auth/v1beta1/auth.proto"; message InterchainAccount { option (gogoproto.goproto_getters) = false; option (gogoproto.goproto_stringer) = false; - option (cosmos_proto.implements_interface) = "InterchainAccountI"; + option (cosmos_proto.implements_interface) = "ibc.applications.interchain_accounts.v1.InterchainAccountI"; cosmos.auth.v1beta1.BaseAccount base_account = 1 [(gogoproto.embed) = true, (gogoproto.moretags) = "yaml:\"base_account\""]; diff --git a/proto/ibc/applications/interchain_accounts/v1/metadata.proto b/proto/ibc/applications/interchain_accounts/v1/metadata.proto index 6a4b4abe57e..9ea7eeca337 100644 --- a/proto/ibc/applications/interchain_accounts/v1/metadata.proto +++ b/proto/ibc/applications/interchain_accounts/v1/metadata.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package ibc.applications.interchain_accounts.v1; -option go_package = "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/types"; +option go_package = "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types"; import "gogoproto/gogo.proto"; diff --git a/proto/ibc/applications/interchain_accounts/v1/packet.proto b/proto/ibc/applications/interchain_accounts/v1/packet.proto index 8e09a9d670c..d2dcf518eab 100644 --- a/proto/ibc/applications/interchain_accounts/v1/packet.proto +++ b/proto/ibc/applications/interchain_accounts/v1/packet.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package ibc.applications.interchain_accounts.v1; -option go_package = "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/types"; +option go_package = "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types"; import "google/protobuf/any.proto"; import "gogoproto/gogo.proto"; diff --git a/proto/ibc/applications/transfer/v1/authz.proto b/proto/ibc/applications/transfer/v1/authz.proto new file mode 100644 index 00000000000..8b27ac9cf7d --- /dev/null +++ b/proto/ibc/applications/transfer/v1/authz.proto @@ -0,0 +1,31 @@ +syntax = "proto3"; + +package ibc.applications.transfer.v1; + +option go_package = "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types"; + +import "cosmos_proto/cosmos.proto"; +import "gogoproto/gogo.proto"; +import "cosmos/base/v1beta1/coin.proto"; + +// Allocation defines the spend limit for a particular port and channel +message Allocation { + // the port on which the packet will be sent + string source_port = 1 [(gogoproto.moretags) = "yaml:\"source_port\""]; + // the channel by which the packet will be sent + string source_channel = 2 [(gogoproto.moretags) = "yaml:\"source_channel\""]; + // spend limitation on the channel + repeated cosmos.base.v1beta1.Coin spend_limit = 3 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; + // allow list of receivers, an empty allow list permits any receiver address + repeated string allow_list = 4; +} + +// TransferAuthorization allows the grantee to spend up to spend_limit coins from +// the granter's account for ibc transfer on a specific channel +message TransferAuthorization { + option (cosmos_proto.implements_interface) = "cosmos.authz.v1beta1.Authorization"; + + // port and channel amounts + repeated Allocation allocations = 1 [(gogoproto.nullable) = false]; +} diff --git a/proto/ibc/applications/transfer/v1/genesis.proto b/proto/ibc/applications/transfer/v1/genesis.proto index 34672fdebfb..5e09a34ab07 100644 --- a/proto/ibc/applications/transfer/v1/genesis.proto +++ b/proto/ibc/applications/transfer/v1/genesis.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package ibc.applications.transfer.v1; -option go_package = "github.com/cosmos/ibc-go/v4/modules/apps/transfer/types"; +option go_package = "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types"; import "ibc/applications/transfer/v1/transfer.proto"; import "gogoproto/gogo.proto"; diff --git a/proto/ibc/applications/transfer/v1/query.proto b/proto/ibc/applications/transfer/v1/query.proto index 075c0001c8a..89b70309f15 100644 --- a/proto/ibc/applications/transfer/v1/query.proto +++ b/proto/ibc/applications/transfer/v1/query.proto @@ -7,13 +7,13 @@ import "cosmos/base/query/v1beta1/pagination.proto"; import "ibc/applications/transfer/v1/transfer.proto"; import "google/api/annotations.proto"; -option go_package = "github.com/cosmos/ibc-go/v4/modules/apps/transfer/types"; +option go_package = "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types"; // Query provides defines the gRPC querier service. service Query { // DenomTrace queries a denomination trace information. rpc DenomTrace(QueryDenomTraceRequest) returns (QueryDenomTraceResponse) { - option (google.api.http).get = "/ibc/apps/transfer/v1/denom_traces/{hash}"; + option (google.api.http).get = "/ibc/apps/transfer/v1/denom_traces/{hash=**}"; } // DenomTraces queries all denomination traces. @@ -28,7 +28,7 @@ service Query { // DenomHash queries a denomination hash information. rpc DenomHash(QueryDenomHashRequest) returns (QueryDenomHashResponse) { - option (google.api.http).get = "/ibc/apps/transfer/v1/denom_hashes/{trace}"; + option (google.api.http).get = "/ibc/apps/transfer/v1/denom_hashes/{trace=**}"; } // EscrowAddress returns the escrow address for a particular port and channel id. diff --git a/proto/ibc/applications/transfer/v1/transfer.proto b/proto/ibc/applications/transfer/v1/transfer.proto index 1f92e81a627..2171074799a 100644 --- a/proto/ibc/applications/transfer/v1/transfer.proto +++ b/proto/ibc/applications/transfer/v1/transfer.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package ibc.applications.transfer.v1; -option go_package = "github.com/cosmos/ibc-go/v4/modules/apps/transfer/types"; +option go_package = "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types"; import "gogoproto/gogo.proto"; diff --git a/proto/ibc/applications/transfer/v1/tx.proto b/proto/ibc/applications/transfer/v1/tx.proto index 5befdf2fbc3..1c67aafb937 100644 --- a/proto/ibc/applications/transfer/v1/tx.proto +++ b/proto/ibc/applications/transfer/v1/tx.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package ibc.applications.transfer.v1; -option go_package = "github.com/cosmos/ibc-go/v4/modules/apps/transfer/types"; +option go_package = "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types"; import "gogoproto/gogo.proto"; import "cosmos/base/v1beta1/coin.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..7dc31347af1 100644 --- a/proto/ibc/applications/transfer/v2/packet.proto +++ b/proto/ibc/applications/transfer/v2/packet.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package ibc.applications.transfer.v2; -option go_package = "github.com/cosmos/ibc-go/v4/modules/apps/transfer/types"; +option go_package = "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types"; // FungibleTokenPacketData defines a struct for the packet payload // See FungibleTokenPacketData spec: @@ -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/channel.proto b/proto/ibc/core/channel/v1/channel.proto index feaf6d43503..287830ef437 100644 --- a/proto/ibc/core/channel/v1/channel.proto +++ b/proto/ibc/core/channel/v1/channel.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package ibc.core.channel.v1; -option go_package = "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types"; +option go_package = "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types"; import "gogoproto/gogo.proto"; import "ibc/core/client/v1/client.proto"; diff --git a/proto/ibc/core/channel/v1/genesis.proto b/proto/ibc/core/channel/v1/genesis.proto index 1c0ff6ee883..65cc928aa2a 100644 --- a/proto/ibc/core/channel/v1/genesis.proto +++ b/proto/ibc/core/channel/v1/genesis.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package ibc.core.channel.v1; -option go_package = "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types"; +option go_package = "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types"; import "gogoproto/gogo.proto"; import "ibc/core/channel/v1/channel.proto"; diff --git a/proto/ibc/core/channel/v1/query.proto b/proto/ibc/core/channel/v1/query.proto index 986633173cc..2d5bdb2fc8c 100644 --- a/proto/ibc/core/channel/v1/query.proto +++ b/proto/ibc/core/channel/v1/query.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package ibc.core.channel.v1; -option go_package = "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types"; +option go_package = "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types"; import "ibc/core/client/v1/client.proto"; import "cosmos/base/query/v1beta1/pagination.proto"; diff --git a/proto/ibc/core/channel/v1/tx.proto b/proto/ibc/core/channel/v1/tx.proto index 10e2ac29286..d81257eae80 100644 --- a/proto/ibc/core/channel/v1/tx.proto +++ b/proto/ibc/core/channel/v1/tx.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package ibc.core.channel.v1; -option go_package = "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types"; +option go_package = "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types"; import "gogoproto/gogo.proto"; import "ibc/core/client/v1/client.proto"; @@ -111,7 +111,8 @@ message MsgChannelOpenTry { // MsgChannelOpenTryResponse defines the Msg/ChannelOpenTry response type. message MsgChannelOpenTryResponse { - string version = 1; + string version = 1; + string channel_id = 2 [(gogoproto.moretags) = "yaml:\"channel_id\""]; } // MsgChannelOpenAck defines a msg sent by a Relayer to Chain A to acknowledge diff --git a/proto/ibc/core/channel/v1/upgrade.proto b/proto/ibc/core/channel/v1/upgrade.proto index 753487ce432..f143a99f8f8 100644 --- a/proto/ibc/core/channel/v1/upgrade.proto +++ b/proto/ibc/core/channel/v1/upgrade.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package ibc.core.channel.v1; -option go_package = "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types"; +option go_package = "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types"; import "gogoproto/gogo.proto"; import "ibc/core/client/v1/client.proto"; diff --git a/proto/ibc/core/client/v1/client.proto b/proto/ibc/core/client/v1/client.proto index 9d386592fa9..266649ba01c 100644 --- a/proto/ibc/core/client/v1/client.proto +++ b/proto/ibc/core/client/v1/client.proto @@ -2,11 +2,12 @@ syntax = "proto3"; package ibc.core.client.v1; -option go_package = "github.com/cosmos/ibc-go/v4/modules/core/02-client/types"; +option go_package = "github.com/cosmos/ibc-go/v7/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/client/v1/genesis.proto b/proto/ibc/core/client/v1/genesis.proto index b2930c48418..78821244169 100644 --- a/proto/ibc/core/client/v1/genesis.proto +++ b/proto/ibc/core/client/v1/genesis.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package ibc.core.client.v1; -option go_package = "github.com/cosmos/ibc-go/v4/modules/core/02-client/types"; +option go_package = "github.com/cosmos/ibc-go/v7/modules/core/02-client/types"; import "ibc/core/client/v1/client.proto"; import "gogoproto/gogo.proto"; diff --git a/proto/ibc/core/client/v1/query.proto b/proto/ibc/core/client/v1/query.proto index 2c9618bc801..0d26cf62e6d 100644 --- a/proto/ibc/core/client/v1/query.proto +++ b/proto/ibc/core/client/v1/query.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package ibc.core.client.v1; -option go_package = "github.com/cosmos/ibc-go/v4/modules/core/02-client/types"; +option go_package = "github.com/cosmos/ibc-go/v7/modules/core/02-client/types"; import "cosmos/base/query/v1beta1/pagination.proto"; import "ibc/core/client/v1/client.proto"; @@ -46,9 +46,9 @@ service Query { option (google.api.http).get = "/ibc/core/client/v1/client_status/{client_id}"; } - // ClientParams queries all parameters of the ibc client. + // ClientParams queries all parameters of the ibc client submodule. rpc ClientParams(QueryClientParamsRequest) returns (QueryClientParamsResponse) { - option (google.api.http).get = "/ibc/client/v1/params"; + option (google.api.http).get = "/ibc/core/client/v1/params"; } // UpgradedClientState queries an Upgraded IBC light client. diff --git a/proto/ibc/core/client/v1/tx.proto b/proto/ibc/core/client/v1/tx.proto index 11dfdadeaa6..23302d5e3aa 100644 --- a/proto/ibc/core/client/v1/tx.proto +++ b/proto/ibc/core/client/v1/tx.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package ibc.core.client.v1; -option go_package = "github.com/cosmos/ibc-go/v4/modules/core/02-client/types"; +option go_package = "github.com/cosmos/ibc-go/v7/modules/core/02-client/types"; import "gogoproto/gogo.proto"; import "google/protobuf/any.proto"; @@ -40,15 +40,15 @@ message MsgCreateClient { message MsgCreateClientResponse {} // MsgUpdateClient defines an sdk.Msg to update a IBC client state using -// the given header. +// the given client message. message MsgUpdateClient { option (gogoproto.equal) = false; option (gogoproto.goproto_getters) = false; // client unique identifier string client_id = 1 [(gogoproto.moretags) = "yaml:\"client_id\""]; - // header to update the light client - google.protobuf.Any header = 2; + // client message to update the light client + google.protobuf.Any client_message = 2; // signer address string signer = 3; } @@ -82,16 +82,17 @@ message MsgUpgradeClientResponse {} // MsgSubmitMisbehaviour defines an sdk.Msg type that submits Evidence for // light client misbehaviour. +// Warning: DEPRECATED message MsgSubmitMisbehaviour { option (gogoproto.equal) = false; option (gogoproto.goproto_getters) = false; // client unique identifier - string client_id = 1 [(gogoproto.moretags) = "yaml:\"client_id\""]; + string client_id = 1 [(gogoproto.moretags) = "yaml:\"client_id\"", deprecated = true]; // misbehaviour used for freezing the light client - google.protobuf.Any misbehaviour = 2; + google.protobuf.Any misbehaviour = 2 [deprecated = true]; // signer address - string signer = 3; + string signer = 3 [deprecated = true]; } // MsgSubmitMisbehaviourResponse defines the Msg/SubmitMisbehaviour response diff --git a/proto/ibc/core/commitment/v1/commitment.proto b/proto/ibc/core/commitment/v1/commitment.proto index b6a68a99fb7..4840ff3e067 100644 --- a/proto/ibc/core/commitment/v1/commitment.proto +++ b/proto/ibc/core/commitment/v1/commitment.proto @@ -2,10 +2,10 @@ syntax = "proto3"; package ibc.core.commitment.v1; -option go_package = "github.com/cosmos/ibc-go/v4/modules/core/23-commitment/types"; +option go_package = "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/types"; import "gogoproto/gogo.proto"; -import "proofs.proto"; +import "cosmos/ics23/v1/proofs.proto"; // MerkleRoot defines a merkle root hash. // In the Cosmos SDK, the AppHash of a block header becomes the root. @@ -37,5 +37,5 @@ message MerklePath { // should be succinct. // MerkleProofs are ordered from leaf-to-root message MerkleProof { - repeated ics23.CommitmentProof proofs = 1; + repeated cosmos.ics23.v1.CommitmentProof proofs = 1; } diff --git a/proto/ibc/core/connection/v1/connection.proto b/proto/ibc/core/connection/v1/connection.proto index 8360af98883..ba367c14d20 100644 --- a/proto/ibc/core/connection/v1/connection.proto +++ b/proto/ibc/core/connection/v1/connection.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package ibc.core.connection.v1; -option go_package = "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types"; +option go_package = "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types"; import "gogoproto/gogo.proto"; import "ibc/core/commitment/v1/commitment.proto"; diff --git a/proto/ibc/core/connection/v1/genesis.proto b/proto/ibc/core/connection/v1/genesis.proto index f616ae67e0d..122c5a4652d 100644 --- a/proto/ibc/core/connection/v1/genesis.proto +++ b/proto/ibc/core/connection/v1/genesis.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package ibc.core.connection.v1; -option go_package = "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types"; +option go_package = "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types"; import "gogoproto/gogo.proto"; import "ibc/core/connection/v1/connection.proto"; diff --git a/proto/ibc/core/connection/v1/query.proto b/proto/ibc/core/connection/v1/query.proto index 129f30a71a8..3c76b238910 100644 --- a/proto/ibc/core/connection/v1/query.proto +++ b/proto/ibc/core/connection/v1/query.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package ibc.core.connection.v1; -option go_package = "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types"; +option go_package = "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types"; import "gogoproto/gogo.proto"; import "cosmos/base/query/v1beta1/pagination.proto"; @@ -41,6 +41,11 @@ service Query { option (google.api.http).get = "/ibc/core/connection/v1/connections/{connection_id}/consensus_state/" "revision/{revision_number}/height/{revision_height}"; } + + // ConnectionParams queries all parameters of the ibc connection submodule. + rpc ConnectionParams(QueryConnectionParamsRequest) returns (QueryConnectionParamsResponse) { + option (google.api.http).get = "/ibc/core/connection/v1/params"; + } } // QueryConnectionRequest is the request type for the Query/Connection RPC @@ -136,3 +141,12 @@ message QueryConnectionConsensusStateResponse { // height at which the proof was retrieved ibc.core.client.v1.Height proof_height = 4 [(gogoproto.nullable) = false]; } + +// QueryConnectionParamsRequest is the request type for the Query/ConnectionParams RPC method. +message QueryConnectionParamsRequest {} + +// QueryConnectionParamsResponse is the response type for the Query/ConnectionParams RPC method. +message QueryConnectionParamsResponse { + // params defines the parameters of the module. + Params params = 1; +} \ No newline at end of file diff --git a/proto/ibc/core/connection/v1/tx.proto b/proto/ibc/core/connection/v1/tx.proto index b2fea632c36..af8f505c461 100644 --- a/proto/ibc/core/connection/v1/tx.proto +++ b/proto/ibc/core/connection/v1/tx.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package ibc.core.connection.v1; -option go_package = "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types"; +option go_package = "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types"; import "gogoproto/gogo.proto"; import "google/protobuf/any.proto"; @@ -67,6 +67,8 @@ message MsgConnectionOpenTry { ibc.core.client.v1.Height consensus_height = 11 [(gogoproto.moretags) = "yaml:\"consensus_height\"", (gogoproto.nullable) = false]; string signer = 12; + // optional proof data for host state machines that are unable to introspect their own consensus state + bytes host_consensus_state_proof = 13; } // MsgConnectionOpenTryResponse defines the Msg/ConnectionOpenTry response type. @@ -94,6 +96,8 @@ message MsgConnectionOpenAck { ibc.core.client.v1.Height consensus_height = 9 [(gogoproto.moretags) = "yaml:\"consensus_height\"", (gogoproto.nullable) = false]; string signer = 10; + // optional proof data for host state machines that are unable to introspect their own consensus state + bytes host_consensus_state_proof = 11; } // MsgConnectionOpenAckResponse defines the Msg/ConnectionOpenAck response type. diff --git a/proto/ibc/core/types/v1/genesis.proto b/proto/ibc/core/types/v1/genesis.proto index 4cc931d32f7..4e07551f823 100644 --- a/proto/ibc/core/types/v1/genesis.proto +++ b/proto/ibc/core/types/v1/genesis.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package ibc.core.types.v1; -option go_package = "github.com/cosmos/ibc-go/v4/modules/core/types"; +option go_package = "github.com/cosmos/ibc-go/v7/modules/core/types"; import "gogoproto/gogo.proto"; import "ibc/core/client/v1/genesis.proto"; diff --git a/proto/ibc/lightclients/localhost/v1/localhost.proto b/proto/ibc/lightclients/localhost/v1/localhost.proto deleted file mode 100644 index 9eda835ebb1..00000000000 --- a/proto/ibc/lightclients/localhost/v1/localhost.proto +++ /dev/null @@ -1,18 +0,0 @@ -syntax = "proto3"; - -package ibc.lightclients.localhost.v1; - -option go_package = "github.com/cosmos/ibc-go/v4/modules/light-clients/09-localhost/types"; - -import "gogoproto/gogo.proto"; -import "ibc/core/client/v1/client.proto"; - -// ClientState defines a loopback (localhost) client. It requires (read-only) -// access to keys outside the client prefix. -message ClientState { - option (gogoproto.goproto_getters) = false; - // self chain ID - string chain_id = 1 [(gogoproto.moretags) = "yaml:\"chain_id\""]; - // self latest block height - ibc.core.client.v1.Height height = 2 [(gogoproto.nullable) = false]; -} diff --git a/proto/ibc/lightclients/solomachine/v1/solomachine.proto b/proto/ibc/lightclients/solomachine/v1/solomachine.proto deleted file mode 100644 index 37bd81e923a..00000000000 --- a/proto/ibc/lightclients/solomachine/v1/solomachine.proto +++ /dev/null @@ -1,189 +0,0 @@ -syntax = "proto3"; - -package ibc.lightclients.solomachine.v1; - -option go_package = "github.com/cosmos/ibc-go/v4/modules/core/02-client/legacy/v100"; - -import "ibc/core/connection/v1/connection.proto"; -import "ibc/core/channel/v1/channel.proto"; -import "gogoproto/gogo.proto"; -import "google/protobuf/any.proto"; - -// ClientState defines a solo machine client that tracks the current consensus -// state and if the client is frozen. -message ClientState { - option (gogoproto.goproto_getters) = false; - // latest sequence of the client state - uint64 sequence = 1; - // frozen sequence of the solo machine - uint64 frozen_sequence = 2 [(gogoproto.moretags) = "yaml:\"frozen_sequence\""]; - ConsensusState consensus_state = 3 [(gogoproto.moretags) = "yaml:\"consensus_state\""]; - // when set to true, will allow governance to update a solo machine client. - // The client will be unfrozen if it is frozen. - bool allow_update_after_proposal = 4 [(gogoproto.moretags) = "yaml:\"allow_update_after_proposal\""]; -} - -// ConsensusState defines a solo machine consensus state. The sequence of a -// consensus state is contained in the "height" key used in storing the -// consensus state. -message ConsensusState { - option (gogoproto.goproto_getters) = false; - // public key of the solo machine - google.protobuf.Any public_key = 1 [(gogoproto.moretags) = "yaml:\"public_key\""]; - // diversifier allows the same public key to be re-used across different solo - // machine clients (potentially on different chains) without being considered - // misbehaviour. - string diversifier = 2; - uint64 timestamp = 3; -} - -// Header defines a solo machine consensus header -message Header { - option (gogoproto.goproto_getters) = false; - // sequence to update solo machine public key at - uint64 sequence = 1; - uint64 timestamp = 2; - bytes signature = 3; - google.protobuf.Any new_public_key = 4 [(gogoproto.moretags) = "yaml:\"new_public_key\""]; - string new_diversifier = 5 [(gogoproto.moretags) = "yaml:\"new_diversifier\""]; -} - -// Misbehaviour defines misbehaviour for a solo machine which consists -// of a sequence and two signatures over different messages at that sequence. -message Misbehaviour { - option (gogoproto.goproto_getters) = false; - string client_id = 1 [(gogoproto.moretags) = "yaml:\"client_id\""]; - uint64 sequence = 2; - SignatureAndData signature_one = 3 [(gogoproto.moretags) = "yaml:\"signature_one\""]; - SignatureAndData signature_two = 4 [(gogoproto.moretags) = "yaml:\"signature_two\""]; -} - -// SignatureAndData contains a signature and the data signed over to create that -// signature. -message SignatureAndData { - option (gogoproto.goproto_getters) = false; - bytes signature = 1; - DataType data_type = 2 [(gogoproto.moretags) = "yaml:\"data_type\""]; - bytes data = 3; - uint64 timestamp = 4; -} - -// TimestampedSignatureData contains the signature data and the timestamp of the -// signature. -message TimestampedSignatureData { - option (gogoproto.goproto_getters) = false; - bytes signature_data = 1 [(gogoproto.moretags) = "yaml:\"signature_data\""]; - uint64 timestamp = 2; -} - -// SignBytes defines the signed bytes used for signature verification. -message SignBytes { - option (gogoproto.goproto_getters) = false; - - uint64 sequence = 1; - uint64 timestamp = 2; - string diversifier = 3; - // type of the data used - DataType data_type = 4 [(gogoproto.moretags) = "yaml:\"data_type\""]; - // marshaled data - bytes data = 5; -} - -// DataType defines the type of solo machine proof being created. This is done -// to preserve uniqueness of different data sign byte encodings. -enum DataType { - option (gogoproto.goproto_enum_prefix) = false; - - // Default State - DATA_TYPE_UNINITIALIZED_UNSPECIFIED = 0 [(gogoproto.enumvalue_customname) = "UNSPECIFIED"]; - // Data type for client state verification - DATA_TYPE_CLIENT_STATE = 1 [(gogoproto.enumvalue_customname) = "CLIENT"]; - // Data type for consensus state verification - DATA_TYPE_CONSENSUS_STATE = 2 [(gogoproto.enumvalue_customname) = "CONSENSUS"]; - // Data type for connection state verification - DATA_TYPE_CONNECTION_STATE = 3 [(gogoproto.enumvalue_customname) = "CONNECTION"]; - // Data type for channel state verification - DATA_TYPE_CHANNEL_STATE = 4 [(gogoproto.enumvalue_customname) = "CHANNEL"]; - // Data type for packet commitment verification - DATA_TYPE_PACKET_COMMITMENT = 5 [(gogoproto.enumvalue_customname) = "PACKETCOMMITMENT"]; - // Data type for packet acknowledgement verification - DATA_TYPE_PACKET_ACKNOWLEDGEMENT = 6 [(gogoproto.enumvalue_customname) = "PACKETACKNOWLEDGEMENT"]; - // Data type for packet receipt absence verification - DATA_TYPE_PACKET_RECEIPT_ABSENCE = 7 [(gogoproto.enumvalue_customname) = "PACKETRECEIPTABSENCE"]; - // Data type for next sequence recv verification - DATA_TYPE_NEXT_SEQUENCE_RECV = 8 [(gogoproto.enumvalue_customname) = "NEXTSEQUENCERECV"]; - // Data type for header verification - DATA_TYPE_HEADER = 9 [(gogoproto.enumvalue_customname) = "HEADER"]; -} - -// HeaderData returns the SignBytes data for update verification. -message HeaderData { - option (gogoproto.goproto_getters) = false; - - // header public key - google.protobuf.Any new_pub_key = 1 [(gogoproto.moretags) = "yaml:\"new_pub_key\""]; - // header diversifier - string new_diversifier = 2 [(gogoproto.moretags) = "yaml:\"new_diversifier\""]; -} - -// ClientStateData returns the SignBytes data for client state verification. -message ClientStateData { - option (gogoproto.goproto_getters) = false; - - bytes path = 1; - google.protobuf.Any client_state = 2 [(gogoproto.moretags) = "yaml:\"client_state\""]; -} - -// ConsensusStateData returns the SignBytes data for consensus state -// verification. -message ConsensusStateData { - option (gogoproto.goproto_getters) = false; - - bytes path = 1; - google.protobuf.Any consensus_state = 2 [(gogoproto.moretags) = "yaml:\"consensus_state\""]; -} - -// ConnectionStateData returns the SignBytes data for connection state -// verification. -message ConnectionStateData { - option (gogoproto.goproto_getters) = false; - - bytes path = 1; - ibc.core.connection.v1.ConnectionEnd connection = 2; -} - -// ChannelStateData returns the SignBytes data for channel state -// verification. -message ChannelStateData { - option (gogoproto.goproto_getters) = false; - - bytes path = 1; - ibc.core.channel.v1.Channel channel = 2; -} - -// PacketCommitmentData returns the SignBytes data for packet commitment -// verification. -message PacketCommitmentData { - bytes path = 1; - bytes commitment = 2; -} - -// PacketAcknowledgementData returns the SignBytes data for acknowledgement -// verification. -message PacketAcknowledgementData { - bytes path = 1; - bytes acknowledgement = 2; -} - -// PacketReceiptAbsenceData returns the SignBytes data for -// packet receipt absence verification. -message PacketReceiptAbsenceData { - bytes path = 1; -} - -// NextSequenceRecvData returns the SignBytes data for verification of the next -// sequence to be received. -message NextSequenceRecvData { - bytes path = 1; - uint64 next_seq_recv = 2 [(gogoproto.moretags) = "yaml:\"next_seq_recv\""]; -} diff --git a/proto/ibc/lightclients/solomachine/v2/solomachine.proto b/proto/ibc/lightclients/solomachine/v2/solomachine.proto index c735fddddf9..250313319b4 100644 --- a/proto/ibc/lightclients/solomachine/v2/solomachine.proto +++ b/proto/ibc/lightclients/solomachine/v2/solomachine.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package ibc.lightclients.solomachine.v2; -option go_package = "github.com/cosmos/ibc-go/v4/modules/light-clients/06-solomachine/types"; +option go_package = "github.com/cosmos/ibc-go/v7/modules/core/02-client/migrations/v7"; import "ibc/core/connection/v1/connection.proto"; import "ibc/core/channel/v1/channel.proto"; diff --git a/proto/ibc/lightclients/solomachine/v3/solomachine.proto b/proto/ibc/lightclients/solomachine/v3/solomachine.proto new file mode 100644 index 00000000000..40e76b722e7 --- /dev/null +++ b/proto/ibc/lightclients/solomachine/v3/solomachine.proto @@ -0,0 +1,99 @@ +syntax = "proto3"; + +package ibc.lightclients.solomachine.v3; + +option go_package = "github.com/cosmos/ibc-go/v7/modules/light-clients/06-solomachine;solomachine"; + +import "gogoproto/gogo.proto"; +import "google/protobuf/any.proto"; + +// ClientState defines a solo machine client that tracks the current consensus +// state and if the client is frozen. +message ClientState { + option (gogoproto.goproto_getters) = false; + // latest sequence of the client state + uint64 sequence = 1; + // frozen sequence of the solo machine + bool is_frozen = 2 [(gogoproto.moretags) = "yaml:\"is_frozen\""]; + ConsensusState consensus_state = 3 [(gogoproto.moretags) = "yaml:\"consensus_state\""]; +} + +// ConsensusState defines a solo machine consensus state. The sequence of a +// consensus state is contained in the "height" key used in storing the +// consensus state. +message ConsensusState { + option (gogoproto.goproto_getters) = false; + // public key of the solo machine + google.protobuf.Any public_key = 1 [(gogoproto.moretags) = "yaml:\"public_key\""]; + // diversifier allows the same public key to be re-used across different solo + // machine clients (potentially on different chains) without being considered + // misbehaviour. + string diversifier = 2; + uint64 timestamp = 3; +} + +// Header defines a solo machine consensus header +message Header { + option (gogoproto.goproto_getters) = false; + + uint64 timestamp = 1; + bytes signature = 2; + google.protobuf.Any new_public_key = 3 [(gogoproto.moretags) = "yaml:\"new_public_key\""]; + string new_diversifier = 4 [(gogoproto.moretags) = "yaml:\"new_diversifier\""]; +} + +// Misbehaviour defines misbehaviour for a solo machine which consists +// of a sequence and two signatures over different messages at that sequence. +message Misbehaviour { + option (gogoproto.goproto_getters) = false; + + uint64 sequence = 1; + SignatureAndData signature_one = 2 [(gogoproto.moretags) = "yaml:\"signature_one\""]; + SignatureAndData signature_two = 3 [(gogoproto.moretags) = "yaml:\"signature_two\""]; +} + +// SignatureAndData contains a signature and the data signed over to create that +// signature. +message SignatureAndData { + option (gogoproto.goproto_getters) = false; + + bytes signature = 1; + bytes path = 2; + bytes data = 3; + uint64 timestamp = 4; +} + +// TimestampedSignatureData contains the signature data and the timestamp of the +// signature. +message TimestampedSignatureData { + option (gogoproto.goproto_getters) = false; + + bytes signature_data = 1 [(gogoproto.moretags) = "yaml:\"signature_data\""]; + uint64 timestamp = 2; +} + +// SignBytes defines the signed bytes used for signature verification. +message SignBytes { + option (gogoproto.goproto_getters) = false; + + // the sequence number + uint64 sequence = 1; + // the proof timestamp + uint64 timestamp = 2; + // the public key diversifier + string diversifier = 3; + // the standardised path bytes + bytes path = 4; + // the marshaled data bytes + bytes data = 5; +} + +// HeaderData returns the SignBytes data for update verification. +message HeaderData { + option (gogoproto.goproto_getters) = false; + + // header public key + google.protobuf.Any new_pub_key = 1 [(gogoproto.moretags) = "yaml:\"new_pub_key\""]; + // header diversifier + string new_diversifier = 2 [(gogoproto.moretags) = "yaml:\"new_diversifier\""]; +} diff --git a/proto/ibc/lightclients/tendermint/v1/tendermint.proto b/proto/ibc/lightclients/tendermint/v1/tendermint.proto index 55a4e06906d..79713e5a253 100644 --- a/proto/ibc/lightclients/tendermint/v1/tendermint.proto +++ b/proto/ibc/lightclients/tendermint/v1/tendermint.proto @@ -2,11 +2,11 @@ syntax = "proto3"; package ibc.lightclients.tendermint.v1; -option go_package = "github.com/cosmos/ibc-go/v4/modules/light-clients/07-tendermint/types"; +option go_package = "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint;tendermint"; import "tendermint/types/validator.proto"; import "tendermint/types/types.proto"; -import "proofs.proto"; +import "cosmos/ics23/v1/proofs.proto"; import "google/protobuf/duration.proto"; import "google/protobuf/timestamp.proto"; import "ibc/core/client/v1/client.proto"; @@ -41,7 +41,7 @@ message ClientState { [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"latest_height\""]; // Proof specifications used in verifying counterparty state - repeated ics23.ProofSpec proof_specs = 8 [(gogoproto.moretags) = "yaml:\"proof_specs\""]; + repeated cosmos.ics23.v1.ProofSpec proof_specs = 8 [(gogoproto.moretags) = "yaml:\"proof_specs\""]; // Path at which next upgraded client will be committed. // Each element corresponds to the key for a single CommitmentProof in the @@ -79,7 +79,8 @@ message ConsensusState { message Misbehaviour { option (gogoproto.goproto_getters) = false; - string client_id = 1 [(gogoproto.moretags) = "yaml:\"client_id\""]; + // ClientID is deprecated + string client_id = 1 [deprecated = true, (gogoproto.moretags) = "yaml:\"client_id\""]; Header header_1 = 2 [(gogoproto.customname) = "Header1", (gogoproto.moretags) = "yaml:\"header_1\""]; Header header_2 = 3 [(gogoproto.customname) = "Header2", (gogoproto.moretags) = "yaml:\"header_2\""]; } diff --git a/scripts/linting/lint-changed-go-files.sh b/scripts/linting/lint-changed-go-files.sh new file mode 100755 index 00000000000..286c4b772d0 --- /dev/null +++ b/scripts/linting/lint-changed-go-files.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash + +set -euo pipefail + +# lint_modified_go_files runs the linter only if changes have been made to any go files. +function lint_modified_go_files() { + local go_files="$(git diff --name-only | grep \.go$)" + for f in $go_files; do + local dir_name="$(dirname $f)" + golangci-lint run "${dir_name}" --fix --out-format=tab --issues-exit-code=0 + done +} + +lint_modified_go_files diff --git a/scripts/linting/lint-changed-md-files.sh b/scripts/linting/lint-changed-md-files.sh new file mode 100755 index 00000000000..214ec70f622 --- /dev/null +++ b/scripts/linting/lint-changed-md-files.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +set -euo pipefail + +# lint_modified_markdown_files runs the linter only if changes have been made to any md files. +function lint_modified_markdown_files() { + local markdown_files="$(git diff --name-only | grep \.md$)" + for f in $markdown_files; do + markdownlint "${f}" --fix + done +} + +lint_modified_markdown_files diff --git a/scripts/protoc-swagger-gen.sh b/scripts/protoc-swagger-gen.sh index aab3e65f6f4..fc58c14ef68 100755 --- a/scripts/protoc-swagger-gen.sh +++ b/scripts/protoc-swagger-gen.sh @@ -3,25 +3,22 @@ set -eo pipefail mkdir -p ./tmp-swagger-gen -proto_dirs=$(find ./proto -path -prune -o -name '*.proto' -print0 | xargs -0 -n1 dirname | sort | uniq) +cd proto +proto_dirs=$(find ./ -path -prune -o -name '*.proto' -print0 | xargs -0 -n1 dirname | sort | uniq) for dir in $proto_dirs; do - # generate swagger files (filter query files) query_file=$(find "${dir}" -maxdepth 1 \( -name 'query.proto' -o -name 'service.proto' \)) if [[ ! -z "$query_file" ]]; then - buf protoc \ - -I "proto" \ - -I "third_party/proto" \ - "$query_file" \ - --swagger_out=./tmp-swagger-gen \ - --swagger_opt=logtostderr=true --swagger_opt=fqn_for_swagger_name=true --swagger_opt=simple_operation_ids=true + buf generate --template buf.gen.swagger.yaml $query_file fi done +cd .. + # combine swagger files # uses nodejs package `swagger-combine`. # all the individual swagger files need to be configured in `config.json` for merging swagger-combine ./docs/client/config.json -o ./docs/client/swagger-ui/swagger.yaml -f yaml --continueOnConflictingPaths true --includeDefinitions true # clean swagger files -rm -rf ./tmp-swagger-gen +rm -rf ./tmp-swagger-gen \ No newline at end of file diff --git a/scripts/protocgen.sh b/scripts/protocgen.sh index 4736b47540b..06a576bfdd0 100755 --- a/scripts/protocgen.sh +++ b/scripts/protocgen.sh @@ -2,38 +2,15 @@ set -eo pipefail -protoc_gen_gocosmos() { - if ! grep "github.com/gogo/protobuf => github.com/regen-network/protobuf" go.mod &>/dev/null ; then - echo -e "\tPlease run this command from somewhere inside the ibc-go folder." - return 1 - fi +echo "Generating gogo proto code" +cd proto - go get github.com/regen-network/cosmos-proto/protoc-gen-gocosmos@latest 2>/dev/null -} +buf generate --template buf.gen.gogo.yaml $file -protoc_gen_gocosmos - -proto_dirs=$(find ./proto -path -prune -o -name '*.proto' -print0 | xargs -0 -n1 dirname | sort | uniq) -for dir in $proto_dirs; do - buf protoc \ - -I "proto" \ - -I "third_party/proto" \ - --gocosmos_out=plugins=interfacetype+grpc,\ -Mgoogle/protobuf/any.proto=github.com/cosmos/cosmos-sdk/codec/types:. \ - --grpc-gateway_out=logtostderr=true:. \ - $(find "${dir}" -maxdepth 1 -name '*.proto') - -done - -# command to generate docs using protoc-gen-doc -buf protoc \ - -I "proto" \ - -I "third_party/proto" \ - --doc_out=./docs/ibc \ - --doc_opt=./docs/protodoc-markdown.tmpl,proto-docs.md \ - $(find "$(pwd)/proto" -maxdepth 7 -name '*.proto') -go mod tidy +cd .. # move proto files to the right places cp -r github.com/cosmos/ibc-go/v*/modules/* modules/ rm -rf github.com + +go mod tidy diff --git a/testing/README.md b/testing/README.md index c682ba9ed8e..8eb95321d46 100644 --- a/testing/README.md +++ b/testing/README.md @@ -1,39 +1,42 @@ -# IBC Testing Package +# IBC Testing Package ## Components The testing package comprises of four parts constructed as a stack. + - coordinator - chain - path - endpoint A coordinator sits at the highest level and contains all the chains which have been initialized. -It also stores and updates the current global time. The time is manually incremented by a `TimeIncrement`. +It also stores and updates the current global time. The time is manually incremented by a `TimeIncrement`. This allows all the chains to remain in synchrony avoiding the issue of a counterparty being perceived to be in the future. The coordinator also contains functions to do basic setup of clients, connections, and channels -between two chains. +between two chains. A chain is an SDK application (as represented by an app.go file). Inside the chain is an `TestingApp` which allows the chain to simulate block production and transaction processing. The chain contains by default a single tendermint validator. A chain is used to process SDK messages. -A path connects two channel endpoints. It contains all the information needed to relay between two endpoints. +A path connects two channel endpoints. It contains all the information needed to relay between two endpoints. An endpoint represents a channel (and its associated client and connections) on some specific chain. It contains references to the chain it is on and the counterparty endpoint it is connected to. The endpoint contains functions -to interact with initialization and updates of its associated clients, connections, and channels. It can send, receive, +to interact with initialization and updates of its associated clients, connections, and channels. It can send, receive, and acknowledge packets. In general: + - endpoints are used for initialization and execution of IBC logic on one side of an IBC connection - paths are used to relay packets - chains are used to commit SDK messages -- coordinator is used to setup a path between two chains +- coordinator is used to setup a path between two chains ## Integration To integrate the testing package into your tests, you will need to define: + - a testing application - a function to initialize the testing application @@ -44,21 +47,21 @@ will need to be extended to fulfill the `TestingApp` interface. ```go type TestingApp interface { - abci.Application + abci.Application - // ibc-go additions - GetBaseApp() *baseapp.BaseApp - GetStakingKeeper() stakingkeeper.Keeper - GetIBCKeeper() *keeper.Keeper - GetScopedIBCKeeper() capabilitykeeper.ScopedKeeper - GetTxConfig() client.TxConfig + // ibc-go additions + GetBaseApp() *baseapp.BaseApp + GetStakingKeeper() ibctestingtypes.StakingKeeper + GetIBCKeeper() *keeper.Keeper + GetScopedIBCKeeper() capabilitykeeper.ScopedKeeper + GetTxConfig() client.TxConfig - // Implemented by SimApp - AppCodec() codec.Codec + // Implemented by SimApp + AppCodec() codec.Codec - // Implemented by BaseApp - LastCommitID() sdk.CommitID - LastBlockHeight() int64 + // Implemented by BaseApp + LastCommitID() sdk.CommitID + LastBlockHeight() int64 } ``` @@ -70,27 +73,27 @@ To begin, you will need to extend your application by adding the following funct // GetBaseApp implements the TestingApp interface. func (app *SimApp) GetBaseApp() *baseapp.BaseApp { - return app.BaseApp + return app.BaseApp } // GetStakingKeeper implements the TestingApp interface. -func (app *SimApp) GetStakingKeeper() stakingkeeper.Keeper { - return app.StakingKeeper +func (app *SimApp) GetStakingKeeper() ibctestingtypes.Keeper { + return app.StakingKeeper } // GetIBCKeeper implements the TestingApp interface. func (app *SimApp) GetIBCKeeper() *ibckeeper.Keeper { - return app.IBCKeeper + return app.IBCKeeper } // GetScopedIBCKeeper implements the TestingApp interface. func (app *SimApp) GetScopedIBCKeeper() capabilitykeeper.ScopedKeeper { - return app.ScopedIBCKeeper + return app.ScopedIBCKeeper } // GetTxConfig implements the TestingApp interface. func (app *SimApp) GetTxConfig() client.TxConfig { - return MakeTestEncodingConfig().TxConfig + return MakeTestEncodingConfig().TxConfig } ``` @@ -103,7 +106,7 @@ Your application may need to define `AppCodec()` if it does not already exist: // NOTE: This is solely to be used for testing purposes as it may be desirable // for modules to register their own custom testing types. func (app *SimApp) AppCodec() codec.Codec { - return app.appCodec + return app.appCodec } ``` @@ -115,19 +118,20 @@ The testing package requires that you provide a function to initialize your Test ```go func SetupTestingApp() (TestingApp, map[string]json.RawMessage) { - db := dbm.NewMemDB() - encCdc := simapp.MakeTestEncodingConfig() - app := simapp.NewSimApp(log.NewNopLogger(), db, nil, true, map[int64]bool{}, simapp.DefaultNodeHome, 5, encCdc, simapp.EmptyAppOptions{}) - return app, simapp.NewDefaultGenesisState(encCdc.Marshaler) + db := dbm.NewMemDB() + encCdc := simapp.MakeTestEncodingConfig() + app := simapp.NewSimApp(log.NewNopLogger(), db, nil, true, map[int64]bool{}, simapp.DefaultNodeHome, 5, encCdc, simapp.EmptyAppOptions{}) + return app, simapp.NewDefaultGenesisState(encCdc.Marshaler) } ``` This function returns the TestingApp and the default genesis state used to initialize the testing app. Change the value of `DefaultTestingAppInit` to use your function: + ```go func init() { - ibctesting.DefaultTestingAppInit = MySetupTestingAppFunction + ibctesting.DefaultTestingAppInit = MySetupTestingAppFunction } ``` @@ -135,69 +139,71 @@ func init() { ## Example Here is an example of how to setup your testing environment in every package you are testing: + ```go // KeeperTestSuite is a testing suite to test keeper functions. type KeeperTestSuite struct { - suite.Suite + suite.Suite - coordinator *ibctesting.Coordinator + coordinator *ibctesting.Coordinator - // testing chains used for convenience and readability - chainA *ibctesting.TestChain - chainB *ibctesting.TestChain + // testing chains used for convenience and readability + chainA *ibctesting.TestChain + chainB *ibctesting.TestChain } // TestKeeperTestSuite runs all the tests within this package. func TestKeeperTestSuite(t *testing.T) { - suite.Run(t, new(KeeperTestSuite)) + suite.Run(t, new(KeeperTestSuite)) } // SetupTest creates a coordinator with 2 test chains. func (suite *KeeperTestSuite) SetupTest() { - suite.coordinator = ibctesting.NewCoordinator(suite.T(), 2) // initializes 2 test chains - suite.chainA = suite.coordinator.GetChain(ibctesting.GetChainID(1)) // convenience and readability - suite.chainB = suite.coordinator.GetChain(ibctesting.GetChainID(2)) // convenience and readability + suite.coordinator = ibctesting.NewCoordinator(suite.T(), 2) // initializes 2 test chains + suite.chainA = suite.coordinator.GetChain(ibctesting.GetChainID(1)) // convenience and readability + suite.chainB = suite.coordinator.GetChain(ibctesting.GetChainID(2)) // convenience and readability } ``` -To create interaction between chainA and chainB, we need to contruct a `Path` these chains will use. +To create interaction between chainA and chainB, we need to contruct a `Path` these chains will use. A path contains two endpoints, `EndpointA` and `EndpointB` (corresponding to the order of the chains passed -into the `NewPath` function). A path is a pointer and its values will be filled in as necessary during the -setup portion of testing. +into the `NewPath` function). A path is a pointer and its values will be filled in as necessary during the +setup portion of testing. Endpoint Struct: + ```go // Endpoint is a which represents a channel endpoint and its associated // client and connections. It contains client, connection, and channel // configuration parameters. Endpoint functions will utilize the parameters // set in the configuration structs when executing IBC messages. type Endpoint struct { - Chain *TestChain - Counterparty *Endpoint - ClientID string - ConnectionID string - ChannelID string - - ClientConfig ClientConfig - ConnectionConfig *ConnectionConfig - ChannelConfig *ChannelConfig + Chain *TestChain + Counterparty *Endpoint + ClientID string + ConnectionID string + ChannelID string + + ClientConfig ClientConfig + ConnectionConfig *ConnectionConfig + ChannelConfig *ChannelConfig } ``` The fields empty after `NewPath` is called are `ClientID`, `ConnectionID` and `ChannelID` as the clients, connections, and channels for these endpoints have not yet been created. The -`ClientConfig`, `ConnectionConfig` and `ChannelConfig` contain all the necessary information for clients, +`ClientConfig`, `ConnectionConfig` and `ChannelConfig` contain all the necessary information for clients, connections, and channels to be initialized. If you would like to use endpoints which are intitialized to use your Port IDs, you might add a helper function similar to the one found in transfer: ```go func NewTransferPath(chainA, chainB *ibctesting.TestChain) *ibctesting.Path { - path := ibctesting.NewPath(chainA, chainB) - path.EndpointA.ChannelConfig.PortID = ibctesting.TransferPort - path.EndpointB.ChannelConfig.PortID = ibctesting.TransferPort + path := ibctesting.NewPath(chainA, chainB) + path.EndpointA.ChannelConfig.PortID = ibctesting.TransferPort + path.EndpointB.ChannelConfig.PortID = ibctesting.TransferPort - return path + return pa``th } ``` @@ -205,41 +211,41 @@ func NewTransferPath(chainA, chainB *ibctesting.TestChain) *ibctesting.Path { Path configurations should be set to the desired values before calling any `Setup` coordinator functions. To initialize the clients, connections, and channels for a path we can call the Setup functions of the coordinator: + - Setup() -> setup clients, connections, channels - SetupClients() -> setup clients only - SetupConnections() -> setup clients and connections only - Here is a basic example of the testing package being used to simulate IBC functionality: ```go - path := ibctesting.NewPath(suite.chainA, suite.chainB) // clientID, connectionID, channelID empty - suite.coordinator.Setup(path) // clientID, connectionID, channelID filled - suite.Require().Equal("07-tendermint-0", path.EndpointA.ClientID) - suite.Require().Equal("connection-0", path.EndpointA.ClientID) - suite.Require().Equal("channel-0", path.EndpointA.ClientID) + path := ibctesting.NewPath(suite.chainA, suite.chainB) // clientID, connectionID, channelID empty + suite.coordinator.Setup(path) // clientID, connectionID, channelID filled + suite.Require().Equal("07-tendermint-0", path.EndpointA.ClientID) + suite.Require().Equal("connection-0", path.EndpointA.ClientID) + suite.Require().Equal("channel-0", path.EndpointA.ClientID) - // create packet 1 - packet1 := NewPacket() // NewPacket would construct your packet + // send on endpointA + sequence, err := path.EndpointA.SendPacket(timeoutHeight1, timeoutTimestamp1, packet1Data) - // send on endpointA - path.EndpointA.SendPacket(packet1) + // create packet 1 + packet1 := NewPacket() // NewPacket would construct your packet - // receive on endpointB - path.EndpointB.RecvPacket(packet1) + // receive on endpointB + path.EndpointB.RecvPacket(packet1) - // acknowledge the receipt of the packet - path.EndpointA.AcknowledgePacket(packet1, ack) + // acknowledge the receipt of the packet + path.EndpointA.AcknowledgePacket(packet1, ack) - // we can also relay - packet2 := NewPacket() + // we can also relay + sequence, err := path.EndpointA.SendPacket(timeoutHeight2, timeoutTimestamp2, packet2Data) - path.EndpointA.SendPacket(packet2) + packet2 := NewPacket() - path.Relay(packet2, expectedAck) + path.Relay(packet2, expectedAck) - // if needed we can update our clients - path.EndpointB.UpdateClient() + // if needed we can update our clients + path.EndpointB.UpdateClient() ``` ### Transfer Testing Example @@ -250,71 +256,74 @@ If ICS 20 had its own simapp, its testing setup might include a `testing/app.go` package transfertesting import ( - "encoding/json" + "encoding/json" - "github.com/tendermint/tendermint/libs/log" - dbm "github.com/tendermint/tm-db" + "github.com/tendermint/tendermint/libs/log" + dbm "github.com/tendermint/tm-db" - "github.com/cosmos/ibc-go/v4/modules/apps/transfer/simapp" - ibctesting "github.com/cosmos/ibc-go/v4/testing" + "github.com/cosmos/ibc-go/v7/modules/apps/transfer/simapp" + ibctesting "github.com/cosmos/ibc-go/v7/testing" ) func SetupTransferTestingApp() (ibctesting.TestingApp, map[string]json.RawMessage) { - db := dbm.NewMemDB() - encCdc := simapp.MakeTestEncodingConfig() - app := simapp.NewSimApp(log.NewNopLogger(), db, nil, true, map[int64]bool{}, simapp.DefaultNodeHome, 5, encCdc, simapp.EmptyAppOptions{}) - return app, simapp.NewDefaultGenesisState(encCdc.Marshaler) + db := dbm.NewMemDB() + encCdc := simapp.MakeTestEncodingConfig() + app := simapp.NewSimApp(log.NewNopLogger(), db, nil, true, map[int64]bool{}, simapp.DefaultNodeHome, 5, encCdc, simapp.EmptyAppOptions{}) + return app, simapp.NewDefaultGenesisState(encCdc.Marshaler) } func init() { - ibctesting.DefaultTestingAppInit = SetupTransferTestingApp + ibctesting.DefaultTestingAppInit = SetupTransferTestingApp } func NewTransferPath(chainA, chainB *ibctesting.TestChain) *ibctesting.Path { - path := ibctesting.NewPath(chainA, chainB) - path.EndpointA.ChannelConfig.PortID = ibctesting.TransferPort - path.EndpointB.ChannelConfig.PortID = ibctesting.TransferPort + path := ibctesting.NewPath(chainA, chainB) + path.EndpointA.ChannelConfig.PortID = ibctesting.TransferPort + path.EndpointB.ChannelConfig.PortID = ibctesting.TransferPort - return path + return path } func GetTransferSimApp(chain *ibctesting.TestChain) *simapp.SimApp { - app, ok := chain.App.(*simapp.SimApp) - if !ok { - panic("not transfer app") - } + app, ok := chain.App.(*simapp.SimApp) + if !ok { + panic("not transfer app") + } - return app + return app } ``` ### Middleware Testing -When writing IBC applications acting as middleware, it might be desirable to test integration points. +When writing IBC applications acting as middleware, it might be desirable to test integration points. This can be done by wiring a middleware stack in the app.go file using existing applications as middleware and IBC base applications. -The mock module may also be leveraged to act as a base application in the instance that such an application is not available for testing or causes dependency concerns. +The mock module may also be leveraged to act as a base application in the instance that such an application is not available for testing or causes dependency concerns. -The mock IBC module contains a `MockIBCApp`. This struct contains a function field for every IBC App Module callback. -Each of these functions can be individually set to mock expected behaviour of a base application. +The mock IBC module contains a `MockIBCApp`. This struct contains a function field for every IBC App Module callback. +Each of these functions can be individually set to mock expected behaviour of a base application. The portID and scoped keeper for the `MockIBCApp` should be set within `MockIBCApp` before calling `NewIBCModule`. For example, if one wanted to test that the base application cannot affect the outcome of the `OnChanOpenTry` callback, the mock module base application callback could be updated as such: + ```go - mockModule.IBCApp.OnChanOpenTry = func(ctx sdk.Context, portID, channelID, version string) error { - return fmt.Errorf("mock base app must not be called for OnChanOpenTry") - } +mockModule.IBCApp.OnChanOpenTry = func(ctx sdk.Context, portID, channelID, version string) error { + return fmt.Errorf("mock base app must not be called for OnChanOpenTry") +} ``` -Using a mock module as a base application in a middleware stack may require adding the module to your `SimApp`. +Using a mock module as a base application in a middleware stack may require adding the module to your `SimApp`. This is because IBC will route to the top level IBC module of a middleware stack, so a module which never sits at the top of middleware stack will need to be accessed via a public field in `SimApp` This might look like: + ```go - suite.chainA.GetSimApp().ICAAuthModule.IBCApp.OnChanOpenInit = func(ctx sdk.Context, order channeltypes.Order, connectionHops []string, - portID, channelID string, chanCap *capabilitytypes.Capability, - counterparty channeltypes.Counterparty, version string, - ) error { - return fmt.Errorf("mock ica auth fails") - } +suite.chainA.GetSimApp().ICAAuthModule.IBCApp.OnChanOpenInit = func( + ctx sdk.Context, order channeltypes.Order, connectionHops []string, + portID, channelID string, chanCap *capabilitytypes.Capability, + counterparty channeltypes.Counterparty, version string, +) error { + return fmt.Errorf("mock ica auth fails") +} ``` diff --git a/testing/app.go b/testing/app.go index 9392c42424c..fea0ed503b4 100644 --- a/testing/app.go +++ b/testing/app.go @@ -5,16 +5,18 @@ import ( "testing" "time" + "cosmossdk.io/math" "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/codec" codectypes "github.com/cosmos/cosmos-sdk/codec/types" cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + storetypes "github.com/cosmos/cosmos-sdk/store/types" + simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" capabilitykeeper "github.com/cosmos/cosmos-sdk/x/capability/keeper" - stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" "github.com/stretchr/testify/require" abci "github.com/tendermint/tendermint/abci/types" @@ -23,18 +25,19 @@ import ( tmtypes "github.com/tendermint/tendermint/types" dbm "github.com/tendermint/tm-db" - "github.com/cosmos/ibc-go/v4/modules/core/keeper" - "github.com/cosmos/ibc-go/v4/testing/simapp" + "github.com/cosmos/ibc-go/v7/modules/core/keeper" + "github.com/cosmos/ibc-go/v7/testing/simapp" + ibctestingtypes "github.com/cosmos/ibc-go/v7/testing/types" ) -var DefaultTestingAppInit func() (TestingApp, map[string]json.RawMessage) = SetupTestingApp +var DefaultTestingAppInit = SetupTestingApp type TestingApp interface { abci.Application // ibc-go additions GetBaseApp() *baseapp.BaseApp - GetStakingKeeper() stakingkeeper.Keeper + GetStakingKeeper() ibctestingtypes.StakingKeeper GetIBCKeeper() *keeper.Keeper GetScopedIBCKeeper() capabilitykeeper.ScopedKeeper GetTxConfig() client.TxConfig @@ -43,14 +46,14 @@ type TestingApp interface { AppCodec() codec.Codec // Implemented by BaseApp - LastCommitID() sdk.CommitID + LastCommitID() storetypes.CommitID LastBlockHeight() int64 } func SetupTestingApp() (TestingApp, map[string]json.RawMessage) { db := dbm.NewMemDB() encCdc := simapp.MakeTestEncodingConfig() - app := simapp.NewSimApp(log.NewNopLogger(), db, nil, true, map[int64]bool{}, simapp.DefaultNodeHome, 5, encCdc, simapp.EmptyAppOptions{}) + app := simapp.NewSimApp(log.NewNopLogger(), db, nil, true, map[int64]bool{}, simapp.DefaultNodeHome, 5, encCdc, simtestutil.EmptyAppOptions{}) return app, simapp.NewDefaultGenesisState(encCdc.Marshaler) } @@ -58,7 +61,7 @@ func SetupTestingApp() (TestingApp, map[string]json.RawMessage) { // that also act as delegators. For simplicity, each validator is bonded with a delegation // of one consensus engine unit (10^6) in the default token of the simapp from first genesis // account. A Nop logger is set in SimApp. -func SetupWithGenesisValSet(t *testing.T, valSet *tmtypes.ValidatorSet, genAccs []authtypes.GenesisAccount, chainID string, powerReduction sdk.Int, balances ...banktypes.Balance) TestingApp { +func SetupWithGenesisValSet(t testing.TB, valSet *tmtypes.ValidatorSet, genAccs []authtypes.GenesisAccount, chainID string, powerReduction math.Int, balances ...banktypes.Balance) TestingApp { app, genesisState := DefaultTestingAppInit() // set genesis accounts @@ -110,7 +113,7 @@ func SetupWithGenesisValSet(t *testing.T, valSet *tmtypes.ValidatorSet, genAccs genesisState[stakingtypes.ModuleName] = app.AppCodec().MustMarshalJSON(&stakingGenesis) // update total supply - bankGenesis := banktypes.NewGenesisState(banktypes.DefaultGenesisState().Params, balances, sdk.NewCoins(), []banktypes.Metadata{}) + bankGenesis := banktypes.NewGenesisState(banktypes.DefaultGenesisState().Params, balances, sdk.NewCoins(), []banktypes.Metadata{}, []banktypes.SendEnabled{}) genesisState[banktypes.ModuleName] = app.AppCodec().MustMarshalJSON(bankGenesis) stateBytes, err := json.MarshalIndent(genesisState, "", " ") @@ -119,10 +122,9 @@ func SetupWithGenesisValSet(t *testing.T, valSet *tmtypes.ValidatorSet, genAccs // init chain will set the validator set and initialize the genesis accounts app.InitChain( abci.RequestInitChain{ - ChainId: chainID, - Validators: []abci.ValidatorUpdate{}, - ConsensusParams: simapp.DefaultConsensusParams, - AppStateBytes: stateBytes, + ChainId: chainID, + Validators: []abci.ValidatorUpdate{}, + AppStateBytes: stateBytes, }, ) diff --git a/testing/chain.go b/testing/chain.go index 038d20c4c54..b82e2cd9823 100644 --- a/testing/chain.go +++ b/testing/chain.go @@ -5,17 +5,17 @@ import ( "testing" "time" + errorsmod "cosmossdk.io/errors" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" capabilitykeeper "github.com/cosmos/cosmos-sdk/x/capability/keeper" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - "github.com/cosmos/cosmos-sdk/x/staking/teststaking" + "github.com/cosmos/cosmos-sdk/x/staking/testutil" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" "github.com/stretchr/testify/require" abci "github.com/tendermint/tendermint/abci/types" @@ -25,14 +25,14 @@ import ( tmtypes "github.com/tendermint/tendermint/types" tmversion "github.com/tendermint/tendermint/version" - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - commitmenttypes "github.com/cosmos/ibc-go/v4/modules/core/23-commitment/types" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" - "github.com/cosmos/ibc-go/v4/modules/core/exported" - "github.com/cosmos/ibc-go/v4/modules/core/types" - ibctmtypes "github.com/cosmos/ibc-go/v4/modules/light-clients/07-tendermint/types" - "github.com/cosmos/ibc-go/v4/testing/mock" - "github.com/cosmos/ibc-go/v4/testing/simapp" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + commitmenttypes "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + "github.com/cosmos/ibc-go/v7/modules/core/exported" + "github.com/cosmos/ibc-go/v7/modules/core/types" + ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" + "github.com/cosmos/ibc-go/v7/testing/mock" + "github.com/cosmos/ibc-go/v7/testing/simapp" ) var MaxAccounts = 10 @@ -48,13 +48,13 @@ type SenderAccount struct { // is used for delivering transactions through the application state. // NOTE: the actual application uses an empty chain-id for ease of testing. type TestChain struct { - *testing.T + testing.TB Coordinator *Coordinator App TestingApp ChainID string - LastHeader *ibctmtypes.Header // header for last block height committed - CurrentHeader tmproto.Header // header for current block height + LastHeader *ibctm.Header // header for last block height committed + CurrentHeader tmproto.Header // header for current block height QueryServer types.QueryServer TxConfig client.TxConfig Codec codec.BinaryCodec @@ -91,7 +91,7 @@ type TestChain struct { // // CONTRACT: Validator array must be provided in the order expected by Tendermint. // i.e. sorted first by power and then lexicographically by address. -func NewTestChainWithValSet(t *testing.T, coord *Coordinator, chainID string, valSet *tmtypes.ValidatorSet, signers map[string]tmtypes.PrivValidator) *TestChain { +func NewTestChainWithValSet(tb testing.TB, coord *Coordinator, chainID string, valSet *tmtypes.ValidatorSet, signers map[string]tmtypes.PrivValidator) *TestChain { genAccs := []authtypes.GenesisAccount{} genBals := []banktypes.Balance{} senderAccs := []SenderAccount{} @@ -101,8 +101,9 @@ func NewTestChainWithValSet(t *testing.T, coord *Coordinator, chainID string, va senderPrivKey := secp256k1.GenPrivKey() acc := authtypes.NewBaseAccount(senderPrivKey.PubKey().Address().Bytes(), senderPrivKey.PubKey(), uint64(i), 0) amount, ok := sdk.NewIntFromString("10000000000000000000") - require.True(t, ok) + require.True(tb, ok) + // add sender account balance := banktypes.Balance{ Address: acc.GetAddress().String(), Coins: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, amount)), @@ -119,7 +120,7 @@ func NewTestChainWithValSet(t *testing.T, coord *Coordinator, chainID string, va senderAccs = append(senderAccs, senderAcc) } - app := SetupWithGenesisValSet(t, valSet, genAccs, chainID, sdk.DefaultPowerReduction, genBals...) + app := SetupWithGenesisValSet(tb, valSet, genAccs, chainID, sdk.DefaultPowerReduction, genBals...) // create current header and call begin block header := tmproto.Header{ @@ -132,7 +133,7 @@ func NewTestChainWithValSet(t *testing.T, coord *Coordinator, chainID string, va // create an account to send transactions from chain := &TestChain{ - T: t, + TB: tb, Coordinator: coord, ChainID: chainID, App: app, @@ -189,7 +190,7 @@ func (chain *TestChain) GetContext() sdk.Context { // their own SimApp. func (chain *TestChain) GetSimApp() *simapp.SimApp { app, ok := chain.App.(*simapp.SimApp) - require.True(chain.T, ok) + require.True(chain.TB, ok) return app } @@ -200,21 +201,28 @@ func (chain *TestChain) QueryProof(key []byte) ([]byte, clienttypes.Height) { return chain.QueryProofAtHeight(key, chain.App.LastBlockHeight()) } -// QueryProof performs an abci query with the given key and returns the proto encoded merkle proof -// for the query and the height at which the proof will succeed on a tendermint verifier. +// QueryProofAtHeight performs an abci query with the given key and returns the proto encoded merkle proof +// for the query and the height at which the proof will succeed on a tendermint verifier. Only the IBC +// store is supported func (chain *TestChain) QueryProofAtHeight(key []byte, height int64) ([]byte, clienttypes.Height) { + return chain.QueryProofForStore(exported.StoreKey, key, height) +} + +// QueryProofForStore performs an abci query with the given key and returns the proto encoded merkle proof +// for the query and the height at which the proof will succeed on a tendermint verifier. +func (chain *TestChain) QueryProofForStore(storeKey string, key []byte, height int64) ([]byte, clienttypes.Height) { res := chain.App.Query(abci.RequestQuery{ - Path: fmt.Sprintf("store/%s/key", host.StoreKey), + Path: fmt.Sprintf("store/%s/key", storeKey), Height: height - 1, Data: key, Prove: true, }) merkleProof, err := commitmenttypes.ConvertProofs(res.ProofOps) - require.NoError(chain.T, err) + require.NoError(chain.TB, err) proof, err := chain.App.AppCodec().Marshal(&merkleProof) - require.NoError(chain.T, err) + require.NoError(chain.TB, err) revision := clienttypes.ParseChainID(chain.ChainID) @@ -235,10 +243,10 @@ func (chain *TestChain) QueryUpgradeProof(key []byte, height uint64) ([]byte, cl }) merkleProof, err := commitmenttypes.ConvertProofs(res.ProofOps) - require.NoError(chain.T, err) + require.NoError(chain.TB, err) proof, err := chain.App.AppCodec().Marshal(&merkleProof) - require.NoError(chain.T, err) + require.NoError(chain.TB, err) revision := clienttypes.ParseChainID(chain.ChainID) @@ -278,7 +286,7 @@ func (chain *TestChain) NextBlock() { // val set changes returned from previous block get applied to the next validators // of this block. See tendermint spec for details. chain.Vals = chain.NextVals - chain.NextVals = ApplyValSetChanges(chain.T, chain.Vals, res.ValidatorUpdates) + chain.NextVals = ApplyValSetChanges(chain.TB, chain.Vals, res.ValidatorUpdates) // increment the current header chain.CurrentHeader = tmproto.Header{ @@ -290,6 +298,7 @@ func (chain *TestChain) NextBlock() { Time: chain.CurrentHeader.Time, ValidatorsHash: chain.Vals.Hash(), NextValidatorsHash: chain.NextVals.Hash(), + ProposerAddress: chain.CurrentHeader.ProposerAddress, } chain.App.BeginBlock(abci.RequestBeginBlock{Header: chain.CurrentHeader}) @@ -309,15 +318,14 @@ func (chain *TestChain) SendMsgs(msgs ...sdk.Msg) (*sdk.Result, error) { chain.Coordinator.UpdateTimeForChain(chain) _, r, err := simapp.SignAndDeliver( - chain.T, + chain.TB, chain.TxConfig, chain.App.GetBaseApp(), - chain.GetContext().BlockHeader(), msgs, chain.ChainID, []uint64{chain.SenderAccount.GetAccountNumber()}, []uint64{chain.SenderAccount.GetSequence()}, - true, true, chain.SenderPrivKey, + true, chain.SenderPrivKey, ) if err != nil { return nil, err @@ -327,7 +335,10 @@ func (chain *TestChain) SendMsgs(msgs ...sdk.Msg) (*sdk.Result, error) { chain.NextBlock() // increment sequence for successful transaction execution - chain.SenderAccount.SetSequence(chain.SenderAccount.GetSequence() + 1) + err = chain.SenderAccount.SetSequence(chain.SenderAccount.GetSequence() + 1) + if err != nil { + return nil, err + } chain.Coordinator.IncrementTime() @@ -338,7 +349,7 @@ func (chain *TestChain) SendMsgs(msgs ...sdk.Msg) (*sdk.Result, error) { // expected to exist otherwise testing will fail. func (chain *TestChain) GetClientState(clientID string) exported.ClientState { clientState, found := chain.App.GetIBCKeeper().ClientKeeper.GetClientState(chain.GetContext(), clientID) - require.True(chain.T, found) + require.True(chain.TB, found) return clientState } @@ -359,7 +370,7 @@ func (chain *TestChain) GetValsAtHeight(height int64) (*tmtypes.ValidatorSet, bo valSet := stakingtypes.Validators(histInfo.Valset) - tmValidators, err := teststaking.ToTmValidators(valSet, sdk.DefaultPowerReduction) + tmValidators, err := testutil.ToTmValidators(valSet, sdk.DefaultPowerReduction) if err != nil { panic(err) } @@ -370,7 +381,7 @@ func (chain *TestChain) GetValsAtHeight(height int64) (*tmtypes.ValidatorSet, bo // acknowledgement does not exist then testing will fail. func (chain *TestChain) GetAcknowledgement(packet exported.PacketI) []byte { ack, found := chain.App.GetIBCKeeper().ChannelKeeper.GetPacketAcknowledgement(chain.GetContext(), packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()) - require.True(chain.T, found) + require.True(chain.TB, found) return ack } @@ -382,13 +393,13 @@ func (chain *TestChain) GetPrefix() commitmenttypes.MerklePrefix { // ConstructUpdateTMClientHeader will construct a valid 07-tendermint Header to update the // light client on the source chain. -func (chain *TestChain) ConstructUpdateTMClientHeader(counterparty *TestChain, clientID string) (*ibctmtypes.Header, error) { +func (chain *TestChain) ConstructUpdateTMClientHeader(counterparty *TestChain, clientID string) (*ibctm.Header, error) { return chain.ConstructUpdateTMClientHeaderWithTrustedHeight(counterparty, clientID, clienttypes.ZeroHeight()) } // ConstructUpdateTMClientHeader will construct a valid 07-tendermint Header to update the // light client on the source chain. -func (chain *TestChain) ConstructUpdateTMClientHeaderWithTrustedHeight(counterparty *TestChain, clientID string, trustedHeight clienttypes.Height) (*ibctmtypes.Header, error) { +func (chain *TestChain) ConstructUpdateTMClientHeaderWithTrustedHeight(counterparty *TestChain, clientID string, trustedHeight clienttypes.Height) (*ibctm.Header, error) { header := counterparty.LastHeader // Relayer must query for LatestHeight on client to get TrustedHeight if the trusted height is not set if trustedHeight.IsZero() { @@ -410,7 +421,7 @@ func (chain *TestChain) ConstructUpdateTMClientHeaderWithTrustedHeight(counterpa // NextValidatorsHash tmTrustedVals, ok = counterparty.GetValsAtHeight(int64(trustedHeight.RevisionHeight + 1)) if !ok { - return nil, sdkerrors.Wrapf(ibctmtypes.ErrInvalidHeaderHeight, "could not retrieve trusted validators at trustedHeight: %d", trustedHeight) + return nil, errorsmod.Wrapf(ibctm.ErrInvalidHeaderHeight, "could not retrieve trusted validators at trustedHeight: %d", trustedHeight) } } // inject trusted fields into last header @@ -434,18 +445,18 @@ func (chain *TestChain) ExpireClient(amount time.Duration) { // CurrentTMClientHeader creates a TM header using the current header parameters // on the chain. The trusted fields in the header are set to nil. -func (chain *TestChain) CurrentTMClientHeader() *ibctmtypes.Header { +func (chain *TestChain) CurrentTMClientHeader() *ibctm.Header { return chain.CreateTMClientHeader(chain.ChainID, chain.CurrentHeader.Height, clienttypes.Height{}, chain.CurrentHeader.Time, chain.Vals, chain.NextVals, nil, chain.Signers) } // CreateTMClientHeader creates a TM header to update the TM client. Args are passed in to allow // caller flexibility to use params that differ from the chain. -func (chain *TestChain) CreateTMClientHeader(chainID string, blockHeight int64, trustedHeight clienttypes.Height, timestamp time.Time, tmValSet, nextVals, tmTrustedVals *tmtypes.ValidatorSet, signers map[string]tmtypes.PrivValidator) *ibctmtypes.Header { +func (chain *TestChain) CreateTMClientHeader(chainID string, blockHeight int64, trustedHeight clienttypes.Height, timestamp time.Time, tmValSet, nextVals, tmTrustedVals *tmtypes.ValidatorSet, signers map[string]tmtypes.PrivValidator) *ibctm.Header { var ( valSet *tmproto.ValidatorSet trustedVals *tmproto.ValidatorSet ) - require.NotNil(chain.T, tmValSet) + require.NotNil(chain.TB, tmValSet) vsetHash := tmValSet.Hash() nextValHash := nextVals.Hash() @@ -474,32 +485,32 @@ func (chain *TestChain) CreateTMClientHeader(chainID string, blockHeight int64, // MakeCommit expects a signer array in the same order as the validator array. // Thus we iterate over the ordered validator set and construct a signer array // from the signer map in the same order. - var signerArr []tmtypes.PrivValidator - for _, v := range tmValSet.Validators { + var signerArr []tmtypes.PrivValidator //nolint:prealloc // using prealloc here would be needlessly complex + for _, v := range tmValSet.Validators { //nolint:staticcheck // need to check for nil validator set signerArr = append(signerArr, signers[v.Address.String()]) } commit, err := tmtypes.MakeCommit(blockID, blockHeight, 1, voteSet, signerArr, timestamp) - require.NoError(chain.T, err) + require.NoError(chain.TB, err) signedHeader := &tmproto.SignedHeader{ Header: tmHeader.ToProto(), Commit: commit.ToProto(), } - if tmValSet != nil { + if tmValSet != nil { //nolint:staticcheck valSet, err = tmValSet.ToProto() - require.NoError(chain.T, err) + require.NoError(chain.TB, err) } if tmTrustedVals != nil { trustedVals, err = tmTrustedVals.ToProto() - require.NoError(chain.T, err) + require.NoError(chain.TB, err) } // The trusted fields may be nil. They may be filled before relaying messages to a client. // The relayer is responsible for querying client and injecting appropriate trusted fields. - return &ibctmtypes.Header{ + return &ibctm.Header{ SignedHeader: signedHeader, ValidatorSet: valSet, TrustedHeight: trustedHeight, @@ -528,11 +539,11 @@ func (chain *TestChain) CreatePortCapability(scopedKeeper capabilitykeeper.Scope if !ok { // create capability using the IBC capability keeper cap, err := chain.App.GetScopedIBCKeeper().NewCapability(chain.GetContext(), host.PortPath(portID)) - require.NoError(chain.T, err) + require.NoError(chain.TB, err) // claim capability using the scopedKeeper err = scopedKeeper.ClaimCapability(chain.GetContext(), cap, host.PortPath(portID)) - require.NoError(chain.T, err) + require.NoError(chain.TB, err) } chain.NextBlock() @@ -542,7 +553,7 @@ func (chain *TestChain) CreatePortCapability(scopedKeeper capabilitykeeper.Scope // exist, otherwise testing will fail. func (chain *TestChain) GetPortCapability(portID string) *capabilitytypes.Capability { cap, ok := chain.App.GetScopedIBCKeeper().GetCapability(chain.GetContext(), host.PortPath(portID)) - require.True(chain.T, ok) + require.True(chain.TB, ok) return cap } @@ -556,9 +567,9 @@ func (chain *TestChain) CreateChannelCapability(scopedKeeper capabilitykeeper.Sc _, ok := chain.App.GetScopedIBCKeeper().GetCapability(chain.GetContext(), capName) if !ok { cap, err := chain.App.GetScopedIBCKeeper().NewCapability(chain.GetContext(), capName) - require.NoError(chain.T, err) + require.NoError(chain.TB, err) err = scopedKeeper.ClaimCapability(chain.GetContext(), cap, capName) - require.NoError(chain.T, err) + require.NoError(chain.TB, err) } chain.NextBlock() @@ -568,7 +579,13 @@ func (chain *TestChain) CreateChannelCapability(scopedKeeper capabilitykeeper.Sc // The capability must exist, otherwise testing will fail. func (chain *TestChain) GetChannelCapability(portID, channelID string) *capabilitytypes.Capability { cap, ok := chain.App.GetScopedIBCKeeper().GetCapability(chain.GetContext(), host.ChannelCapabilityPath(portID, channelID)) - require.True(chain.T, ok) + require.True(chain.TB, ok) 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/chain_test.go b/testing/chain_test.go index 5edb189c2f8..4f1f4e4a118 100644 --- a/testing/chain_test.go +++ b/testing/chain_test.go @@ -3,12 +3,11 @@ package ibctesting_test import ( "testing" - "github.com/stretchr/testify/require" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/stretchr/testify/require" - ibctesting "github.com/cosmos/ibc-go/v4/testing" + ibctesting "github.com/cosmos/ibc-go/v7/testing" ) func TestChangeValSet(t *testing.T) { @@ -24,16 +23,18 @@ func TestChangeValSet(t *testing.T) { amount2, ok := sdk.NewIntFromString("30000000000000000000") require.True(t, ok) - val := chainA.App.GetStakingKeeper().GetValidators(chainA.GetContext(), 4) + val := chainA.GetSimApp().StakingKeeper.GetValidators(chainA.GetContext(), 4) - chainA.App.GetStakingKeeper().Delegate(chainA.GetContext(), chainA.SenderAccounts[1].SenderAccount.GetAddress(), + chainA.GetSimApp().StakingKeeper.Delegate(chainA.GetContext(), chainA.SenderAccounts[1].SenderAccount.GetAddress(), //nolint:errcheck // ignore error for test amount, types.Unbonded, val[1], true) - chainA.App.GetStakingKeeper().Delegate(chainA.GetContext(), chainA.SenderAccounts[3].SenderAccount.GetAddress(), + chainA.GetSimApp().StakingKeeper.Delegate(chainA.GetContext(), chainA.SenderAccounts[3].SenderAccount.GetAddress(), //nolint:errcheck // ignore error for test amount2, types.Unbonded, val[3], true) coord.CommitBlock(chainA) // verify that update clients works even after validator update goes into effect - path.EndpointB.UpdateClient() - path.EndpointB.UpdateClient() + err := path.EndpointB.UpdateClient() + require.NoError(t, err) + err = path.EndpointB.UpdateClient() + require.NoError(t, err) } diff --git a/testing/config.go b/testing/config.go index b75ecc8d918..9a6ace904ba 100644 --- a/testing/config.go +++ b/testing/config.go @@ -3,11 +3,11 @@ package ibctesting import ( "time" - connectiontypes "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types" - channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" - "github.com/cosmos/ibc-go/v4/modules/core/exported" - ibctmtypes "github.com/cosmos/ibc-go/v4/modules/light-clients/07-tendermint/types" - "github.com/cosmos/ibc-go/v4/testing/mock" + connectiontypes "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + "github.com/cosmos/ibc-go/v7/modules/core/exported" + ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" + "github.com/cosmos/ibc-go/v7/testing/mock" ) type ClientConfig interface { @@ -15,22 +15,18 @@ type ClientConfig interface { } type TendermintConfig struct { - TrustLevel ibctmtypes.Fraction - TrustingPeriod time.Duration - UnbondingPeriod time.Duration - MaxClockDrift time.Duration - AllowUpdateAfterExpiry bool - AllowUpdateAfterMisbehaviour bool + TrustLevel ibctm.Fraction + TrustingPeriod time.Duration + UnbondingPeriod time.Duration + MaxClockDrift time.Duration } func NewTendermintConfig() *TendermintConfig { return &TendermintConfig{ - TrustLevel: DefaultTrustLevel, - TrustingPeriod: TrustingPeriod, - UnbondingPeriod: UnbondingPeriod, - MaxClockDrift: MaxClockDrift, - AllowUpdateAfterExpiry: false, - AllowUpdateAfterMisbehaviour: false, + TrustLevel: DefaultTrustLevel, + TrustingPeriod: TrustingPeriod, + UnbondingPeriod: UnbondingPeriod, + MaxClockDrift: MaxClockDrift, } } diff --git a/testing/coordinator.go b/testing/coordinator.go index f2cc49f9a91..6374a17b170 100644 --- a/testing/coordinator.go +++ b/testing/coordinator.go @@ -11,7 +11,9 @@ import ( ) var ( - ChainIDPrefix = "testchain" + ChainIDPrefix = "testchain" + // to disable revision format, set ChainIDSuffix to "" + ChainIDSuffix = "-1" globalStartTime = time.Date(2020, 1, 2, 0, 0, 0, 0, time.UTC) TimeIncrement = time.Second * 5 ) @@ -173,7 +175,7 @@ func (coord *Coordinator) GetChain(chainID string) *TestChain { // GetChainID returns the chainID used for the provided index. func GetChainID(index int) string { - return ChainIDPrefix + strconv.Itoa(index) + return ChainIDPrefix + strconv.Itoa(index) + ChainIDSuffix } // CommitBlock commits a block on the provided indexes and then increments the global time. diff --git a/testing/endpoint.go b/testing/endpoint.go index ce8895ebb51..b30c1534fc7 100644 --- a/testing/endpoint.go +++ b/testing/endpoint.go @@ -2,17 +2,18 @@ package ibctesting import ( "fmt" + "strings" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - connectiontypes "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types" - channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" - commitmenttypes "github.com/cosmos/ibc-go/v4/modules/core/23-commitment/types" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" - "github.com/cosmos/ibc-go/v4/modules/core/exported" - ibctmtypes "github.com/cosmos/ibc-go/v4/modules/light-clients/07-tendermint/types" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + connectiontypes "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + commitmenttypes "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + "github.com/cosmos/ibc-go/v7/modules/core/exported" + ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" ) // Endpoint is a which represents a channel endpoint and its associated @@ -88,17 +89,16 @@ func (endpoint *Endpoint) CreateClient() (err error) { switch endpoint.ClientConfig.GetClientType() { case exported.Tendermint: tmConfig, ok := endpoint.ClientConfig.(*TendermintConfig) - require.True(endpoint.Chain.T, ok) + require.True(endpoint.Chain.TB, ok) height := endpoint.Counterparty.Chain.LastHeader.GetHeight().(clienttypes.Height) - clientState = ibctmtypes.NewClientState( + clientState = ibctm.NewClientState( endpoint.Counterparty.Chain.ChainID, tmConfig.TrustLevel, tmConfig.TrustingPeriod, tmConfig.UnbondingPeriod, tmConfig.MaxClockDrift, - height, commitmenttypes.GetSDKSpecs(), UpgradePath, tmConfig.AllowUpdateAfterExpiry, tmConfig.AllowUpdateAfterMisbehaviour, - ) + height, commitmenttypes.GetSDKSpecs(), UpgradePath) consensusState = endpoint.Counterparty.Chain.LastHeader.ConsensusState() case exported.Solomachine: // TODO - // solo := NewSolomachine(endpoint.Chain.T, endpoint.Chain.Codec, clientID, "", 1) + // solo := NewSolomachine(endpoint.Chain.TB, endpoint.Chain.Codec, clientID, "", 1) // clientState = solo.ClientState() // consensusState = solo.ConsensusState() @@ -113,7 +113,7 @@ func (endpoint *Endpoint) CreateClient() (err error) { msg, err := clienttypes.NewMsgCreateClient( clientState, consensusState, endpoint.Chain.SenderAccount.GetAddress().String(), ) - require.NoError(endpoint.Chain.T, err) + require.NoError(endpoint.Chain.TB, err) res, err := endpoint.Chain.SendMsgs(msg) if err != nil { @@ -121,7 +121,7 @@ func (endpoint *Endpoint) CreateClient() (err error) { } endpoint.ClientID, err = ParseClientIDFromEvents(res.GetEvents()) - require.NoError(endpoint.Chain.T, err) + require.NoError(endpoint.Chain.TB, err) return nil } @@ -131,7 +131,7 @@ func (endpoint *Endpoint) UpdateClient() (err error) { // ensure counterparty has committed state endpoint.Chain.Coordinator.CommitBlock(endpoint.Counterparty.Chain) - var header exported.Header + var header exported.ClientMessage switch endpoint.ClientConfig.GetClientType() { case exported.Tendermint: @@ -149,11 +149,64 @@ func (endpoint *Endpoint) UpdateClient() (err error) { endpoint.ClientID, header, endpoint.Chain.SenderAccount.GetAddress().String(), ) - require.NoError(endpoint.Chain.T, err) + require.NoError(endpoint.Chain.TB, err) return endpoint.Chain.sendMsgs(msg) } +// UpgradeChain will upgrade a chain's chainID to the next revision number. +// It will also update the counterparty client. +// TODO: implement actual upgrade chain functionality via scheduling an upgrade +// and upgrading the client via MsgUpgradeClient +// see reference https://github.com/cosmos/ibc-go/pull/1169 +func (endpoint *Endpoint) UpgradeChain() error { + if strings.TrimSpace(endpoint.Counterparty.ClientID) == "" { + return fmt.Errorf("cannot upgrade chain if there is no counterparty client") + } + + clientState := endpoint.Counterparty.GetClientState().(*ibctm.ClientState) + + // increment revision number in chainID + + oldChainID := clientState.ChainId + if !clienttypes.IsRevisionFormat(oldChainID) { + return fmt.Errorf("cannot upgrade chain which is not of revision format: %s", oldChainID) + } + + revisionNumber := clienttypes.ParseChainID(oldChainID) + newChainID, err := clienttypes.SetRevisionNumber(oldChainID, revisionNumber+1) + if err != nil { + return err + } + + // update chain + endpoint.Chain.ChainID = newChainID + endpoint.Chain.CurrentHeader.ChainID = newChainID + endpoint.Chain.NextBlock() // commit changes + + // update counterparty client manually + clientState.ChainId = newChainID + clientState.LatestHeight = clienttypes.NewHeight(revisionNumber+1, clientState.LatestHeight.GetRevisionHeight()+1) + endpoint.Counterparty.SetClientState(clientState) + + consensusState := &ibctm.ConsensusState{ + Timestamp: endpoint.Chain.LastHeader.GetTime(), + Root: commitmenttypes.NewMerkleRoot(endpoint.Chain.LastHeader.Header.GetAppHash()), + NextValidatorsHash: endpoint.Chain.LastHeader.Header.NextValidatorsHash, + } + endpoint.Counterparty.SetConsensusState(consensusState, clientState.GetLatestHeight()) + + // ensure the next update isn't identical to the one set in state + endpoint.Chain.Coordinator.IncrementTime() + endpoint.Chain.NextBlock() + + if err = endpoint.Counterparty.UpdateClient(); err != nil { + return err + } + + return nil +} + // ConnOpenInit will construct and execute a MsgConnectionOpenInit on the associated endpoint. func (endpoint *Endpoint) ConnOpenInit() error { msg := connectiontypes.NewMsgConnectionOpenInit( @@ -168,7 +221,7 @@ func (endpoint *Endpoint) ConnOpenInit() error { } endpoint.ConnectionID, err = ParseConnectionIDFromEvents(res.GetEvents()) - require.NoError(endpoint.Chain.T, err) + require.NoError(endpoint.Chain.TB, err) return nil } @@ -176,13 +229,12 @@ func (endpoint *Endpoint) ConnOpenInit() error { // ConnOpenTry will construct and execute a MsgConnectionOpenTry on the associated endpoint. func (endpoint *Endpoint) ConnOpenTry() error { err := endpoint.UpdateClient() - require.NoError(endpoint.Chain.T, err) + require.NoError(endpoint.Chain.TB, err) 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, @@ -195,7 +247,7 @@ func (endpoint *Endpoint) ConnOpenTry() error { if endpoint.ConnectionID == "" { endpoint.ConnectionID, err = ParseConnectionIDFromEvents(res.GetEvents()) - require.NoError(endpoint.Chain.T, err) + require.NoError(endpoint.Chain.TB, err) } return nil @@ -204,7 +256,7 @@ func (endpoint *Endpoint) ConnOpenTry() error { // ConnOpenAck will construct and execute a MsgConnectionOpenAck on the associated endpoint. func (endpoint *Endpoint) ConnOpenAck() error { err := endpoint.UpdateClient() - require.NoError(endpoint.Chain.T, err) + require.NoError(endpoint.Chain.TB, err) counterpartyClient, proofClient, proofConsensus, consensusHeight, proofTry, proofHeight := endpoint.QueryConnectionHandshakeProof() @@ -221,7 +273,7 @@ func (endpoint *Endpoint) ConnOpenAck() error { // ConnOpenConfirm will construct and execute a MsgConnectionOpenConfirm on the associated endpoint. func (endpoint *Endpoint) ConnOpenConfirm() error { err := endpoint.UpdateClient() - require.NoError(endpoint.Chain.T, err) + require.NoError(endpoint.Chain.TB, err) connectionKey := host.ConnectionKey(endpoint.Counterparty.ConnectionID) proof, height := endpoint.Counterparty.Chain.QueryProof(connectionKey) @@ -277,7 +329,7 @@ func (endpoint *Endpoint) ChanOpenInit() error { } endpoint.ChannelID, err = ParseChannelIDFromEvents(res.GetEvents()) - require.NoError(endpoint.Chain.T, err) + require.NoError(endpoint.Chain.TB, err) // update version to selected app version // NOTE: this update must be performed after SendMsgs() @@ -289,13 +341,13 @@ func (endpoint *Endpoint) ChanOpenInit() error { // ChanOpenTry will construct and execute a MsgChannelOpenTry on the associated endpoint. func (endpoint *Endpoint) ChanOpenTry() error { err := endpoint.UpdateClient() - require.NoError(endpoint.Chain.T, err) + require.NoError(endpoint.Chain.TB, err) channelKey := host.ChannelKey(endpoint.Counterparty.ChannelConfig.PortID, endpoint.Counterparty.ChannelID) 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, @@ -308,7 +360,7 @@ func (endpoint *Endpoint) ChanOpenTry() error { if endpoint.ChannelID == "" { endpoint.ChannelID, err = ParseChannelIDFromEvents(res.GetEvents()) - require.NoError(endpoint.Chain.T, err) + require.NoError(endpoint.Chain.TB, err) } // update version to selected app version @@ -321,7 +373,7 @@ func (endpoint *Endpoint) ChanOpenTry() error { // ChanOpenAck will construct and execute a MsgChannelOpenAck on the associated endpoint. func (endpoint *Endpoint) ChanOpenAck() error { err := endpoint.UpdateClient() - require.NoError(endpoint.Chain.T, err) + require.NoError(endpoint.Chain.TB, err) channelKey := host.ChannelKey(endpoint.Counterparty.ChannelConfig.PortID, endpoint.Counterparty.ChannelID) proof, height := endpoint.Counterparty.Chain.QueryProof(channelKey) @@ -345,7 +397,7 @@ func (endpoint *Endpoint) ChanOpenAck() error { // ChanOpenConfirm will construct and execute a MsgChannelOpenConfirm on the associated endpoint. func (endpoint *Endpoint) ChanOpenConfirm() error { err := endpoint.UpdateClient() - require.NoError(endpoint.Chain.T, err) + require.NoError(endpoint.Chain.TB, err) channelKey := host.ChannelKey(endpoint.Counterparty.ChannelConfig.PortID, endpoint.Counterparty.ChannelID) proof, height := endpoint.Counterparty.Chain.QueryProof(channelKey) @@ -371,19 +423,30 @@ func (endpoint *Endpoint) ChanCloseInit() error { // SendPacket sends a packet through the channel keeper using the associated endpoint // The counterparty client is updated so proofs can be sent to the counterparty chain. -func (endpoint *Endpoint) SendPacket(packet exported.PacketI) error { - channelCap := endpoint.Chain.GetChannelCapability(packet.GetSourcePort(), packet.GetSourceChannel()) +// The packet sequence generated for the packet to be sent is returned. An error +// is returned if one occurs. +func (endpoint *Endpoint) SendPacket( + timeoutHeight clienttypes.Height, + timeoutTimestamp uint64, + data []byte, +) (uint64, error) { + channelCap := endpoint.Chain.GetChannelCapability(endpoint.ChannelConfig.PortID, endpoint.ChannelID) // no need to send message, acting as a module - err := endpoint.Chain.App.GetIBCKeeper().ChannelKeeper.SendPacket(endpoint.Chain.GetContext(), channelCap, packet) + sequence, err := endpoint.Chain.App.GetIBCKeeper().ChannelKeeper.SendPacket(endpoint.Chain.GetContext(), channelCap, endpoint.ChannelConfig.PortID, endpoint.ChannelID, timeoutHeight, timeoutTimestamp, data) if err != nil { - return err + return 0, err } // commit changes since no message was sent endpoint.Chain.Coordinator.CommitBlock(endpoint.Chain) - return endpoint.Counterparty.UpdateClient() + err = endpoint.Counterparty.UpdateClient() + if err != nil { + return 0, err + } + + return sequence, nil } // RecvPacket receives a packet on the associated endpoint. @@ -463,7 +526,7 @@ func (endpoint *Endpoint) TimeoutPacket(packet channeltypes.Packet) error { proof, proofHeight := endpoint.Counterparty.QueryProof(packetKey) nextSeqRecv, found := endpoint.Counterparty.Chain.App.GetIBCKeeper().ChannelKeeper.GetNextSequenceRecv(endpoint.Counterparty.Chain.GetContext(), endpoint.ChannelConfig.PortID, endpoint.ChannelID) - require.True(endpoint.Chain.T, found) + require.True(endpoint.Chain.TB, found) timeoutMsg := channeltypes.NewMsgTimeout( packet, nextSeqRecv, @@ -493,7 +556,7 @@ func (endpoint *Endpoint) TimeoutOnClose(packet channeltypes.Packet) error { proofClosed, _ := endpoint.Counterparty.QueryProof(channelKey) nextSeqRecv, found := endpoint.Counterparty.Chain.App.GetIBCKeeper().ChannelKeeper.GetNextSequenceRecv(endpoint.Counterparty.Chain.GetContext(), endpoint.ChannelConfig.PortID, endpoint.ChannelID) - require.True(endpoint.Chain.T, found) + require.True(endpoint.Chain.TB, found) timeoutOnCloseMsg := channeltypes.NewMsgTimeoutOnClose( packet, nextSeqRecv, @@ -503,11 +566,11 @@ func (endpoint *Endpoint) TimeoutOnClose(packet channeltypes.Packet) error { return endpoint.Chain.sendMsgs(timeoutOnCloseMsg) } -// SetChannelClosed sets a channel state to CLOSED. -func (endpoint *Endpoint) SetChannelClosed() error { +// SetChannelState sets a channel state +func (endpoint *Endpoint) SetChannelState(state channeltypes.State) error { channel := endpoint.GetChannel() - channel.State = channeltypes.CLOSED + channel.State = state endpoint.Chain.App.GetIBCKeeper().ChannelKeeper.SetChannel(endpoint.Chain.GetContext(), endpoint.ChannelConfig.PortID, endpoint.ChannelID, channel) endpoint.Chain.Coordinator.CommitBlock(endpoint.Chain) @@ -530,7 +593,7 @@ func (endpoint *Endpoint) SetClientState(clientState exported.ClientState) { // The consensus state is expected to exist otherwise testing will fail. func (endpoint *Endpoint) GetConsensusState(height exported.Height) exported.ConsensusState { consensusState, found := endpoint.Chain.GetConsensusState(endpoint.ClientID, height) - require.True(endpoint.Chain.T, found) + require.True(endpoint.Chain.TB, found) return consensusState } @@ -544,7 +607,7 @@ func (endpoint *Endpoint) SetConsensusState(consensusState exported.ConsensusSta // connection is expected to exist otherwise testing will fail. func (endpoint *Endpoint) GetConnection() connectiontypes.ConnectionEnd { connection, found := endpoint.Chain.App.GetIBCKeeper().ConnectionKeeper.GetConnection(endpoint.Chain.GetContext(), endpoint.ConnectionID) - require.True(endpoint.Chain.T, found) + require.True(endpoint.Chain.TB, found) return connection } @@ -558,7 +621,7 @@ func (endpoint *Endpoint) SetConnection(connection connectiontypes.ConnectionEnd // is expected to exist otherwise testing will fail. func (endpoint *Endpoint) GetChannel() channeltypes.Channel { channel, found := endpoint.Chain.App.GetIBCKeeper().ChannelKeeper.GetChannel(endpoint.Chain.GetContext(), endpoint.ChannelConfig.PortID, endpoint.ChannelID) - require.True(endpoint.Chain.T, found) + require.True(endpoint.Chain.TB, found) return channel } diff --git a/testing/events.go b/testing/events.go index 18169d30818..ac1fdd71954 100644 --- a/testing/events.go +++ b/testing/events.go @@ -5,20 +5,23 @@ import ( "strconv" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/suite" - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - connectiontypes "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types" - channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + connectiontypes "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" ) +type EventsMap map[string]map[string]string + // ParseClientIDFromEvents parses events emitted from a MsgCreateClient and returns the // client identifier. func ParseClientIDFromEvents(events sdk.Events) (string, error) { for _, ev := range events { if ev.Type == clienttypes.EventTypeCreateClient { for _, attr := range ev.Attributes { - if string(attr.Key) == clienttypes.AttributeKeyClientID { - return string(attr.Value), nil + if attr.Key == clienttypes.AttributeKeyClientID { + return attr.Value, nil } } } @@ -33,8 +36,8 @@ func ParseConnectionIDFromEvents(events sdk.Events) (string, error) { if ev.Type == connectiontypes.EventTypeConnectionOpenInit || ev.Type == connectiontypes.EventTypeConnectionOpenTry { for _, attr := range ev.Attributes { - if string(attr.Key) == connectiontypes.AttributeKeyConnectionID { - return string(attr.Value), nil + if attr.Key == connectiontypes.AttributeKeyConnectionID { + return attr.Value, nil } } } @@ -48,8 +51,8 @@ func ParseChannelIDFromEvents(events sdk.Events) (string, error) { for _, ev := range events { if ev.Type == channeltypes.EventTypeChannelOpenInit || ev.Type == channeltypes.EventTypeChannelOpenTry { for _, attr := range ev.Attributes { - if string(attr.Key) == channeltypes.AttributeKeyChannelID { - return string(attr.Value), nil + if attr.Key == channeltypes.AttributeKeyChannelID { + return attr.Value, nil } } } @@ -64,12 +67,12 @@ func ParsePacketFromEvents(events sdk.Events) (channeltypes.Packet, error) { if ev.Type == channeltypes.EventTypeSendPacket { packet := channeltypes.Packet{} for _, attr := range ev.Attributes { - switch string(attr.Key) { - case channeltypes.AttributeKeyData: - packet.Data = attr.Value + switch attr.Key { + case channeltypes.AttributeKeyData: //nolint:staticcheck // DEPRECATED + packet.Data = []byte(attr.Value) case channeltypes.AttributeKeySequence: - seq, err := strconv.ParseUint(string(attr.Value), 10, 64) + seq, err := strconv.ParseUint(attr.Value, 10, 64) if err != nil { return channeltypes.Packet{}, err } @@ -77,19 +80,19 @@ func ParsePacketFromEvents(events sdk.Events) (channeltypes.Packet, error) { packet.Sequence = seq case channeltypes.AttributeKeySrcPort: - packet.SourcePort = string(attr.Value) + packet.SourcePort = attr.Value case channeltypes.AttributeKeySrcChannel: - packet.SourceChannel = string(attr.Value) + packet.SourceChannel = attr.Value case channeltypes.AttributeKeyDstPort: - packet.DestinationPort = string(attr.Value) + packet.DestinationPort = attr.Value case channeltypes.AttributeKeyDstChannel: - packet.DestinationChannel = string(attr.Value) + packet.DestinationChannel = attr.Value case channeltypes.AttributeKeyTimeoutHeight: - height, err := clienttypes.ParseHeight(string(attr.Value)) + height, err := clienttypes.ParseHeight(attr.Value) if err != nil { return channeltypes.Packet{}, err } @@ -97,7 +100,7 @@ func ParsePacketFromEvents(events sdk.Events) (channeltypes.Packet, error) { packet.TimeoutHeight = height case channeltypes.AttributeKeyTimeoutTimestamp: - timestamp, err := strconv.ParseUint(string(attr.Value), 10, 64) + timestamp, err := strconv.ParseUint(attr.Value, 10, 64) if err != nil { return channeltypes.Packet{}, err } @@ -121,11 +124,41 @@ func ParseAckFromEvents(events sdk.Events) ([]byte, error) { for _, ev := range events { if ev.Type == channeltypes.EventTypeWriteAck { for _, attr := range ev.Attributes { - if string(attr.Key) == channeltypes.AttributeKeyAck { - return attr.Value, nil + if attr.Key == channeltypes.AttributeKeyAck { //nolint:staticcheck // DEPRECATED + return []byte(attr.Value), nil } } } } return nil, fmt.Errorf("acknowledgement event attribute not found") } + +// AssertEvents asserts that expected events are present in the actual events. +// Expected map needs to be a subset of actual events to pass. +func AssertEvents( + suite *suite.Suite, + expected EventsMap, + actual sdk.Events, +) { + hasEvents := make(map[string]bool) + for eventType := range expected { + hasEvents[eventType] = false + } + + for _, event := range actual { + expEvent, eventFound := expected[event.Type] + if eventFound { + hasEvents[event.Type] = true + suite.Require().Len(event.Attributes, len(expEvent)) + for _, attr := range event.Attributes { + expValue, found := expEvent[attr.Key] + suite.Require().True(found) + suite.Require().Equal(expValue, attr.Value) + } + } + } + + for eventName, hasEvent := range hasEvents { + suite.Require().True(hasEvent, "event: %s was not found in events", eventName) + } +} diff --git a/testing/mock/README.md b/testing/mock/README.md index 5da403f9c34..71c170a357f 100644 --- a/testing/mock/README.md +++ b/testing/mock/README.md @@ -1,4 +1,4 @@ -This package is only intended to be used for testing core IBC. In order to maintain secure +This package is only intended to be used for testing core IBC. In order to maintain secure testing, we need to do message passing and execution which requires connecting an IBC application module that fulfills all the callbacks. We cannot connect to ibc-transfer which does not support all channel types so instead we create a mock application module which does nothing. It simply diff --git a/testing/mock/ack.go b/testing/mock/ack.go index c25176a02da..280e848b3be 100644 --- a/testing/mock/ack.go +++ b/testing/mock/ack.go @@ -1,23 +1,23 @@ package mock -// MockEmptyAcknowledgement implements the exported.Acknowledgement interface and always returns an empty byte string as Response -type MockEmptyAcknowledgement struct { +// EmptyAcknowledgement implements the exported.Acknowledgement interface and always returns an empty byte string as Response +type EmptyAcknowledgement struct { Response []byte } -// NewMockEmptyAcknowledgement returns a new instance of MockEmptyAcknowledgement -func NewMockEmptyAcknowledgement() MockEmptyAcknowledgement { - return MockEmptyAcknowledgement{ +// NewEmptyAcknowledgement returns a new instance of EmptyAcknowledgement +func NewEmptyAcknowledgement() EmptyAcknowledgement { + return EmptyAcknowledgement{ Response: []byte{}, } } // Success implements the Acknowledgement interface -func (ack MockEmptyAcknowledgement) Success() bool { +func (ack EmptyAcknowledgement) Success() bool { return true } // Acknowledgement implements the Acknowledgement interface -func (ack MockEmptyAcknowledgement) Acknowledgement() []byte { +func (ack EmptyAcknowledgement) Acknowledgement() []byte { return []byte{} } diff --git a/testing/mock/ibc_app.go b/testing/mock/ibc_app.go index 47f39deb5ce..d3bac8889a2 100644 --- a/testing/mock/ibc_app.go +++ b/testing/mock/ibc_app.go @@ -5,12 +5,12 @@ import ( capabilitykeeper "github.com/cosmos/cosmos-sdk/x/capability/keeper" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" - "github.com/cosmos/ibc-go/v4/modules/core/exported" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + "github.com/cosmos/ibc-go/v7/modules/core/exported" ) -// MockIBCApp contains IBC application module callbacks as defined in 05-port. -type MockIBCApp struct { +// IBCApp contains IBC application module callbacks as defined in 05-port. +type IBCApp struct { PortID string ScopedKeeper capabilitykeeper.ScopedKeeper @@ -87,9 +87,9 @@ type MockIBCApp struct { ) error } -// NewMockIBCApp returns a MockIBCApp. An empty PortID indicates the mock app doesn't bind/claim ports. -func NewMockIBCApp(portID string, scopedKeeper capabilitykeeper.ScopedKeeper) *MockIBCApp { - return &MockIBCApp{ +// NewIBCApp returns a IBCApp. An empty PortID indicates the mock app doesn't bind/claim ports. +func NewIBCApp(portID string, scopedKeeper capabilitykeeper.ScopedKeeper) *IBCApp { + return &IBCApp{ PortID: portID, ScopedKeeper: scopedKeeper, } diff --git a/testing/mock/ibc_module.go b/testing/mock/ibc_module.go index 12d35564005..b1afd1087c1 100644 --- a/testing/mock/ibc_module.go +++ b/testing/mock/ibc_module.go @@ -9,19 +9,19 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" - "github.com/cosmos/ibc-go/v4/modules/core/exported" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + "github.com/cosmos/ibc-go/v7/modules/core/exported" ) // IBCModule implements the ICS26 callbacks for testing/mock. type IBCModule struct { appModule *AppModule - IBCApp *MockIBCApp // base application of an IBC middleware stack + IBCApp *IBCApp // base application of an IBC middleware stack } // NewIBCModule creates a new IBCModule given the underlying mock IBC application and scopedKeeper. -func NewIBCModule(appModule *AppModule, app *MockIBCApp) IBCModule { +func NewIBCModule(appModule *AppModule, app *IBCApp) IBCModule { appModule.ibcApps = append(appModule.ibcApps, app) return IBCModule{ appModule: appModule, @@ -42,9 +42,11 @@ func (im IBCModule) OnChanOpenInit( return im.IBCApp.OnChanOpenInit(ctx, order, connectionHops, portID, channelID, chanCap, counterparty, version) } - // Claim channel capability passed back by IBC module - if err := im.IBCApp.ScopedKeeper.ClaimCapability(ctx, chanCap, host.ChannelCapabilityPath(portID, channelID)); err != nil { - return "", err + if chanCap != nil { + // Claim channel capability passed back by IBC module + if err := im.IBCApp.ScopedKeeper.ClaimCapability(ctx, chanCap, host.ChannelCapabilityPath(portID, channelID)); err != nil { + return "", err + } } return version, nil @@ -59,9 +61,11 @@ func (im IBCModule) OnChanOpenTry( return im.IBCApp.OnChanOpenTry(ctx, order, connectionHops, portID, channelID, chanCap, counterparty, counterpartyVersion) } - // Claim channel capability passed back by IBC module - if err := im.IBCApp.ScopedKeeper.ClaimCapability(ctx, chanCap, host.ChannelCapabilityPath(portID, channelID)); err != nil { - return "", err + if chanCap != nil { + // Claim channel capability passed back by IBC module + if err := im.IBCApp.ScopedKeeper.ClaimCapability(ctx, chanCap, host.ChannelCapabilityPath(portID, channelID)); err != nil { + return "", err + } } return Version, nil diff --git a/testing/mock/ibc_module_test.go b/testing/mock/ibc_module_test.go index e790f6e39e7..6eec0c11db9 100644 --- a/testing/mock/ibc_module_test.go +++ b/testing/mock/ibc_module_test.go @@ -5,9 +5,9 @@ import ( "github.com/stretchr/testify/require" - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" - "github.com/cosmos/ibc-go/v4/testing/mock" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + "github.com/cosmos/ibc-go/v7/testing/mock" ) func TestCreateCapabilityName(t *testing.T) { diff --git a/testing/mock/mock.go b/testing/mock/mock.go index 3ee9e1dec89..2c4b536fa26 100644 --- a/testing/mock/mock.go +++ b/testing/mock/mock.go @@ -10,14 +10,14 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - "github.com/gorilla/mux" "github.com/grpc-ecosystem/grpc-gateway/runtime" "github.com/spf13/cobra" abci "github.com/tendermint/tendermint/abci/types" - channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" - porttypes "github.com/cosmos/ibc-go/v4/modules/core/05-port/types" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + porttypes "github.com/cosmos/ibc-go/v7/modules/core/05-port/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + "github.com/cosmos/ibc-go/v7/modules/core/exported" ) const ( @@ -71,9 +71,6 @@ func (AppModuleBasic) ValidateGenesis(codec.JSONCodec, client.TxEncodingConfig, return nil } -// RegisterRESTRoutes implements AppModuleBasic interface. -func (AppModuleBasic) RegisterRESTRoutes(clientCtx client.Context, rtr *mux.Router) {} - // RegisterGRPCGatewayRoutes implements AppModuleBasic interface. func (a AppModuleBasic) RegisterGRPCGatewayRoutes(_ client.Context, _ *runtime.ServeMux) {} @@ -90,7 +87,7 @@ func (AppModuleBasic) GetQueryCmd() *cobra.Command { // AppModule represents the AppModule for the mock module. type AppModule struct { AppModuleBasic - ibcApps []*MockIBCApp + ibcApps []*IBCApp portKeeper PortKeeper } @@ -104,21 +101,6 @@ func NewAppModule(pk PortKeeper) AppModule { // RegisterInvariants implements the AppModule interface. func (AppModule) RegisterInvariants(ir sdk.InvariantRegistry) {} -// Route implements the AppModule interface. -func (am AppModule) Route() sdk.Route { - return sdk.NewRoute(ModuleName, nil) -} - -// QuerierRoute implements the AppModule interface. -func (AppModule) QuerierRoute() string { - return "" -} - -// LegacyQuerierHandler implements the AppModule interface. -func (am AppModule) LegacyQuerierHandler(*codec.LegacyAmino) sdk.Querier { - return nil -} - // RegisterServices implements the AppModule interface. func (am AppModule) RegisterServices(module.Configurator) {} @@ -128,7 +110,10 @@ func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, data json. if ibcApp.PortID != "" && !am.portKeeper.IsBound(ctx, ibcApp.PortID) { // bind mock portID cap := am.portKeeper.BindPort(ctx, ibcApp.PortID) - ibcApp.ScopedKeeper.ClaimCapability(ctx, cap, host.PortPath(ibcApp.PortID)) + err := ibcApp.ScopedKeeper.ClaimCapability(ctx, cap, host.PortPath(ibcApp.PortID)) + if err != nil { + panic(err) + } } } @@ -151,3 +136,18 @@ func (am AppModule) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) { func (am AppModule) EndBlock(ctx sdk.Context, req abci.RequestEndBlock) []abci.ValidatorUpdate { return []abci.ValidatorUpdate{} } + +var _ exported.Path = KeyPath{} + +// KeyPath defines a placeholder struct which implements the exported.Path interface +type KeyPath struct{} + +// String implements the exported.Path interface +func (KeyPath) String() string { + return "" +} + +// Empty implements the exported.Path interface +func (KeyPath) Empty() bool { + return false +} diff --git a/testing/mock/privval_test.go b/testing/mock/privval_test.go index 8fbabd90729..7b40f7b7130 100644 --- a/testing/mock/privval_test.go +++ b/testing/mock/privval_test.go @@ -7,7 +7,7 @@ import ( tmproto "github.com/tendermint/tendermint/proto/tendermint/types" tmtypes "github.com/tendermint/tendermint/types" - "github.com/cosmos/ibc-go/v4/testing/mock" + "github.com/cosmos/ibc-go/v7/testing/mock" ) const chainID = "testChain" @@ -24,7 +24,8 @@ func TestSignVote(t *testing.T) { pk, _ := pv.GetPubKey() vote := &tmproto.Vote{Height: 2} - pv.SignVote(chainID, vote) + err := pv.SignVote(chainID, vote) + require.NoError(t, err) msg := tmtypes.VoteSignBytes(chainID, vote) ok := pk.VerifySignature(msg, vote.Signature) @@ -36,7 +37,8 @@ func TestSignProposal(t *testing.T) { pk, _ := pv.GetPubKey() proposal := &tmproto.Proposal{Round: 2} - pv.SignProposal(chainID, proposal) + err := pv.SignProposal(chainID, proposal) + require.NoError(t, err) msg := tmtypes.ProposalSignBytes(chainID, proposal) ok := pk.VerifySignature(msg, proposal.Signature) diff --git a/testing/path.go b/testing/path.go index bfec2c803f2..d3b84a96ca7 100644 --- a/testing/path.go +++ b/testing/path.go @@ -4,7 +4,7 @@ import ( "bytes" "fmt" - channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" ) // Path contains two endpoints representing two chains connected over IBC diff --git a/testing/sdk_test.go b/testing/sdk_test.go deleted file mode 100644 index a56dae3b73e..00000000000 --- a/testing/sdk_test.go +++ /dev/null @@ -1,279 +0,0 @@ -package ibctesting_test - -import ( - "fmt" - "testing" - "time" - - "github.com/cosmos/cosmos-sdk/baseapp" - "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/crypto/hd" - "github.com/cosmos/cosmos-sdk/crypto/keyring" - kmultisig "github.com/cosmos/cosmos-sdk/crypto/keys/multisig" - cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" - servertypes "github.com/cosmos/cosmos-sdk/server/types" - storetypes "github.com/cosmos/cosmos-sdk/store/types" - "github.com/cosmos/cosmos-sdk/testutil" - clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" - "github.com/cosmos/cosmos-sdk/testutil/network" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/types/rest" - txtypes "github.com/cosmos/cosmos-sdk/types/tx" - authcli "github.com/cosmos/cosmos-sdk/x/auth/client/cli" - authrest "github.com/cosmos/cosmos-sdk/x/auth/client/rest" - authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - "github.com/spf13/cobra" - "github.com/stretchr/testify/suite" - tmrand "github.com/tendermint/tendermint/libs/rand" - dbm "github.com/tendermint/tm-db" - - ibcclientcli "github.com/cosmos/ibc-go/v4/modules/core/02-client/client/cli" - "github.com/cosmos/ibc-go/v4/testing/simapp" - "github.com/cosmos/ibc-go/v4/testing/simapp/params" -) - -/* - This file contains tests from the SDK which had to deleted during the migration of - the IBC module from the SDK into this repository. https://github.com/cosmos/cosmos-sdk/pull/8735 - - They can be removed once the SDK deprecates amino. -*/ - -type IntegrationTestSuite struct { - suite.Suite - - cfg network.Config - network *network.Network -} - -func (s *IntegrationTestSuite) SetupSuite() { - s.T().Log("setting up integration test suite") - - cfg := DefaultConfig() - - cfg.NumValidators = 2 - - s.cfg = cfg - s.network = network.New(s.T(), cfg) - - kb := s.network.Validators[0].ClientCtx.Keyring - _, _, err := kb.NewMnemonic("newAccount", keyring.English, sdk.FullFundraiserPath, keyring.DefaultBIP39Passphrase, hd.Secp256k1) - s.Require().NoError(err) - - account1, _, err := kb.NewMnemonic("newAccount1", keyring.English, sdk.FullFundraiserPath, keyring.DefaultBIP39Passphrase, hd.Secp256k1) - s.Require().NoError(err) - - account2, _, err := kb.NewMnemonic("newAccount2", keyring.English, sdk.FullFundraiserPath, keyring.DefaultBIP39Passphrase, hd.Secp256k1) - s.Require().NoError(err) - - multi := kmultisig.NewLegacyAminoPubKey(2, []cryptotypes.PubKey{account1.GetPubKey(), account2.GetPubKey()}) - _, err = kb.SaveMultisig("multi", multi) - s.Require().NoError(err) - - _, err = s.network.WaitForHeight(1) - s.Require().NoError(err) - - s.Require().NoError(s.network.WaitForNextBlock()) -} - -func TestIntegrationTestSuite(t *testing.T) { - suite.Run(t, new(IntegrationTestSuite)) -} - -// NewAppConstructor returns a new simapp AppConstructor -func NewAppConstructor(encodingCfg params.EncodingConfig) network.AppConstructor { - return func(val network.Validator) servertypes.Application { - return simapp.NewSimApp( - val.Ctx.Logger, dbm.NewMemDB(), nil, true, make(map[int64]bool), val.Ctx.Config.RootDir, 0, - encodingCfg, - simapp.EmptyAppOptions{}, - baseapp.SetPruning(storetypes.NewPruningOptionsFromString(val.AppConfig.Pruning)), - baseapp.SetMinGasPrices(val.AppConfig.MinGasPrices), - ) - } -} - -// DefaultConfig returns a sane default configuration suitable for nearly all -// testing requirements. -func DefaultConfig() network.Config { - encCfg := simapp.MakeTestEncodingConfig() - - return network.Config{ - Codec: encCfg.Marshaler, - TxConfig: encCfg.TxConfig, - LegacyAmino: encCfg.Amino, - InterfaceRegistry: encCfg.InterfaceRegistry, - AccountRetriever: authtypes.AccountRetriever{}, - AppConstructor: NewAppConstructor(encCfg), - GenesisState: simapp.ModuleBasics.DefaultGenesis(encCfg.Marshaler), - TimeoutCommit: 2 * time.Second, - ChainID: "chain-" + tmrand.NewRand().Str(6), - NumValidators: 4, - BondDenom: sdk.DefaultBondDenom, - MinGasPrices: fmt.Sprintf("0.000006%s", sdk.DefaultBondDenom), - AccountTokens: sdk.TokensFromConsensusPower(1000, sdk.DefaultPowerReduction), - StakingTokens: sdk.TokensFromConsensusPower(500, sdk.DefaultPowerReduction), - BondedTokens: sdk.TokensFromConsensusPower(100, sdk.DefaultPowerReduction), - PruningStrategy: storetypes.PruningOptionNothing, - CleanupDir: true, - SigningAlgo: string(hd.Secp256k1Type), - KeyringOptions: []keyring.Option{}, - } -} - -func (s *IntegrationTestSuite) TearDownSuite() { - s.T().Log("tearing down integration test suite") - s.network.Cleanup() -} - -// TestLegacyRestErrMessages creates two IBC txs, one that fails, one that -// succeeds, and make sure we cannot query any of them (with pretty error msg). -// Our intension is to test the error message of querying a message which is -// signed with proto, since IBC won't support legacy amino at all we are -// considering a message from IBC module. -func (s *IntegrationTestSuite) TestLegacyRestErrMessages() { - val := s.network.Validators[0] - - // Write client state json to temp file, used for an IBC message. - // Generated by printing the result of cdc.MarshalIntefaceJSON on - // a solo machine client state - clientStateJSON := testutil.WriteToNewTempFile( - s.T(), - `{"@type":"/ibc.lightclients.solomachine.v2.ClientState","sequence":"1","is_frozen":false,"consensus_state":{"public_key":{"@type":"/cosmos.crypto.secp256k1.PubKey","key":"AtK50+5pJOoaa04qqAqrnyAqsYrwrR/INnA6UPIaYZlp"},"diversifier":"testing","timestamp":"10"},"allow_update_after_proposal":false}`, - ) - - badClientStateJSON := testutil.WriteToNewTempFile( - s.T(), - `{"@type":"/ibc.lightclients.solomachine.v2.ClientState","sequence":"1","is_frozen":false,"consensus_state":{"public_key":{"@type":"/cosmos.crypto.secp256k1.PubKey","key":"AtK50+5pJOoaa04qqAqrnyAqsYrwrR/INnA6UPIaYZlp"},"diversifier":"DIFFERENT","timestamp":"10"},"allow_update_after_proposal":false}`, - ) - - // Write consensus json to temp file, used for an IBC message. - // Generated by printing the result of cdc.MarshalIntefaceJSON on - // a solo machine consensus state - consensusJSON := testutil.WriteToNewTempFile( - s.T(), - `{"@type":"/ibc.lightclients.solomachine.v2.ConsensusState","public_key":{"@type":"/cosmos.crypto.secp256k1.PubKey","key":"AtK50+5pJOoaa04qqAqrnyAqsYrwrR/INnA6UPIaYZlp"},"diversifier":"testing","timestamp":"10"}`, - ) - - testCases := []struct { - desc string - cmd *cobra.Command - args []string - code uint32 - }{ - { - "Failing IBC message", - ibcclientcli.NewCreateClientCmd(), - []string{ - badClientStateJSON.Name(), // path to client state json - consensusJSON.Name(), // path to consensus json, - fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), - fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), - fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), - fmt.Sprintf("--gas=%d", flags.DefaultGasLimit), - fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), - fmt.Sprintf("--%s=foobar", flags.FlagNote), - }, - uint32(8), - }, - { - "Successful IBC message", - ibcclientcli.NewCreateClientCmd(), - []string{ - clientStateJSON.Name(), // path to client state json - consensusJSON.Name(), // path to consensus json, - fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), - fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), - fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), - fmt.Sprintf("--gas=%d", flags.DefaultGasLimit), - fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), - fmt.Sprintf("--%s=foobar", flags.FlagNote), - }, - uint32(0), - }, - } - - for _, tc := range testCases { - s.Run(fmt.Sprintf("Case %s", tc.desc), func() { - out, err := clitestutil.ExecTestCLICmd(val.ClientCtx, tc.cmd, tc.args) - s.Require().NoError(err) - var txRes sdk.TxResponse - s.Require().NoError(val.ClientCtx.JSONCodec.UnmarshalJSON(out.Bytes(), &txRes)) - s.Require().Equal(tc.code, txRes.Code) - - s.Require().NoError(s.network.WaitForNextBlock()) - - s.testQueryIBCTx(txRes, tc.cmd, tc.args) - }) - } -} - -// testQueryIBCTx is a helper function to test querying txs which: -// - show an error message on legacy REST endpoints -// - succeed using gRPC -// In practice, we call this function on IBC txs. -func (s *IntegrationTestSuite) testQueryIBCTx(txRes sdk.TxResponse, cmd *cobra.Command, args []string) { - val := s.network.Validators[0] - - errMsg := "this transaction cannot be displayed via legacy REST endpoints, because it does not support" + - " Amino serialization. Please either use CLI, gRPC, gRPC-gateway, or directly query the Tendermint RPC" + - " endpoint to query this transaction. The new REST endpoint (via gRPC-gateway) is " - - // Test that legacy endpoint return the above error message on IBC txs. - testCases := []struct { - desc string - url string - }{ - { - "Query by hash", - fmt.Sprintf("%s/txs/%s", val.APIAddress, txRes.TxHash), - }, - { - "Query by height", - fmt.Sprintf("%s/txs?tx.height=%d", val.APIAddress, txRes.Height), - }, - } - - for _, tc := range testCases { - s.Run(fmt.Sprintf("Case %s", tc.desc), func() { - txJSON, err := rest.GetRequest(tc.url) - s.Require().NoError(err) - - var errResp rest.ErrorResponse - s.Require().NoError(val.ClientCtx.LegacyAmino.UnmarshalJSON(txJSON, &errResp)) - - s.Require().Contains(errResp.Error, errMsg) - }) - } - - // try fetching the txn using gRPC req, it will fetch info since it has proto codec. - grpcJSON, err := rest.GetRequest(fmt.Sprintf("%s/cosmos/tx/v1beta1/txs/%s", val.APIAddress, txRes.TxHash)) - s.Require().NoError(err) - - var getTxRes txtypes.GetTxResponse - s.Require().NoError(val.ClientCtx.JSONCodec.UnmarshalJSON(grpcJSON, &getTxRes)) - s.Require().Equal(getTxRes.Tx.Body.Memo, "foobar") - - // generate broadcast only txn. - args = append(args, fmt.Sprintf("--%s=true", flags.FlagGenerateOnly)) - out, err := clitestutil.ExecTestCLICmd(val.ClientCtx, cmd, args) - s.Require().NoError(err) - - txFile := testutil.WriteToNewTempFile(s.T(), string(out.Bytes())) - txFileName := txFile.Name() - - // encode the generated txn. - out, err = clitestutil.ExecTestCLICmd(val.ClientCtx, authcli.GetEncodeCommand(), []string{txFileName}) - s.Require().NoError(err) - - bz, err := val.ClientCtx.LegacyAmino.MarshalJSON(authrest.DecodeReq{Tx: string(out.Bytes())}) - s.Require().NoError(err) - - // try to decode the txn using legacy rest, it fails. - res, err := rest.PostRequest(fmt.Sprintf("%s/txs/decode", val.APIAddress), "application/json", bz) - s.Require().NoError(err) - - var errResp rest.ErrorResponse - s.Require().NoError(val.ClientCtx.LegacyAmino.UnmarshalJSON(res, &errResp)) - s.Require().Contains(errResp.Error, errMsg) -} diff --git a/testing/simapp/README.md b/testing/simapp/README.md index fc449f7f2d0..412ef054eaf 100644 --- a/testing/simapp/README.md +++ b/testing/simapp/README.md @@ -4,48 +4,48 @@ order: false # simapp -simapp is an application built using the Cosmos SDK for testing and educational purposes. +simapp is an application built using the Cosmos SDK for testing and educational purposes. -## Running testnets with `simd` +## Running testnets with `simd` -If you want to spin up a quick testnet with your friends, you can follow these steps. +If you want to spin up a quick testnet with your friends, you can follow these steps. Unless otherwise noted, every step must be done by everyone who wants to participate in this testnet. -1. `$ make build`. This will build the `simd` binary and install it in your Cosmos SDK repo, - inside a new `build` directory. The following instructions are run from inside - that directory. +1. `$ make build`. This will build the `simd` binary and install it in your Cosmos SDK repo, + inside a new `build` directory. The following instructions are run from inside + that directory. 2. If you've run `simd` before, you may need to reset your database before starting a new - testnet: `$ ./simd unsafe-reset-all` -3. `$ ./simd init [moniker]`. This will initialize a new working directory, by default at + testnet: `$ ./simd unsafe-reset-all` +3. `$ ./simd init [moniker]`. This will initialize a new working directory, by default at `~/.simapp`. You need a provide a "moniker," but it doesn't matter what it is. -4. `$ ./simd keys add [key_name]`. This will create a new key, with a name of your choosing. +4. `$ ./simd keys add [key_name]`. This will create a new key, with a name of your choosing. Save the output of this command somewhere; you'll need the address generated here later. 5. `$ ./simd add-genesis-account $(simd keys show [key_name] -a) [amount]`, where `key_name` is the same key name as before; and `amount` is something like `10000000000000000000000000stake`. 6. `$ ./simd gentx [key_name] [amount] --chain-id [chain-id]`. This will create the - genesis transaction for your new chain. -7. Now, one person needs to create the genesis file `genesis.json` using the genesis transactions + genesis transaction for your new chain. +7. Now, one person needs to create the genesis file `genesis.json` using the genesis transactions from every participant, by gathering all the genesis transactions under `config/gentx` and then calling `./simd collect-gentxs`. This will create a new `genesis.json` file that includes data from all the validators (we sometimes call it the "super genesis file" to distinguish it from - single-validator genesis files). -8. Once you've received the super genesis file, overwrite your original `genesis.json` file with - the new super `genesis.json`. + single-validator genesis files). +8. Once you've received the super genesis file, overwrite your original `genesis.json` file with + the new super `genesis.json`. 9. Modify your `config/config.toml` (in the simapp working directory) to include the other participants as persistent peers: - ``` + ```text # Comma separated list of nodes to keep persistent connections to persistent_peers = "[validator address]@[ip address]:[port],[validator address]@[ip address]:[port]" ``` You can find `validator address` by running `./simd tendermint show-node-id`. (It will be hex-encoded.) By default, `port` is 26656. -10. Now you can start your nodes: `$ ./simd start`. +10. Now you can start your nodes: `$ ./simd start`. -Now you have a small testnet that you can use to try out changes to the Cosmos SDK or Tendermint! +Now you have a small testnet that you can use to try out changes to the Cosmos SDK or Tendermint! NOTE: Sometimes creating the network through the `collect-gentxs` will fail, and validators will start in a funny state (and then panic). If this happens, you can try to create and start the network first -with a single validator and then add additional validators using a `create-validator` transaction. \ No newline at end of file +with a single validator and then add additional validators using a `create-validator` transaction. diff --git a/testing/simapp/ante_handler.go b/testing/simapp/ante_handler.go index 5f8a6265399..1fee5a3a529 100644 --- a/testing/simapp/ante_handler.go +++ b/testing/simapp/ante_handler.go @@ -1,12 +1,13 @@ package simapp import ( + errorsmod "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/x/auth/ante" - ibcante "github.com/cosmos/ibc-go/v4/modules/core/ante" - "github.com/cosmos/ibc-go/v4/modules/core/keeper" + ibcerrors "github.com/cosmos/ibc-go/v7/internal/errors" + ibcante "github.com/cosmos/ibc-go/v7/modules/core/ante" + "github.com/cosmos/ibc-go/v7/modules/core/keeper" ) // HandlerOptions extend the SDK's AnteHandler options by requiring the IBC keeper. @@ -19,36 +20,29 @@ type HandlerOptions struct { // NewAnteHandler creates a new ante handler func NewAnteHandler(options HandlerOptions) (sdk.AnteHandler, error) { if options.AccountKeeper == nil { - return nil, sdkerrors.Wrap(sdkerrors.ErrLogic, "account keeper is required for AnteHandler") + return nil, errorsmod.Wrap(ibcerrors.ErrLogic, "account keeper is required for AnteHandler") } if options.BankKeeper == nil { - return nil, sdkerrors.Wrap(sdkerrors.ErrLogic, "bank keeper is required for AnteHandler") + return nil, errorsmod.Wrap(ibcerrors.ErrLogic, "bank keeper is required for AnteHandler") } if options.SignModeHandler == nil { - return nil, sdkerrors.Wrap(sdkerrors.ErrLogic, "sign mode handler is required for ante builder") - } - - sigGasConsumer := options.SigGasConsumer - if sigGasConsumer == nil { - sigGasConsumer = ante.DefaultSigVerificationGasConsumer + return nil, errorsmod.Wrap(ibcerrors.ErrLogic, "sign mode handler is required for AnteHandler") } anteDecorators := []sdk.AnteDecorator{ - ante.NewSetUpContextDecorator(), - ante.NewRejectExtensionOptionsDecorator(), - ante.NewMempoolFeeDecorator(), + ante.NewSetUpContextDecorator(), // outermost AnteDecorator. SetUpContext must be called first + ante.NewExtensionOptionsDecorator(options.ExtensionOptionChecker), ante.NewValidateBasicDecorator(), ante.NewTxTimeoutHeightDecorator(), ante.NewValidateMemoDecorator(options.AccountKeeper), ante.NewConsumeGasForTxSizeDecorator(options.AccountKeeper), - ante.NewDeductFeeDecorator(options.AccountKeeper, options.BankKeeper, options.FeegrantKeeper), - // SetPubKeyDecorator must be called before all signature verification decorators - ante.NewSetPubKeyDecorator(options.AccountKeeper), + ante.NewDeductFeeDecorator(options.AccountKeeper, options.BankKeeper, options.FeegrantKeeper, options.TxFeeChecker), + ante.NewSetPubKeyDecorator(options.AccountKeeper), // SetPubKeyDecorator must be called before all signature verification decorators ante.NewValidateSigCountDecorator(options.AccountKeeper), - ante.NewSigGasConsumeDecorator(options.AccountKeeper, sigGasConsumer), + ante.NewSigGasConsumeDecorator(options.AccountKeeper, options.SigGasConsumer), ante.NewSigVerificationDecorator(options.AccountKeeper, options.SignModeHandler), ante.NewIncrementSequenceDecorator(options.AccountKeeper), - ibcante.NewAnteDecorator(options.IBCKeeper), + ibcante.NewRedundantRelayDecorator(options.IBCKeeper), } return sdk.ChainAnteDecorators(anteDecorators...), nil diff --git a/testing/simapp/app.go b/testing/simapp/app.go index 312de2eb624..78b72ad6c6e 100644 --- a/testing/simapp/app.go +++ b/testing/simapp/app.go @@ -2,53 +2,54 @@ package simapp import ( "encoding/json" + "fmt" "io" "net/http" "os" "path/filepath" - "github.com/gorilla/mux" - "github.com/rakyll/statik/fs" - "github.com/spf13/cast" - abci "github.com/tendermint/tendermint/abci/types" - "github.com/tendermint/tendermint/libs/log" - tmos "github.com/tendermint/tendermint/libs/os" - dbm "github.com/tendermint/tm-db" - + autocliv1 "cosmossdk.io/api/cosmos/autocli/v1" + reflectionv1 "cosmossdk.io/api/cosmos/reflection/v1" "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/client" + _ "github.com/cosmos/cosmos-sdk/client/docs/statik" // this is used for serving docs + nodeservice "github.com/cosmos/cosmos-sdk/client/grpc/node" "github.com/cosmos/cosmos-sdk/client/grpc/tmservice" - "github.com/cosmos/cosmos-sdk/client/rpc" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/codec/types" + runtimeservices "github.com/cosmos/cosmos-sdk/runtime/services" "github.com/cosmos/cosmos-sdk/server/api" "github.com/cosmos/cosmos-sdk/server/config" servertypes "github.com/cosmos/cosmos-sdk/server/types" + storetypes "github.com/cosmos/cosmos-sdk/store/types" "github.com/cosmos/cosmos-sdk/testutil/testdata" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" "github.com/cosmos/cosmos-sdk/version" "github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/auth/ante" - authrest "github.com/cosmos/cosmos-sdk/x/auth/client/rest" authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" authsims "github.com/cosmos/cosmos-sdk/x/auth/simulation" authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" "github.com/cosmos/cosmos-sdk/x/auth/vesting" vestingtypes "github.com/cosmos/cosmos-sdk/x/auth/vesting/types" + authz "github.com/cosmos/cosmos-sdk/x/authz" + authzkeeper "github.com/cosmos/cosmos-sdk/x/authz/keeper" + authzmodule "github.com/cosmos/cosmos-sdk/x/authz/module" "github.com/cosmos/cosmos-sdk/x/bank" bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" "github.com/cosmos/cosmos-sdk/x/capability" capabilitykeeper "github.com/cosmos/cosmos-sdk/x/capability/keeper" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - + "github.com/cosmos/cosmos-sdk/x/consensus" + consensusparamkeeper "github.com/cosmos/cosmos-sdk/x/consensus/keeper" + consensusparamtypes "github.com/cosmos/cosmos-sdk/x/consensus/types" "github.com/cosmos/cosmos-sdk/x/crisis" crisiskeeper "github.com/cosmos/cosmos-sdk/x/crisis/keeper" crisistypes "github.com/cosmos/cosmos-sdk/x/crisis/types" distr "github.com/cosmos/cosmos-sdk/x/distribution" - distrclient "github.com/cosmos/cosmos-sdk/x/distribution/client" distrkeeper "github.com/cosmos/cosmos-sdk/x/distribution/keeper" distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" "github.com/cosmos/cosmos-sdk/x/evidence" @@ -60,8 +61,13 @@ import ( "github.com/cosmos/cosmos-sdk/x/genutil" genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" "github.com/cosmos/cosmos-sdk/x/gov" + govclient "github.com/cosmos/cosmos-sdk/x/gov/client" govkeeper "github.com/cosmos/cosmos-sdk/x/gov/keeper" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + govv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" + "github.com/cosmos/cosmos-sdk/x/group" + groupkeeper "github.com/cosmos/cosmos-sdk/x/group/keeper" + groupmodule "github.com/cosmos/cosmos-sdk/x/group/module" "github.com/cosmos/cosmos-sdk/x/mint" mintkeeper "github.com/cosmos/cosmos-sdk/x/mint/keeper" minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" @@ -80,37 +86,43 @@ import ( upgradeclient "github.com/cosmos/cosmos-sdk/x/upgrade/client" upgradekeeper "github.com/cosmos/cosmos-sdk/x/upgrade/keeper" upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" + "github.com/gorilla/mux" + "github.com/rakyll/statik/fs" + "github.com/spf13/cast" + abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/libs/log" + tmos "github.com/tendermint/tendermint/libs/os" + dbm "github.com/tendermint/tm-db" - ica "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts" - icacontroller "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/controller" - icacontrollerkeeper "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/controller/keeper" - icacontrollertypes "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/controller/types" - icahost "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/host" - icahostkeeper "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/host/keeper" - icahosttypes "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" - ibcfee "github.com/cosmos/ibc-go/v4/modules/apps/29-fee" - ibcfeekeeper "github.com/cosmos/ibc-go/v4/modules/apps/29-fee/keeper" - ibcfeetypes "github.com/cosmos/ibc-go/v4/modules/apps/29-fee/types" - transfer "github.com/cosmos/ibc-go/v4/modules/apps/transfer" - ibctransferkeeper "github.com/cosmos/ibc-go/v4/modules/apps/transfer/keeper" - ibctransfertypes "github.com/cosmos/ibc-go/v4/modules/apps/transfer/types" - ibc "github.com/cosmos/ibc-go/v4/modules/core" - ibcclient "github.com/cosmos/ibc-go/v4/modules/core/02-client" - ibcclientclient "github.com/cosmos/ibc-go/v4/modules/core/02-client/client" - ibcclienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - porttypes "github.com/cosmos/ibc-go/v4/modules/core/05-port/types" - ibchost "github.com/cosmos/ibc-go/v4/modules/core/24-host" - ibckeeper "github.com/cosmos/ibc-go/v4/modules/core/keeper" - ibcmock "github.com/cosmos/ibc-go/v4/testing/mock" - simappparams "github.com/cosmos/ibc-go/v4/testing/simapp/params" - - authz "github.com/cosmos/cosmos-sdk/x/authz" - authzkeeper "github.com/cosmos/cosmos-sdk/x/authz/keeper" - authzmodule "github.com/cosmos/cosmos-sdk/x/authz/module" - - // unnamed import of statik for swagger UI support - _ "github.com/cosmos/cosmos-sdk/client/docs/statik" + ica "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts" + icacontroller "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/controller" + icacontrollerkeeper "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/controller/keeper" + icacontrollertypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/controller/types" + icahost "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/host" + icahostkeeper "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/host/keeper" + icahosttypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/host/types" + icatypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types" + ibcfee "github.com/cosmos/ibc-go/v7/modules/apps/29-fee" + ibcfeekeeper "github.com/cosmos/ibc-go/v7/modules/apps/29-fee/keeper" + ibcfeetypes "github.com/cosmos/ibc-go/v7/modules/apps/29-fee/types" + transfer "github.com/cosmos/ibc-go/v7/modules/apps/transfer" + ibctransferkeeper "github.com/cosmos/ibc-go/v7/modules/apps/transfer/keeper" + ibctransfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" + ibc "github.com/cosmos/ibc-go/v7/modules/core" + ibcclient "github.com/cosmos/ibc-go/v7/modules/core/02-client" + ibcclientclient "github.com/cosmos/ibc-go/v7/modules/core/02-client/client" + ibcclienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + porttypes "github.com/cosmos/ibc-go/v7/modules/core/05-port/types" + ibcexported "github.com/cosmos/ibc-go/v7/modules/core/exported" + ibckeeper "github.com/cosmos/ibc-go/v7/modules/core/keeper" + solomachine "github.com/cosmos/ibc-go/v7/modules/light-clients/06-solomachine" + ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" + ibcmock "github.com/cosmos/ibc-go/v7/testing/mock" + simappparams "github.com/cosmos/ibc-go/v7/testing/simapp/params" + simappupgrades "github.com/cosmos/ibc-go/v7/testing/simapp/upgrades" + v6 "github.com/cosmos/ibc-go/v7/testing/simapp/upgrades/v6" + v7 "github.com/cosmos/ibc-go/v7/testing/simapp/upgrades/v7" + ibctestingtypes "github.com/cosmos/ibc-go/v7/testing/types" ) const appName = "SimApp" @@ -129,20 +141,28 @@ var ( // and genesis verification. ModuleBasics = module.NewBasicManager( auth.AppModuleBasic{}, - genutil.AppModuleBasic{}, + genutil.NewAppModuleBasic(genutiltypes.DefaultMessageValidator), bank.AppModuleBasic{}, capability.AppModuleBasic{}, staking.AppModuleBasic{}, mint.AppModuleBasic{}, distr.AppModuleBasic{}, gov.NewAppModuleBasic( - paramsclient.ProposalHandler, distrclient.ProposalHandler, upgradeclient.ProposalHandler, upgradeclient.CancelProposalHandler, - ibcclientclient.UpdateClientProposalHandler, ibcclientclient.UpgradeProposalHandler, + []govclient.ProposalHandler{ + paramsclient.ProposalHandler, + upgradeclient.LegacyProposalHandler, + upgradeclient.LegacyCancelProposalHandler, + ibcclientclient.UpdateClientProposalHandler, + ibcclientclient.UpgradeProposalHandler, + }, ), + groupmodule.AppModuleBasic{}, params.AppModuleBasic{}, crisis.AppModuleBasic{}, slashing.AppModuleBasic{}, ibc.AppModuleBasic{}, + ibctm.AppModuleBasic{}, + solomachine.AppModuleBasic{}, feegrantmodule.AppModuleBasic{}, upgrade.AppModuleBasic{}, evidence.AppModuleBasic{}, @@ -152,6 +172,7 @@ var ( authzmodule.AppModuleBasic{}, vesting.AppModuleBasic{}, ibcfee.AppModuleBasic{}, + consensus.AppModuleBasic{}, ) // module account permissions @@ -182,39 +203,41 @@ type SimApp struct { legacyAmino *codec.LegacyAmino appCodec codec.Codec interfaceRegistry types.InterfaceRegistry + txConfig client.TxConfig invCheckPeriod uint // keys to access the substores - keys map[string]*sdk.KVStoreKey - tkeys map[string]*sdk.TransientStoreKey - memKeys map[string]*sdk.MemoryStoreKey + keys map[string]*storetypes.KVStoreKey + tkeys map[string]*storetypes.TransientStoreKey + memKeys map[string]*storetypes.MemoryStoreKey // keepers - AccountKeeper authkeeper.AccountKeeper - BankKeeper bankkeeper.Keeper - CapabilityKeeper *capabilitykeeper.Keeper - StakingKeeper stakingkeeper.Keeper - SlashingKeeper slashingkeeper.Keeper - MintKeeper mintkeeper.Keeper - DistrKeeper distrkeeper.Keeper - GovKeeper govkeeper.Keeper - CrisisKeeper crisiskeeper.Keeper - UpgradeKeeper upgradekeeper.Keeper - ParamsKeeper paramskeeper.Keeper - AuthzKeeper authzkeeper.Keeper - IBCKeeper *ibckeeper.Keeper // IBC Keeper must be a pointer in the app, so we can SetRouter on it correctly - IBCFeeKeeper ibcfeekeeper.Keeper - ICAControllerKeeper icacontrollerkeeper.Keeper - ICAHostKeeper icahostkeeper.Keeper - EvidenceKeeper evidencekeeper.Keeper - TransferKeeper ibctransferkeeper.Keeper - FeeGrantKeeper feegrantkeeper.Keeper + AccountKeeper authkeeper.AccountKeeper + BankKeeper bankkeeper.Keeper + CapabilityKeeper *capabilitykeeper.Keeper + StakingKeeper *stakingkeeper.Keeper + SlashingKeeper slashingkeeper.Keeper + MintKeeper mintkeeper.Keeper + DistrKeeper distrkeeper.Keeper + GovKeeper govkeeper.Keeper + GroupKeeper groupkeeper.Keeper + CrisisKeeper *crisiskeeper.Keeper + UpgradeKeeper *upgradekeeper.Keeper + ParamsKeeper paramskeeper.Keeper + AuthzKeeper authzkeeper.Keeper + IBCKeeper *ibckeeper.Keeper // IBC Keeper must be a pointer in the app, so we can SetRouter on it correctly + IBCFeeKeeper ibcfeekeeper.Keeper + ICAControllerKeeper icacontrollerkeeper.Keeper + ICAHostKeeper icahostkeeper.Keeper + EvidenceKeeper evidencekeeper.Keeper + TransferKeeper ibctransferkeeper.Keeper + FeeGrantKeeper feegrantkeeper.Keeper + ConsensusParamsKeeper consensusparamkeeper.Keeper // make scoped keepers public for test purposes ScopedIBCKeeper capabilitykeeper.ScopedKeeper ScopedTransferKeeper capabilitykeeper.ScopedKeeper - ScopedIBCFeeKeeper capabilitykeeper.ScopedKeeper ScopedFeeMockKeeper capabilitykeeper.ScopedKeeper ScopedICAControllerKeeper capabilitykeeper.ScopedKeeper ScopedICAHostKeeper capabilitykeeper.ScopedKeeper @@ -261,11 +284,11 @@ func NewSimApp( bApp.SetInterfaceRegistry(interfaceRegistry) keys := sdk.NewKVStoreKeys( - authtypes.StoreKey, banktypes.StoreKey, stakingtypes.StoreKey, + authtypes.StoreKey, banktypes.StoreKey, stakingtypes.StoreKey, crisistypes.StoreKey, minttypes.StoreKey, distrtypes.StoreKey, slashingtypes.StoreKey, - govtypes.StoreKey, paramstypes.StoreKey, ibchost.StoreKey, upgradetypes.StoreKey, feegrant.StoreKey, + govtypes.StoreKey, group.StoreKey, paramstypes.StoreKey, ibcexported.StoreKey, upgradetypes.StoreKey, feegrant.StoreKey, evidencetypes.StoreKey, ibctransfertypes.StoreKey, icacontrollertypes.StoreKey, icahosttypes.StoreKey, capabilitytypes.StoreKey, - authzkeeper.StoreKey, ibcfeetypes.StoreKey, + authzkeeper.StoreKey, ibcfeetypes.StoreKey, consensusparamtypes.StoreKey, ) tkeys := sdk.NewTransientStoreKeys(paramstypes.TStoreKey) memKeys := sdk.NewMemoryStoreKeys(capabilitytypes.MemStoreKey) @@ -279,16 +302,18 @@ func NewSimApp( keys: keys, tkeys: tkeys, memKeys: memKeys, + txConfig: encodingConfig.TxConfig, } app.ParamsKeeper = initParamsKeeper(appCodec, legacyAmino, keys[paramstypes.StoreKey], tkeys[paramstypes.TStoreKey]) // set the BaseApp's parameter store - bApp.SetParamStore(app.ParamsKeeper.Subspace(baseapp.Paramspace).WithKeyTable(paramskeeper.ConsensusParamsKeyTable())) + app.ConsensusParamsKeeper = consensusparamkeeper.NewKeeper(appCodec, keys[consensusparamtypes.StoreKey], authtypes.NewModuleAddress(govtypes.ModuleName).String()) + bApp.SetParamStore(&app.ConsensusParamsKeeper) // add capability keeper and ScopeToModule for ibc module app.CapabilityKeeper = capabilitykeeper.NewKeeper(appCodec, keys[capabilitytypes.StoreKey], memKeys[capabilitytypes.MemStoreKey]) - scopedIBCKeeper := app.CapabilityKeeper.ScopeToModule(ibchost.ModuleName) + scopedIBCKeeper := app.CapabilityKeeper.ScopeToModule(ibcexported.ModuleName) scopedTransferKeeper := app.CapabilityKeeper.ScopeToModule(ibctransfertypes.ModuleName) scopedICAControllerKeeper := app.CapabilityKeeper.ScopeToModule(icacontrollertypes.SubModuleName) scopedICAHostKeeper := app.CapabilityKeeper.ScopeToModule(icahosttypes.SubModuleName) @@ -304,62 +329,89 @@ func NewSimApp( // SDK module keepers - app.AccountKeeper = authkeeper.NewAccountKeeper( - appCodec, keys[authtypes.StoreKey], app.GetSubspace(authtypes.ModuleName), authtypes.ProtoBaseAccount, maccPerms, - ) + app.AccountKeeper = authkeeper.NewAccountKeeper(appCodec, keys[authtypes.StoreKey], authtypes.ProtoBaseAccount, maccPerms, sdk.GetConfig().GetBech32AccountAddrPrefix(), authtypes.NewModuleAddress(govtypes.ModuleName).String()) + app.BankKeeper = bankkeeper.NewBaseKeeper( - appCodec, keys[banktypes.StoreKey], app.AccountKeeper, app.GetSubspace(banktypes.ModuleName), app.ModuleAccountAddrs(), + appCodec, + keys[banktypes.StoreKey], + app.AccountKeeper, + BlockedAddresses(), + authtypes.NewModuleAddress(govtypes.ModuleName).String(), ) - stakingKeeper := stakingkeeper.NewKeeper( - appCodec, keys[stakingtypes.StoreKey], app.AccountKeeper, app.BankKeeper, app.GetSubspace(stakingtypes.ModuleName), + + // register the staking hooks + // NOTE: stakingKeeper above is passed by reference, so that it will contain these hooks + app.StakingKeeper = stakingkeeper.NewKeeper( + appCodec, keys[stakingtypes.StoreKey], app.AccountKeeper, app.BankKeeper, authtypes.NewModuleAddress(govtypes.ModuleName).String(), ) + app.MintKeeper = mintkeeper.NewKeeper( - appCodec, keys[minttypes.StoreKey], app.GetSubspace(minttypes.ModuleName), &stakingKeeper, - app.AccountKeeper, app.BankKeeper, authtypes.FeeCollectorName, - ) - app.DistrKeeper = distrkeeper.NewKeeper( - appCodec, keys[distrtypes.StoreKey], app.GetSubspace(distrtypes.ModuleName), app.AccountKeeper, app.BankKeeper, - &stakingKeeper, authtypes.FeeCollectorName, app.ModuleAccountAddrs(), + appCodec, keys[minttypes.StoreKey], app.StakingKeeper, + app.AccountKeeper, app.BankKeeper, authtypes.FeeCollectorName, authtypes.NewModuleAddress(govtypes.ModuleName).String(), ) + + app.DistrKeeper = distrkeeper.NewKeeper(appCodec, keys[distrtypes.StoreKey], app.AccountKeeper, app.BankKeeper, app.StakingKeeper, authtypes.FeeCollectorName, authtypes.NewModuleAddress(govtypes.ModuleName).String()) + app.SlashingKeeper = slashingkeeper.NewKeeper( - appCodec, keys[slashingtypes.StoreKey], &stakingKeeper, app.GetSubspace(slashingtypes.ModuleName), + appCodec, legacyAmino, keys[slashingtypes.StoreKey], app.StakingKeeper, authtypes.NewModuleAddress(govtypes.ModuleName).String(), ) - app.CrisisKeeper = crisiskeeper.NewKeeper( - app.GetSubspace(crisistypes.ModuleName), invCheckPeriod, app.BankKeeper, authtypes.FeeCollectorName, - ) - - app.FeeGrantKeeper = feegrantkeeper.NewKeeper(appCodec, keys[feegrant.StoreKey], app.AccountKeeper) - app.UpgradeKeeper = upgradekeeper.NewKeeper(skipUpgradeHeights, keys[upgradetypes.StoreKey], appCodec, homePath, app.BaseApp) // register the staking hooks // NOTE: stakingKeeper above is passed by reference, so that it will contain these hooks - app.StakingKeeper = *stakingKeeper.SetHooks( + app.StakingKeeper.SetHooks( stakingtypes.NewMultiStakingHooks(app.DistrKeeper.Hooks(), app.SlashingKeeper.Hooks()), ) - app.AuthzKeeper = authzkeeper.NewKeeper(keys[authzkeeper.StoreKey], appCodec, app.BaseApp.MsgServiceRouter()) + app.CrisisKeeper = crisiskeeper.NewKeeper(appCodec, keys[crisistypes.StoreKey], invCheckPeriod, + app.BankKeeper, authtypes.FeeCollectorName, authtypes.NewModuleAddress(govtypes.ModuleName).String()) + + app.FeeGrantKeeper = feegrantkeeper.NewKeeper(appCodec, keys[feegrant.StoreKey], app.AccountKeeper) + app.UpgradeKeeper = upgradekeeper.NewKeeper(skipUpgradeHeights, keys[upgradetypes.StoreKey], appCodec, homePath, app.BaseApp, authtypes.NewModuleAddress(govtypes.ModuleName).String()) + + app.AuthzKeeper = authzkeeper.NewKeeper(keys[authzkeeper.StoreKey], appCodec, app.MsgServiceRouter(), app.AccountKeeper) // IBC Keepers app.IBCKeeper = ibckeeper.NewKeeper( - appCodec, keys[ibchost.StoreKey], app.GetSubspace(ibchost.ModuleName), app.StakingKeeper, app.UpgradeKeeper, scopedIBCKeeper, + appCodec, keys[ibcexported.StoreKey], app.GetSubspace(ibcexported.ModuleName), app.StakingKeeper, app.UpgradeKeeper, scopedIBCKeeper, ) // register the proposal types - govRouter := govtypes.NewRouter() - govRouter.AddRoute(govtypes.RouterKey, govtypes.ProposalHandler). + govRouter := govv1beta1.NewRouter() + govRouter.AddRoute(govtypes.RouterKey, govv1beta1.ProposalHandler). AddRoute(paramproposal.RouterKey, params.NewParamChangeProposalHandler(app.ParamsKeeper)). - AddRoute(distrtypes.RouterKey, distr.NewCommunityPoolSpendProposalHandler(app.DistrKeeper)). AddRoute(upgradetypes.RouterKey, upgrade.NewSoftwareUpgradeProposalHandler(app.UpgradeKeeper)). AddRoute(ibcclienttypes.RouterKey, ibcclient.NewClientProposalHandler(app.IBCKeeper.ClientKeeper)) - app.GovKeeper = govkeeper.NewKeeper( - appCodec, keys[govtypes.StoreKey], app.GetSubspace(govtypes.ModuleName), app.AccountKeeper, app.BankKeeper, - &stakingKeeper, govRouter, + + govConfig := govtypes.DefaultConfig() + /* + Example of setting gov params: + govConfig.MaxMetadataLen = 10000 + */ + govKeeper := govkeeper.NewKeeper( + appCodec, keys[govtypes.StoreKey], app.AccountKeeper, app.BankKeeper, + app.StakingKeeper, app.MsgServiceRouter(), govConfig, authtypes.NewModuleAddress(govtypes.ModuleName).String(), + ) + + // Set legacy router for backwards compatibility with gov v1beta1 + govKeeper.SetLegacyRouter(govRouter) + + app.GovKeeper = *govKeeper.SetHooks( + govtypes.NewMultiGovHooks( + // register the governance hooks + ), ) + groupConfig := group.DefaultConfig() + /* + Example of setting group params: + groupConfig.MaxMetadataLen = 1000 + */ + app.GroupKeeper = groupkeeper.NewKeeper(keys[group.StoreKey], appCodec, app.MsgServiceRouter(), app.AccountKeeper, groupConfig) + // IBC Fee Module keeper app.IBCFeeKeeper = ibcfeekeeper.NewKeeper( - appCodec, keys[ibcfeetypes.StoreKey], app.GetSubspace(ibcfeetypes.ModuleName), + appCodec, keys[ibcfeetypes.StoreKey], app.IBCKeeper.ChannelKeeper, // may be replaced with IBC middleware app.IBCKeeper.ChannelKeeper, &app.IBCKeeper.PortKeeper, app.AccountKeeper, app.BankKeeper, @@ -376,6 +428,7 @@ func NewSimApp( // ICA Host keeper app.ICAHostKeeper = icahostkeeper.NewKeeper( appCodec, keys[icahosttypes.StoreKey], app.GetSubspace(icahosttypes.SubModuleName), + app.IBCFeeKeeper, // use ics29 fee as ics4Wrapper in middleware stack app.IBCKeeper.ChannelKeeper, &app.IBCKeeper.PortKeeper, app.AccountKeeper, scopedICAHostKeeper, app.MsgServiceRouter(), ) @@ -402,7 +455,7 @@ func NewSimApp( mockModule := ibcmock.NewAppModule(&app.IBCKeeper.PortKeeper) // The mock module is used for testing IBC - mockIBCModule := ibcmock.NewIBCModule(&mockModule, ibcmock.NewMockIBCApp(ibcmock.ModuleName, scopedIBCMockKeeper)) + mockIBCModule := ibcmock.NewIBCModule(&mockModule, ibcmock.NewIBCApp(ibcmock.ModuleName, scopedIBCMockKeeper)) ibcRouter.AddRoute(ibcmock.ModuleName, mockIBCModule) // Create Transfer Stack @@ -430,7 +483,7 @@ func NewSimApp( // initialize ICA module with mock module as the authentication module on the controller side var icaControllerStack porttypes.IBCModule - icaControllerStack = ibcmock.NewIBCModule(&mockModule, ibcmock.NewMockIBCApp("", scopedICAMockKeeper)) + icaControllerStack = ibcmock.NewIBCModule(&mockModule, ibcmock.NewIBCApp("", scopedICAMockKeeper)) app.ICAAuthModule = icaControllerStack.(ibcmock.IBCModule) icaControllerStack = icacontroller.NewIBCMiddleware(icaControllerStack, app.ICAControllerKeeper) icaControllerStack = ibcfee.NewIBCMiddleware(icaControllerStack, app.IBCFeeKeeper) @@ -462,7 +515,7 @@ func NewSimApp( // mockModule.OnAcknowledgementPacket -> fee.OnAcknowledgementPacket -> channel.OnAcknowledgementPacket // create fee wrapped mock module - feeMockModule := ibcmock.NewIBCModule(&mockModule, ibcmock.NewMockIBCApp(MockFeePort, scopedFeeMockKeeper)) + feeMockModule := ibcmock.NewIBCModule(&mockModule, ibcmock.NewIBCApp(MockFeePort, scopedFeeMockKeeper)) app.FeeMockModule = feeMockModule feeWithMockModule := ibcfee.NewIBCMiddleware(feeMockModule, app.IBCFeeKeeper) ibcRouter.AddRoute(MockFeePort, feeWithMockModule) @@ -472,7 +525,7 @@ func NewSimApp( // create evidence keeper with router evidenceKeeper := evidencekeeper.NewKeeper( - appCodec, keys[evidencetypes.StoreKey], &app.StakingKeeper, app.SlashingKeeper, + appCodec, keys[evidencetypes.StoreKey], app.StakingKeeper, app.SlashingKeeper, ) // If evidence needs to be handled for the app, set routes in router here and seal app.EvidenceKeeper = *evidenceKeeper @@ -491,24 +544,26 @@ func NewSimApp( app.AccountKeeper, app.StakingKeeper, app.BaseApp.DeliverTx, encodingConfig.TxConfig, ), - auth.NewAppModule(appCodec, app.AccountKeeper, authsims.RandomGenesisAccounts), + auth.NewAppModule(appCodec, app.AccountKeeper, authsims.RandomGenesisAccounts, app.GetSubspace(authtypes.ModuleName)), vesting.NewAppModule(app.AccountKeeper, app.BankKeeper), - bank.NewAppModule(appCodec, app.BankKeeper, app.AccountKeeper), - capability.NewAppModule(appCodec, *app.CapabilityKeeper), - crisis.NewAppModule(&app.CrisisKeeper, skipGenesisInvariants), + bank.NewAppModule(appCodec, app.BankKeeper, app.AccountKeeper, app.GetSubspace(banktypes.ModuleName)), + capability.NewAppModule(appCodec, *app.CapabilityKeeper, false), + crisis.NewAppModule(app.CrisisKeeper, skipGenesisInvariants, app.GetSubspace(crisistypes.ModuleName)), feegrantmodule.NewAppModule(appCodec, app.AccountKeeper, app.BankKeeper, app.FeeGrantKeeper, app.interfaceRegistry), - gov.NewAppModule(appCodec, app.GovKeeper, app.AccountKeeper, app.BankKeeper), - mint.NewAppModule(appCodec, app.MintKeeper, app.AccountKeeper), - slashing.NewAppModule(appCodec, app.SlashingKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper), - distr.NewAppModule(appCodec, app.DistrKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper), - staking.NewAppModule(appCodec, app.StakingKeeper, app.AccountKeeper, app.BankKeeper), + gov.NewAppModule(appCodec, &app.GovKeeper, app.AccountKeeper, app.BankKeeper, app.GetSubspace(govtypes.ModuleName)), + mint.NewAppModule(appCodec, app.MintKeeper, app.AccountKeeper, nil, app.GetSubspace(minttypes.ModuleName)), + slashing.NewAppModule(appCodec, app.SlashingKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper, app.GetSubspace(slashingtypes.ModuleName)), + distr.NewAppModule(appCodec, app.DistrKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper, app.GetSubspace(distrtypes.ModuleName)), + staking.NewAppModule(appCodec, app.StakingKeeper, app.AccountKeeper, app.BankKeeper, app.GetSubspace(stakingtypes.ModuleName)), upgrade.NewAppModule(app.UpgradeKeeper), evidence.NewAppModule(app.EvidenceKeeper), - ibc.NewAppModule(app.IBCKeeper), params.NewAppModule(app.ParamsKeeper), authzmodule.NewAppModule(appCodec, app.AuthzKeeper, app.AccountKeeper, app.BankKeeper, app.interfaceRegistry), + groupmodule.NewAppModule(appCodec, app.GroupKeeper, app.AccountKeeper, app.BankKeeper, app.interfaceRegistry), + consensus.NewAppModule(appCodec, app.ConsensusParamsKeeper), // IBC modules + ibc.NewAppModule(app.IBCKeeper), transfer.NewAppModule(app.TransferKeeper), ibcfee.NewAppModule(app.IBCFeeKeeper), ica.NewAppModule(&app.ICAControllerKeeper, &app.ICAHostKeeper), @@ -522,15 +577,15 @@ func NewSimApp( // NOTE: capability module's beginblocker must come before any modules using capabilities (e.g. IBC) app.mm.SetOrderBeginBlockers( upgradetypes.ModuleName, capabilitytypes.ModuleName, minttypes.ModuleName, distrtypes.ModuleName, slashingtypes.ModuleName, - evidencetypes.ModuleName, stakingtypes.ModuleName, ibchost.ModuleName, ibctransfertypes.ModuleName, authtypes.ModuleName, + evidencetypes.ModuleName, stakingtypes.ModuleName, ibcexported.ModuleName, ibctransfertypes.ModuleName, authtypes.ModuleName, banktypes.ModuleName, govtypes.ModuleName, crisistypes.ModuleName, genutiltypes.ModuleName, authz.ModuleName, feegrant.ModuleName, - paramstypes.ModuleName, vestingtypes.ModuleName, icatypes.ModuleName, ibcfeetypes.ModuleName, ibcmock.ModuleName, + paramstypes.ModuleName, vestingtypes.ModuleName, icatypes.ModuleName, ibcfeetypes.ModuleName, ibcmock.ModuleName, group.ModuleName, consensusparamtypes.ModuleName, ) app.mm.SetOrderEndBlockers( - crisistypes.ModuleName, govtypes.ModuleName, stakingtypes.ModuleName, ibchost.ModuleName, ibctransfertypes.ModuleName, + crisistypes.ModuleName, govtypes.ModuleName, stakingtypes.ModuleName, ibcexported.ModuleName, ibctransfertypes.ModuleName, capabilitytypes.ModuleName, authtypes.ModuleName, banktypes.ModuleName, distrtypes.ModuleName, slashingtypes.ModuleName, minttypes.ModuleName, genutiltypes.ModuleName, evidencetypes.ModuleName, authz.ModuleName, feegrant.ModuleName, paramstypes.ModuleName, - upgradetypes.ModuleName, vestingtypes.ModuleName, icatypes.ModuleName, ibcfeetypes.ModuleName, ibcmock.ModuleName, + upgradetypes.ModuleName, vestingtypes.ModuleName, icatypes.ModuleName, ibcfeetypes.ModuleName, ibcmock.ModuleName, group.ModuleName, consensusparamtypes.ModuleName, ) // NOTE: The genutils module must occur after staking so that pools are @@ -541,15 +596,23 @@ func NewSimApp( app.mm.SetOrderInitGenesis( capabilitytypes.ModuleName, authtypes.ModuleName, banktypes.ModuleName, distrtypes.ModuleName, stakingtypes.ModuleName, slashingtypes.ModuleName, govtypes.ModuleName, minttypes.ModuleName, crisistypes.ModuleName, - ibchost.ModuleName, genutiltypes.ModuleName, evidencetypes.ModuleName, authz.ModuleName, ibctransfertypes.ModuleName, - icatypes.ModuleName, ibcfeetypes.ModuleName, ibcmock.ModuleName, feegrant.ModuleName, paramstypes.ModuleName, upgradetypes.ModuleName, vestingtypes.ModuleName, + ibcexported.ModuleName, genutiltypes.ModuleName, evidencetypes.ModuleName, authz.ModuleName, ibctransfertypes.ModuleName, + icatypes.ModuleName, ibcfeetypes.ModuleName, ibcmock.ModuleName, feegrant.ModuleName, paramstypes.ModuleName, upgradetypes.ModuleName, + vestingtypes.ModuleName, group.ModuleName, consensusparamtypes.ModuleName, ) - app.mm.RegisterInvariants(&app.CrisisKeeper) - app.mm.RegisterRoutes(app.Router(), app.QueryRouter(), encodingConfig.Amino) + app.mm.RegisterInvariants(app.CrisisKeeper) app.configurator = module.NewConfigurator(app.appCodec, app.MsgServiceRouter(), app.GRPCQueryRouter()) app.mm.RegisterServices(app.configurator) + autocliv1.RegisterQueryServer(app.GRPCQueryRouter(), runtimeservices.NewAutoCLIQueryService(app.mm.Modules)) + + reflectionSvc, err := runtimeservices.NewReflectionService() + if err != nil { + panic(err) + } + reflectionv1.RegisterReflectionServiceServer(app.GRPCQueryRouter(), reflectionSvc) + // add test gRPC service for testing gRPC queries in isolation testdata.RegisterQueryServer(app.GRPCQueryRouter(), testdata.QueryImpl{}) @@ -558,20 +621,21 @@ func NewSimApp( // NOTE: this is not required apps that don't use the simulator for fuzz testing // transactions app.sm = module.NewSimulationManager( - auth.NewAppModule(appCodec, app.AccountKeeper, authsims.RandomGenesisAccounts), - bank.NewAppModule(appCodec, app.BankKeeper, app.AccountKeeper), - capability.NewAppModule(appCodec, *app.CapabilityKeeper), + auth.NewAppModule(appCodec, app.AccountKeeper, authsims.RandomGenesisAccounts, app.GetSubspace(authtypes.ModuleName)), + bank.NewAppModule(appCodec, app.BankKeeper, app.AccountKeeper, app.GetSubspace(banktypes.ModuleName)), + capability.NewAppModule(appCodec, *app.CapabilityKeeper, false), feegrantmodule.NewAppModule(appCodec, app.AccountKeeper, app.BankKeeper, app.FeeGrantKeeper, app.interfaceRegistry), - gov.NewAppModule(appCodec, app.GovKeeper, app.AccountKeeper, app.BankKeeper), - mint.NewAppModule(appCodec, app.MintKeeper, app.AccountKeeper), - staking.NewAppModule(appCodec, app.StakingKeeper, app.AccountKeeper, app.BankKeeper), - distr.NewAppModule(appCodec, app.DistrKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper), - slashing.NewAppModule(appCodec, app.SlashingKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper), + gov.NewAppModule(appCodec, &app.GovKeeper, app.AccountKeeper, app.BankKeeper, app.GetSubspace(govtypes.ModuleName)), + mint.NewAppModule(appCodec, app.MintKeeper, app.AccountKeeper, nil, app.GetSubspace(minttypes.ModuleName)), + staking.NewAppModule(appCodec, app.StakingKeeper, app.AccountKeeper, app.BankKeeper, app.GetSubspace(stakingtypes.ModuleName)), + distr.NewAppModule(appCodec, app.DistrKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper, app.GetSubspace(distrtypes.ModuleName)), + slashing.NewAppModule(appCodec, app.SlashingKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper, app.GetSubspace(slashingtypes.ModuleName)), params.NewAppModule(app.ParamsKeeper), evidence.NewAppModule(app.EvidenceKeeper), authzmodule.NewAppModule(appCodec, app.AuthzKeeper, app.AccountKeeper, app.BankKeeper, app.interfaceRegistry), ibc.NewAppModule(app.IBCKeeper), transfer.NewAppModule(app.TransferKeeper), + ica.NewAppModule(&app.ICAControllerKeeper, &app.ICAHostKeeper), ) app.sm.RegisterStoreDecoders() @@ -604,6 +668,9 @@ func NewSimApp( app.SetEndBlocker(app.EndBlocker) + app.setupUpgradeHandlers() + app.setupUpgradeStoreLoaders() + if loadLatest { if err := app.LoadLatestVersion(); err != nil { tmos.Exit(err.Error()) @@ -656,7 +723,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 @@ -698,21 +765,21 @@ func (app *SimApp) InterfaceRegistry() types.InterfaceRegistry { // GetKey returns the KVStoreKey for the provided store key. // // NOTE: This is solely to be used for testing purposes. -func (app *SimApp) GetKey(storeKey string) *sdk.KVStoreKey { +func (app *SimApp) GetKey(storeKey string) *storetypes.KVStoreKey { return app.keys[storeKey] } // GetTKey returns the TransientStoreKey for the provided store key. // // NOTE: This is solely to be used for testing purposes. -func (app *SimApp) GetTKey(storeKey string) *sdk.TransientStoreKey { +func (app *SimApp) GetTKey(storeKey string) *storetypes.TransientStoreKey { return app.tkeys[storeKey] } // GetMemKey returns the MemStoreKey for the provided mem key. // // NOTE: This is solely used for testing purposes. -func (app *SimApp) GetMemKey(storeKey string) *sdk.MemoryStoreKey { +func (app *SimApp) GetMemKey(storeKey string) *storetypes.MemoryStoreKey { return app.memKeys[storeKey] } @@ -732,7 +799,7 @@ func (app *SimApp) GetBaseApp() *baseapp.BaseApp { } // GetStakingKeeper implements the TestingApp interface. -func (app *SimApp) GetStakingKeeper() stakingkeeper.Keeper { +func (app *SimApp) GetStakingKeeper() ibctestingtypes.StakingKeeper { return app.StakingKeeper } @@ -748,7 +815,7 @@ func (app *SimApp) GetScopedIBCKeeper() capabilitykeeper.ScopedKeeper { // GetTxConfig implements the TestingApp interface. func (app *SimApp) GetTxConfig() client.TxConfig { - return MakeTestEncodingConfig().TxConfig + return app.txConfig } // SimulationManager implements the SimulationApp interface @@ -760,18 +827,17 @@ func (app *SimApp) SimulationManager() *module.SimulationManager { // API server. func (app *SimApp) RegisterAPIRoutes(apiSvr *api.Server, apiConfig config.APIConfig) { clientCtx := apiSvr.ClientCtx - rpc.RegisterRoutes(clientCtx, apiSvr.Router) - // Register legacy tx routes. - authrest.RegisterTxRoutes(clientCtx, apiSvr.Router) // Register new tx routes from grpc-gateway. authtx.RegisterGRPCGatewayRoutes(clientCtx, apiSvr.GRPCGatewayRouter) // Register new tendermint queries routes from grpc-gateway. tmservice.RegisterGRPCGatewayRoutes(clientCtx, apiSvr.GRPCGatewayRouter) // Register legacy and grpc-gateway routes for all modules. - ModuleBasics.RegisterRESTRoutes(clientCtx, apiSvr.Router) ModuleBasics.RegisterGRPCGatewayRoutes(clientCtx, apiSvr.GRPCGatewayRouter) + // Register nodeservice grpc-gateway routes. + nodeservice.RegisterGRPCGatewayRoutes(clientCtx, apiSvr.GRPCGatewayRouter) + // register swagger API from root so that other applications can override easily if apiConfig.Swagger { RegisterSwaggerAPI(clientCtx, apiSvr.Router) @@ -785,7 +851,16 @@ func (app *SimApp) RegisterTxService(clientCtx client.Context) { // RegisterTendermintService implements the Application.RegisterTendermintService method. func (app *SimApp) RegisterTendermintService(clientCtx client.Context) { - tmservice.RegisterTendermintService(app.BaseApp.GRPCQueryRouter(), clientCtx, app.interfaceRegistry) + tmservice.RegisterTendermintService( + clientCtx, + app.BaseApp.GRPCQueryRouter(), + app.interfaceRegistry, + app.Query, + ) +} + +func (app *SimApp) RegisterNodeService(clientCtx client.Context) { + nodeservice.RegisterNodeService(clientCtx, app.GRPCQueryRouter()) } // RegisterSwaggerAPI registers swagger route with API Server @@ -808,22 +883,91 @@ func GetMaccPerms() map[string][]string { return dupMaccPerms } +// ModuleAccountAddrsLegacy returns all the app's module account addresses. +func ModuleAccountAddrsLegacy() map[string]bool { + modAccAddrs := make(map[string]bool) + for acc := range GetMaccPerms() { + modAccAddrs[authtypes.NewModuleAddress(acc).String()] = true + } + + return modAccAddrs +} + +func BlockedAddresses() map[string]bool { + modAccAddrs := ModuleAccountAddrsLegacy() + + // allow the following addresses to receive funds + delete(modAccAddrs, authtypes.NewModuleAddress(govtypes.ModuleName).String()) + delete(modAccAddrs, authtypes.NewModuleAddress(ibcmock.ModuleName).String()) + + return modAccAddrs +} + // initParamsKeeper init params keeper and its subspaces -func initParamsKeeper(appCodec codec.BinaryCodec, legacyAmino *codec.LegacyAmino, key, tkey sdk.StoreKey) paramskeeper.Keeper { +func initParamsKeeper(appCodec codec.BinaryCodec, legacyAmino *codec.LegacyAmino, key, tkey storetypes.StoreKey) paramskeeper.Keeper { paramsKeeper := paramskeeper.NewKeeper(appCodec, legacyAmino, key, tkey) - paramsKeeper.Subspace(authtypes.ModuleName) - paramsKeeper.Subspace(banktypes.ModuleName) - paramsKeeper.Subspace(stakingtypes.ModuleName) - paramsKeeper.Subspace(minttypes.ModuleName) - paramsKeeper.Subspace(distrtypes.ModuleName) - paramsKeeper.Subspace(slashingtypes.ModuleName) - paramsKeeper.Subspace(govtypes.ModuleName).WithKeyTable(govtypes.ParamKeyTable()) - paramsKeeper.Subspace(crisistypes.ModuleName) + // TODO: ibc module subspaces can be removed after migration of params + // https://github.com/cosmos/ibc-go/issues/2010 paramsKeeper.Subspace(ibctransfertypes.ModuleName) - paramsKeeper.Subspace(ibchost.ModuleName) + paramsKeeper.Subspace(ibcexported.ModuleName) paramsKeeper.Subspace(icacontrollertypes.SubModuleName) paramsKeeper.Subspace(icahosttypes.SubModuleName) return paramsKeeper } + +// setupUpgradeHandlers sets all necessary upgrade handlers for testing purposes +func (app *SimApp) setupUpgradeHandlers() { + app.UpgradeKeeper.SetUpgradeHandler( + simappupgrades.DefaultUpgradeName, + simappupgrades.CreateDefaultUpgradeHandler(app.mm, app.configurator), + ) + + // NOTE: The moduleName arg of v6.CreateUpgradeHandler refers to the auth module ScopedKeeper name to which the channel capability should be migrated from. + // This should be the same string value provided upon instantiation of the ScopedKeeper with app.CapabilityKeeper.ScopeToModule() + // See: https://github.com/cosmos/ibc-go/blob/v6.1.0/testing/simapp/app.go#L310 + app.UpgradeKeeper.SetUpgradeHandler( + v6.UpgradeName, + v6.CreateUpgradeHandler( + app.mm, + app.configurator, + app.appCodec, + app.keys[capabilitytypes.ModuleName], + app.CapabilityKeeper, + ibcmock.ModuleName+icacontrollertypes.SubModuleName, + ), + ) + + app.UpgradeKeeper.SetUpgradeHandler( + v7.UpgradeName, + v7.CreateUpgradeHandler( + app.mm, + app.configurator, + app.appCodec, + app.IBCKeeper.ClientKeeper, + app.ConsensusParamsKeeper, + app.ParamsKeeper, + ), + ) +} + +// setupUpgradeStoreLoaders sets all necessary store loaders required by upgrades. +func (app *SimApp) setupUpgradeStoreLoaders() { + upgradeInfo, err := app.UpgradeKeeper.ReadUpgradeInfoFromDisk() + if err != nil { + tmos.Exit(fmt.Sprintf("failed to read upgrade info from disk %s", err)) + } + + if upgradeInfo.Name == v7.UpgradeName && !app.UpgradeKeeper.IsSkipHeight(upgradeInfo.Height) { + storeUpgrades := storetypes.StoreUpgrades{ + Added: []string{ + consensusparamtypes.StoreKey, + crisistypes.StoreKey, + }, + } + + // configure store loader that checks if version == upgradeHeight and applies store upgrades + app.SetStoreLoader(upgradetypes.UpgradeStoreLoader(upgradeInfo.Height, &storeUpgrades)) + } +} diff --git a/testing/simapp/encoding.go b/testing/simapp/encoding.go index c13124c5c7b..ff03bcd5ed6 100644 --- a/testing/simapp/encoding.go +++ b/testing/simapp/encoding.go @@ -3,7 +3,7 @@ package simapp import ( "github.com/cosmos/cosmos-sdk/std" - simappparams "github.com/cosmos/ibc-go/v4/testing/simapp/params" + simappparams "github.com/cosmos/ibc-go/v7/testing/simapp/params" ) // MakeTestEncodingConfig creates an EncodingConfig for testing. This function diff --git a/testing/simapp/export.go b/testing/simapp/export.go index fba37920697..3d436bc4b6d 100644 --- a/testing/simapp/export.go +++ b/testing/simapp/export.go @@ -45,7 +45,7 @@ func (app *SimApp) ExportAppStateAndValidators( // prepare for fresh start at zero height // NOTE zero height genesis is a temporary feature which will be deprecated -// in favour of export at a block height +// in favour of export at a block height func (app *SimApp) prepForZeroHeightGenesis(ctx sdk.Context, jailAllowedAddrs []string) { applyAllowedAddrs := false @@ -108,8 +108,8 @@ func (app *SimApp) prepForZeroHeightGenesis(ctx sdk.Context, jailAllowedAddrs [] feePool.CommunityPool = feePool.CommunityPool.Add(scraps...) app.DistrKeeper.SetFeePool(ctx, feePool) - app.DistrKeeper.Hooks().AfterValidatorCreated(ctx, val.GetOperator()) - return false + err := app.DistrKeeper.Hooks().AfterValidatorCreated(ctx, val.GetOperator()) + return err != nil }) // reinitialize all delegations @@ -122,8 +122,14 @@ func (app *SimApp) prepForZeroHeightGenesis(ctx sdk.Context, jailAllowedAddrs [] if err != nil { panic(err) } - app.DistrKeeper.Hooks().BeforeDelegationCreated(ctx, delAddr, valAddr) - app.DistrKeeper.Hooks().AfterDelegationModified(ctx, delAddr, valAddr) + err = app.DistrKeeper.Hooks().BeforeDelegationCreated(ctx, delAddr, valAddr) + if err != nil { + panic(err) + } + err = app.DistrKeeper.Hooks().AfterDelegationModified(ctx, delAddr, valAddr) + if err != nil { + panic(err) + } } // reset context height diff --git a/testing/simapp/genesis_account_test.go b/testing/simapp/genesis_account_test.go index f4595c0fdf0..3f88fd435c5 100644 --- a/testing/simapp/genesis_account_test.go +++ b/testing/simapp/genesis_account_test.go @@ -10,7 +10,7 @@ import ( "github.com/stretchr/testify/require" "github.com/tendermint/tendermint/crypto" - "github.com/cosmos/ibc-go/v4/testing/simapp" + "github.com/cosmos/ibc-go/v7/testing/simapp" ) func TestSimGenesisAccountValidate(t *testing.T) { diff --git a/testing/simapp/helpers/test_helpers.go b/testing/simapp/helpers/test_helpers.go deleted file mode 100644 index 9ccecbd976c..00000000000 --- a/testing/simapp/helpers/test_helpers.go +++ /dev/null @@ -1,80 +0,0 @@ -package helpers - -import ( - "math/rand" - "time" - - "github.com/cosmos/cosmos-sdk/client" - cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/types/simulation" - "github.com/cosmos/cosmos-sdk/types/tx/signing" - authsign "github.com/cosmos/cosmos-sdk/x/auth/signing" -) - -// SimAppChainID hardcoded chainID for simulation -const ( - DefaultGenTxGas = 1000000 - SimAppChainID = "simulation-app" -) - -// GenTx generates a signed mock transaction. -func GenTx(gen client.TxConfig, msgs []sdk.Msg, feeAmt sdk.Coins, gas uint64, chainID string, accNums, accSeqs []uint64, priv ...cryptotypes.PrivKey) (sdk.Tx, error) { - sigs := make([]signing.SignatureV2, len(priv)) - - // create a random length memo - r := rand.New(rand.NewSource(time.Now().UnixNano())) - - memo := simulation.RandStringOfLength(r, simulation.RandIntBetween(r, 0, 100)) - - signMode := gen.SignModeHandler().DefaultMode() - - // 1st round: set SignatureV2 with empty signatures, to set correct - // signer infos. - for i, p := range priv { - sigs[i] = signing.SignatureV2{ - PubKey: p.PubKey(), - Data: &signing.SingleSignatureData{ - SignMode: signMode, - }, - Sequence: accSeqs[i], - } - } - - tx := gen.NewTxBuilder() - err := tx.SetMsgs(msgs...) - if err != nil { - return nil, err - } - err = tx.SetSignatures(sigs...) - if err != nil { - return nil, err - } - tx.SetMemo(memo) - tx.SetFeeAmount(feeAmt) - tx.SetGasLimit(gas) - - // 2nd round: once all signer infos are set, every signer can sign. - for i, p := range priv { - signerData := authsign.SignerData{ - ChainID: chainID, - AccountNumber: accNums[i], - Sequence: accSeqs[i], - } - signBytes, err := gen.SignModeHandler().GetSignBytes(signMode, signerData, tx.GetTx()) - if err != nil { - panic(err) - } - sig, err := p.Sign(signBytes) - if err != nil { - panic(err) - } - sigs[i].Data.(*signing.SingleSignatureData).Signature = sig - err = tx.SetSignatures(sigs...) - if err != nil { - panic(err) - } - } - - return tx.GetTx(), nil -} diff --git a/testing/simapp/params/amino.go b/testing/simapp/params/amino.go index 0d43dd1eb8f..d603987dd5f 100644 --- a/testing/simapp/params/amino.go +++ b/testing/simapp/params/amino.go @@ -6,7 +6,7 @@ package params import ( "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/codec/types" - "github.com/cosmos/cosmos-sdk/x/auth/legacy/legacytx" + "github.com/cosmos/cosmos-sdk/x/auth/migrations/legacytx" ) // MakeTestEncodingConfig creates an EncodingConfig for an amino based test configuration. diff --git a/testing/simapp/params/doc.go b/testing/simapp/params/doc.go index 1c721342a9c..bef67c83b3d 100644 --- a/testing/simapp/params/doc.go +++ b/testing/simapp/params/doc.go @@ -13,7 +13,7 @@ file with the weights defined for each of the transaction operations: "op_weight_msg_delegate": 100, } -In the example above, the `MsgSend` has 60% chance to be simulated, while the -`MsgDelegate` will always be simulated. +In the example above, the MsgSend has 60% chance to be simulated, while the +MsgDelegate will always be simulated. */ package params diff --git a/testing/simapp/sim_bench_test.go b/testing/simapp/sim_bench_test.go index 26c2223c543..8cdecd5c331 100644 --- a/testing/simapp/sim_bench_test.go +++ b/testing/simapp/sim_bench_test.go @@ -5,13 +5,14 @@ import ( "os" "testing" + simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" simtypes "github.com/cosmos/cosmos-sdk/types/simulation" "github.com/cosmos/cosmos-sdk/x/simulation" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" ) // Profile with: -// /usr/local/go/bin/go test -benchmem -run=^$ github.com/cosmos/ibc-go/v4/testing/simapp -bench ^BenchmarkFullAppSimulation$ -Commit=true -cpuprofile cpu.out +// /usr/local/go/bin/go test -benchmem -run=^$ github.com/cosmos/ibc-go/v7/testing/simapp -bench ^BenchmarkFullAppSimulation$ -Commit=true -cpuprofile cpu.out func BenchmarkFullAppSimulation(b *testing.B) { b.ReportAllocs() config, db, dir, logger, _, err := SetupSimulation("goleveldb-app-sim", "Simulation") @@ -27,7 +28,7 @@ func BenchmarkFullAppSimulation(b *testing.B) { } }() - app := NewSimApp(logger, db, nil, true, map[int64]bool{}, DefaultNodeHome, FlagPeriodValue, MakeTestEncodingConfig(), EmptyAppOptions{}, interBlockCacheOpt()) + app := NewSimApp(logger, db, nil, true, map[int64]bool{}, DefaultNodeHome, FlagPeriodValue, MakeTestEncodingConfig(), simtestutil.EmptyAppOptions{}, interBlockCacheOpt()) // run randomized simulation _, simParams, simErr := simulation.SimulateFromSeed( @@ -73,7 +74,7 @@ func BenchmarkInvariants(b *testing.B) { } }() - app := NewSimApp(logger, db, nil, true, map[int64]bool{}, DefaultNodeHome, FlagPeriodValue, MakeTestEncodingConfig(), EmptyAppOptions{}, interBlockCacheOpt()) + app := NewSimApp(logger, db, nil, true, map[int64]bool{}, DefaultNodeHome, FlagPeriodValue, MakeTestEncodingConfig(), simtestutil.EmptyAppOptions{}, interBlockCacheOpt()) // run randomized simulation _, simParams, simErr := simulation.SimulateFromSeed( diff --git a/testing/simapp/sim_test.go b/testing/simapp/sim_test.go index 7935213c9b3..5c9dc1cddde 100644 --- a/testing/simapp/sim_test.go +++ b/testing/simapp/sim_test.go @@ -9,6 +9,8 @@ import ( "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/store" + storetypes "github.com/cosmos/cosmos-sdk/store/types" + simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" sdk "github.com/cosmos/cosmos-sdk/types" simtypes "github.com/cosmos/cosmos-sdk/types/simulation" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" @@ -29,9 +31,8 @@ import ( tmproto "github.com/tendermint/tendermint/proto/tendermint/types" dbm "github.com/tendermint/tm-db" - ibctransfertypes "github.com/cosmos/ibc-go/v4/modules/apps/transfer/types" - ibchost "github.com/cosmos/ibc-go/v4/modules/core/24-host" - "github.com/cosmos/ibc-go/v4/testing/simapp/helpers" + ibctransfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" + ibcexported "github.com/cosmos/ibc-go/v7/modules/core/exported" ) // Get flags every time the simulator is run @@ -40,8 +41,8 @@ func init() { } type StoreKeysPrefixes struct { - A sdk.StoreKey - B sdk.StoreKey + A storetypes.StoreKey + B storetypes.StoreKey Prefixes [][]byte } @@ -69,7 +70,7 @@ func TestFullAppSimulation(t *testing.T) { require.NoError(t, os.RemoveAll(dir)) }() - app := NewSimApp(logger, db, nil, true, map[int64]bool{}, DefaultNodeHome, FlagPeriodValue, MakeTestEncodingConfig(), EmptyAppOptions{}, fauxMerkleModeOpt) + app := NewSimApp(logger, db, nil, true, map[int64]bool{}, DefaultNodeHome, FlagPeriodValue, MakeTestEncodingConfig(), simtestutil.EmptyAppOptions{}, fauxMerkleModeOpt) require.Equal(t, "SimApp", app.Name()) // run randomized simulation @@ -107,7 +108,7 @@ func TestAppImportExport(t *testing.T) { require.NoError(t, os.RemoveAll(dir)) }() - app := NewSimApp(logger, db, nil, true, map[int64]bool{}, DefaultNodeHome, FlagPeriodValue, MakeTestEncodingConfig(), EmptyAppOptions{}, fauxMerkleModeOpt) + app := NewSimApp(logger, db, nil, true, map[int64]bool{}, DefaultNodeHome, FlagPeriodValue, MakeTestEncodingConfig(), simtestutil.EmptyAppOptions{}, fauxMerkleModeOpt) require.Equal(t, "SimApp", app.Name()) // Run randomized simulation @@ -147,7 +148,7 @@ func TestAppImportExport(t *testing.T) { require.NoError(t, os.RemoveAll(newDir)) }() - newApp := NewSimApp(log.NewNopLogger(), newDB, nil, true, map[int64]bool{}, DefaultNodeHome, FlagPeriodValue, MakeTestEncodingConfig(), EmptyAppOptions{}, fauxMerkleModeOpt) + newApp := NewSimApp(log.NewNopLogger(), newDB, nil, true, map[int64]bool{}, DefaultNodeHome, FlagPeriodValue, MakeTestEncodingConfig(), simtestutil.EmptyAppOptions{}, fauxMerkleModeOpt) require.Equal(t, "SimApp", newApp.Name()) var genesisState GenesisState @@ -178,7 +179,7 @@ func TestAppImportExport(t *testing.T) { {app.keys[govtypes.StoreKey], newApp.keys[govtypes.StoreKey], [][]byte{}}, {app.keys[evidencetypes.StoreKey], newApp.keys[evidencetypes.StoreKey], [][]byte{}}, {app.keys[capabilitytypes.StoreKey], newApp.keys[capabilitytypes.StoreKey], [][]byte{}}, - {app.keys[ibchost.StoreKey], newApp.keys[ibchost.StoreKey], [][]byte{}}, + {app.keys[ibcexported.StoreKey], newApp.keys[ibcexported.StoreKey], [][]byte{}}, {app.keys[ibctransfertypes.StoreKey], newApp.keys[ibctransfertypes.StoreKey], [][]byte{}}, {app.keys[authzkeeper.StoreKey], newApp.keys[authzkeeper.StoreKey], [][]byte{}}, } @@ -207,7 +208,7 @@ func TestAppSimulationAfterImport(t *testing.T) { require.NoError(t, os.RemoveAll(dir)) }() - app := NewSimApp(logger, db, nil, true, map[int64]bool{}, DefaultNodeHome, FlagPeriodValue, MakeTestEncodingConfig(), EmptyAppOptions{}, fauxMerkleModeOpt) + app := NewSimApp(logger, db, nil, true, map[int64]bool{}, DefaultNodeHome, FlagPeriodValue, MakeTestEncodingConfig(), simtestutil.EmptyAppOptions{}, fauxMerkleModeOpt) require.Equal(t, "SimApp", app.Name()) // Run randomized simulation @@ -252,7 +253,7 @@ func TestAppSimulationAfterImport(t *testing.T) { require.NoError(t, os.RemoveAll(newDir)) }() - newApp := NewSimApp(log.NewNopLogger(), newDB, nil, true, map[int64]bool{}, DefaultNodeHome, FlagPeriodValue, MakeTestEncodingConfig(), EmptyAppOptions{}, fauxMerkleModeOpt) + newApp := NewSimApp(log.NewNopLogger(), newDB, nil, true, map[int64]bool{}, DefaultNodeHome, FlagPeriodValue, MakeTestEncodingConfig(), simtestutil.EmptyAppOptions{}, fauxMerkleModeOpt) require.Equal(t, "SimApp", newApp.Name()) newApp.InitChain(abci.RequestInitChain{ @@ -285,7 +286,7 @@ func TestAppStateDeterminism(t *testing.T) { config.ExportParamsPath = "" config.OnOperation = false config.AllInvariants = false - config.ChainID = helpers.SimAppChainID + config.ChainID = "simulation-app" numSeeds := 3 numTimesToRunPerSeed := 5 @@ -303,7 +304,7 @@ func TestAppStateDeterminism(t *testing.T) { } db := dbm.NewMemDB() - app := NewSimApp(logger, db, nil, true, map[int64]bool{}, DefaultNodeHome, FlagPeriodValue, MakeTestEncodingConfig(), EmptyAppOptions{}, interBlockCacheOpt()) + app := NewSimApp(logger, db, nil, true, map[int64]bool{}, DefaultNodeHome, FlagPeriodValue, MakeTestEncodingConfig(), simtestutil.EmptyAppOptions{}, interBlockCacheOpt()) fmt.Printf( "running non-determinism simulation; seed %d: %d/%d, attempt: %d/%d\n", diff --git a/testing/simapp/simd/cmd/cmd_test.go b/testing/simapp/simd/cmd/cmd_test.go index 5108d3c0a2e..4ac8843035a 100644 --- a/testing/simapp/simd/cmd/cmd_test.go +++ b/testing/simapp/simd/cmd/cmd_test.go @@ -8,8 +8,8 @@ import ( "github.com/cosmos/cosmos-sdk/x/genutil/client/cli" "github.com/stretchr/testify/require" - "github.com/cosmos/ibc-go/v4/testing/simapp" - "github.com/cosmos/ibc-go/v4/testing/simapp/simd/cmd" + "github.com/cosmos/ibc-go/v7/testing/simapp" + "github.com/cosmos/ibc-go/v7/testing/simapp/simd/cmd" ) func TestInitCmd(t *testing.T) { @@ -20,5 +20,5 @@ func TestInitCmd(t *testing.T) { fmt.Sprintf("--%s=%s", cli.FlagOverwrite, "true"), // Overwrite genesis.json, in case it already exists }) - require.NoError(t, svrcmd.Execute(rootCmd, simapp.DefaultNodeHome)) + require.NoError(t, svrcmd.Execute(rootCmd, "simd", simapp.DefaultNodeHome)) } diff --git a/testing/simapp/simd/cmd/genaccounts.go b/testing/simapp/simd/cmd/genaccounts.go index b31b1c20fb4..0b25662d093 100644 --- a/testing/simapp/simd/cmd/genaccounts.go +++ b/testing/simapp/simd/cmd/genaccounts.go @@ -8,7 +8,6 @@ import ( "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/crypto/keyring" "github.com/cosmos/cosmos-sdk/server" sdk "github.com/cosmos/cosmos-sdk/types" @@ -39,31 +38,36 @@ contain valid denominations. Accounts may optionally be supplied with vesting pa Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { clientCtx := client.GetClientContextFromCmd(cmd) - depCdc := clientCtx.JSONCodec - cdc := depCdc.(codec.Codec) - serverCtx := server.GetServerContextFromCmd(cmd) config := serverCtx.Config config.SetRoot(clientCtx.HomeDir) + var kr keyring.Keyring addr, err := sdk.AccAddressFromBech32(args[0]) if err != nil { inBuf := bufio.NewReader(cmd.InOrStdin()) keyringBackend, _ := cmd.Flags().GetString(flags.FlagKeyringBackend) - // attempt to lookup address from Keybase if no address was provided - kb, err := keyring.New(sdk.KeyringServiceName(), keyringBackend, clientCtx.HomeDir, inBuf) - if err != nil { - return err + if keyringBackend != "" && clientCtx.Keyring == nil { + var err error + kr, err = keyring.New(sdk.KeyringServiceName(), keyringBackend, clientCtx.HomeDir, inBuf, clientCtx.Codec) + if err != nil { + return err + } + } else { + kr = clientCtx.Keyring } - info, err := kb.Key(args[0]) + k, err := kr.Key(args[0]) if err != nil { - return fmt.Errorf("failed to get address from Keybase: %w", err) + return fmt.Errorf("failed to get address from Keyring: %w", err) } - addr = info.GetAddress() + addr, err = k.GetAddress() + if err != nil { + return err + } } coins, err := sdk.ParseCoinsNormalized(args[1]) @@ -118,7 +122,7 @@ contain valid denominations. Accounts may optionally be supplied with vesting pa return fmt.Errorf("failed to unmarshal genesis state: %w", err) } - authGenState := authtypes.GetGenesisStateFromAppState(cdc, appState) + authGenState := authtypes.GetGenesisStateFromAppState(clientCtx.Codec, appState) accs, err := authtypes.UnpackAccounts(authGenState.Accounts) if err != nil { @@ -140,19 +144,19 @@ contain valid denominations. Accounts may optionally be supplied with vesting pa } authGenState.Accounts = genAccs - authGenStateBz, err := cdc.MarshalJSON(&authGenState) + authGenStateBz, err := clientCtx.Codec.MarshalJSON(&authGenState) if err != nil { return fmt.Errorf("failed to marshal auth genesis state: %w", err) } appState[authtypes.ModuleName] = authGenStateBz - bankGenState := banktypes.GetGenesisStateFromAppState(depCdc, appState) + bankGenState := banktypes.GetGenesisStateFromAppState(clientCtx.Codec, appState) bankGenState.Balances = append(bankGenState.Balances, balances) bankGenState.Balances = banktypes.SanitizeGenesisBalances(bankGenState.Balances) bankGenState.Supply = bankGenState.Supply.Add(balances.Coins...) - bankGenStateBz, err := cdc.MarshalJSON(bankGenState) + bankGenStateBz, err := clientCtx.Codec.MarshalJSON(bankGenState) if err != nil { return fmt.Errorf("failed to marshal bank genesis state: %w", err) } diff --git a/testing/simapp/simd/cmd/genaccounts_test.go b/testing/simapp/simd/cmd/genaccounts_test.go index b22f956d4d2..f643174707a 100644 --- a/testing/simapp/simd/cmd/genaccounts_test.go +++ b/testing/simapp/simd/cmd/genaccounts_test.go @@ -16,8 +16,8 @@ import ( "github.com/stretchr/testify/require" "github.com/tendermint/tendermint/libs/log" - "github.com/cosmos/ibc-go/v4/testing/simapp" - simcmd "github.com/cosmos/ibc-go/v4/testing/simapp/simd/cmd" + "github.com/cosmos/ibc-go/v7/testing/simapp" + simcmd "github.com/cosmos/ibc-go/v7/testing/simapp/simd/cmd" ) var testMbm = module.NewBasicManager(genutil.AppModuleBasic{}) @@ -63,7 +63,7 @@ func TestAddGenesisAccountCmd(t *testing.T) { require.NoError(t, err) serverCtx := server.NewContext(viper.New(), cfg, logger) - clientCtx := client.Context{}.WithJSONCodec(appCodec).WithHomeDir(home) + clientCtx := client.Context{}.WithCodec(appCodec).WithHomeDir(home) ctx := context.Background() ctx = context.WithValue(ctx, client.ClientContextKey, &clientCtx) diff --git a/testing/simapp/simd/cmd/root.go b/testing/simapp/simd/cmd/root.go index 219091ea09a..10a43fbb5d2 100644 --- a/testing/simapp/simd/cmd/root.go +++ b/testing/simapp/simd/cmd/root.go @@ -6,6 +6,8 @@ import ( "os" "path/filepath" + sdkcmd "cosmossdk.io/simapp/simd/cmd" + rosettaCmd "cosmossdk.io/tools/rosetta/cmd" "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/config" @@ -17,21 +19,25 @@ import ( serverconfig "github.com/cosmos/cosmos-sdk/server/config" servertypes "github.com/cosmos/cosmos-sdk/server/types" "github.com/cosmos/cosmos-sdk/snapshots" + snapshottypes "github.com/cosmos/cosmos-sdk/snapshots/types" "github.com/cosmos/cosmos-sdk/store" sdk "github.com/cosmos/cosmos-sdk/types" authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli" "github.com/cosmos/cosmos-sdk/x/auth/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" "github.com/cosmos/cosmos-sdk/x/crisis" + "github.com/cosmos/cosmos-sdk/x/genutil" genutilcli "github.com/cosmos/cosmos-sdk/x/genutil/client/cli" + genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" "github.com/spf13/cast" "github.com/spf13/cobra" + tmcfg "github.com/tendermint/tendermint/config" tmcli "github.com/tendermint/tendermint/libs/cli" "github.com/tendermint/tendermint/libs/log" dbm "github.com/tendermint/tm-db" - "github.com/cosmos/ibc-go/v4/testing/simapp" - "github.com/cosmos/ibc-go/v4/testing/simapp/params" + "github.com/cosmos/ibc-go/v7/testing/simapp" + "github.com/cosmos/ibc-go/v7/testing/simapp/params" ) // NewRootCmd creates a new root command for simd. It is called once in the @@ -71,8 +77,9 @@ func NewRootCmd() (*cobra.Command, params.EncodingConfig) { } customAppTemplate, customAppConfig := initAppConfig() + customTMConfig := initTendermintConfig() - return server.InterceptConfigsPreRunHandler(cmd, customAppTemplate, customAppConfig) + return server.InterceptConfigsPreRunHandler(cmd, customAppTemplate, customAppConfig, customTMConfig) }, } @@ -81,6 +88,18 @@ func NewRootCmd() (*cobra.Command, params.EncodingConfig) { return rootCmd, encodingConfig } +// initTendermintConfig helps to override default Tendermint Config values. +// return tmcfg.DefaultConfig if no custom configuration is required for the application. +func initTendermintConfig() *tmcfg.Config { + cfg := tmcfg.DefaultConfig() + + // these values put a higher strain on node memory + // cfg.P2P.MaxNumInboundPeers = 100 + // cfg.P2P.MaxNumOutboundPeers = 40 + + return cfg +} + // initAppConfig helps to override default appConfig template and configs. // return "", nil if no custom configuration is required for the application. func initAppConfig() (string, interface{}) { @@ -140,16 +159,17 @@ lru_size = 0` func initRootCmd(rootCmd *cobra.Command, encodingConfig params.EncodingConfig) { cfg := sdk.GetConfig() cfg.Seal() + gentxModule := simapp.ModuleBasics[genutiltypes.ModuleName].(genutil.AppModuleBasic) rootCmd.AddCommand( genutilcli.InitCmd(simapp.ModuleBasics, simapp.DefaultNodeHome), - genutilcli.CollectGenTxsCmd(banktypes.GenesisBalancesIterator{}, simapp.DefaultNodeHome), + genutilcli.CollectGenTxsCmd(banktypes.GenesisBalancesIterator{}, simapp.DefaultNodeHome, gentxModule.GenTxValidator), genutilcli.MigrateGenesisCmd(), genutilcli.GenTxCmd(simapp.ModuleBasics, encodingConfig.TxConfig, banktypes.GenesisBalancesIterator{}, simapp.DefaultNodeHome), genutilcli.ValidateGenesisCmd(simapp.ModuleBasics), AddGenesisAccountCmd(simapp.DefaultNodeHome), tmcli.NewCompletionCmd(rootCmd, true), - testnetCmd(simapp.ModuleBasics, banktypes.GenesisBalancesIterator{}), + sdkcmd.NewTestnetCmd(simapp.ModuleBasics, banktypes.GenesisBalancesIterator{}), debug.Cmd(), config.Cmd(), ) @@ -166,7 +186,7 @@ func initRootCmd(rootCmd *cobra.Command, encodingConfig params.EncodingConfig) { ) // add rosetta - rootCmd.AddCommand(server.RosettaCommand(encodingConfig.InterfaceRegistry, encodingConfig.Marshaler)) + rootCmd.AddCommand(rosettaCmd.RosettaCommand(encodingConfig.InterfaceRegistry, encodingConfig.Marshaler)) } func addModuleInitFlags(startCmd *cobra.Command) { @@ -246,7 +266,7 @@ func (a appCreator) newApp(logger log.Logger, db dbm.DB, traceStore io.Writer, a } snapshotDir := filepath.Join(cast.ToString(appOpts.Get(flags.FlagHome)), "data", "snapshots") - snapshotDB, err := sdk.NewLevelDB("metadata", snapshotDir) + snapshotDB, err := dbm.NewDB("metadata", server.GetAppDBBackend(appOpts), snapshotDir) if err != nil { panic(err) } @@ -255,6 +275,11 @@ func (a appCreator) newApp(logger log.Logger, db dbm.DB, traceStore io.Writer, a panic(err) } + snapshotOptions := snapshottypes.NewSnapshotOptions( + cast.ToUint64(appOpts.Get(server.FlagStateSyncSnapshotInterval)), + cast.ToUint32(appOpts.Get(server.FlagStateSyncSnapshotKeepRecent)), + ) + return simapp.NewSimApp( logger, db, traceStore, true, skipUpgradeHeights, cast.ToString(appOpts.Get(flags.FlagHome)), @@ -269,9 +294,7 @@ func (a appCreator) newApp(logger log.Logger, db dbm.DB, traceStore io.Writer, a baseapp.SetInterBlockCache(cache), baseapp.SetTrace(cast.ToBool(appOpts.Get(server.FlagTrace))), baseapp.SetIndexEvents(cast.ToStringSlice(appOpts.Get(server.FlagIndexEvents))), - baseapp.SetSnapshotStore(snapshotStore), - baseapp.SetSnapshotInterval(cast.ToUint64(appOpts.Get(server.FlagStateSyncSnapshotInterval))), - baseapp.SetSnapshotKeepRecent(cast.ToUint32(appOpts.Get(server.FlagStateSyncSnapshotKeepRecent))), + baseapp.SetSnapshot(snapshotStore, snapshotOptions), ) } @@ -279,7 +302,7 @@ func (a appCreator) newApp(logger log.Logger, db dbm.DB, traceStore io.Writer, a // and exports state. func (a appCreator) appExport( logger log.Logger, db dbm.DB, traceStore io.Writer, height int64, forZeroHeight bool, jailAllowedAddrs []string, - appOpts servertypes.AppOptions, + appOpts servertypes.AppOptions, modulesToExport []string, ) (servertypes.ExportedApp, error) { var simApp *simapp.SimApp homePath, ok := appOpts.Get(flags.FlagHome).(string) diff --git a/testing/simapp/simd/cmd/testnet.go b/testing/simapp/simd/cmd/testnet.go deleted file mode 100644 index 657393fa915..00000000000 --- a/testing/simapp/simd/cmd/testnet.go +++ /dev/null @@ -1,396 +0,0 @@ -package cmd - -// DONTCOVER - -import ( - "bufio" - "encoding/json" - "fmt" - "net" - "os" - "path/filepath" - - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/client/tx" - "github.com/cosmos/cosmos-sdk/crypto/hd" - "github.com/cosmos/cosmos-sdk/crypto/keyring" - cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" - "github.com/cosmos/cosmos-sdk/server" - srvconfig "github.com/cosmos/cosmos-sdk/server/config" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/types/module" - authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" - "github.com/cosmos/cosmos-sdk/x/genutil" - genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - "github.com/spf13/cobra" - tmconfig "github.com/tendermint/tendermint/config" - tmos "github.com/tendermint/tendermint/libs/os" - tmrand "github.com/tendermint/tendermint/libs/rand" - "github.com/tendermint/tendermint/types" - tmtime "github.com/tendermint/tendermint/types/time" -) - -var ( - flagNodeDirPrefix = "node-dir-prefix" - flagNumValidators = "v" - flagOutputDir = "output-dir" - flagNodeDaemonHome = "node-daemon-home" - flagStartingIPAddress = "starting-ip-address" -) - -// get cmd to initialize all files for tendermint testnet and application -func testnetCmd(mbm module.BasicManager, genBalIterator banktypes.GenesisBalancesIterator) *cobra.Command { - cmd := &cobra.Command{ - Use: "testnet", - Short: "Initialize files for a simapp testnet", - Long: `testnet will create "v" number of directories and populate each with -necessary files (private validator, genesis, config, etc.). - -Note, strict routability for addresses is turned off in the config file. - -Example: - simd testnet --v 4 --output-dir ./output --starting-ip-address 192.168.10.2 - `, - RunE: func(cmd *cobra.Command, _ []string) error { - clientCtx, err := client.GetClientQueryContext(cmd) - if err != nil { - return err - } - - serverCtx := server.GetServerContextFromCmd(cmd) - config := serverCtx.Config - - outputDir, _ := cmd.Flags().GetString(flagOutputDir) - keyringBackend, _ := cmd.Flags().GetString(flags.FlagKeyringBackend) - chainID, _ := cmd.Flags().GetString(flags.FlagChainID) - minGasPrices, _ := cmd.Flags().GetString(server.FlagMinGasPrices) - nodeDirPrefix, _ := cmd.Flags().GetString(flagNodeDirPrefix) - nodeDaemonHome, _ := cmd.Flags().GetString(flagNodeDaemonHome) - startingIPAddress, _ := cmd.Flags().GetString(flagStartingIPAddress) - numValidators, _ := cmd.Flags().GetInt(flagNumValidators) - algo, _ := cmd.Flags().GetString(flags.FlagKeyAlgorithm) - - return InitTestnet( - clientCtx, cmd, config, mbm, genBalIterator, outputDir, chainID, minGasPrices, - nodeDirPrefix, nodeDaemonHome, startingIPAddress, keyringBackend, algo, numValidators, - ) - }, - } - - cmd.Flags().Int(flagNumValidators, 4, "Number of validators to initialize the testnet with") - cmd.Flags().StringP(flagOutputDir, "o", "./mytestnet", "Directory to store initialization data for the testnet") - cmd.Flags().String(flagNodeDirPrefix, "node", "Prefix the directory name for each node with (node results in node0, node1, ...)") - cmd.Flags().String(flagNodeDaemonHome, "simd", "Home directory of the node's daemon configuration") - cmd.Flags().String(flagStartingIPAddress, "192.168.0.1", "Starting IP address (192.168.0.1 results in persistent peers list ID0@192.168.0.1:46656, ID1@192.168.0.2:46656, ...)") - cmd.Flags().String(flags.FlagChainID, "", "genesis file chain-id, if left blank will be randomly created") - cmd.Flags().String(server.FlagMinGasPrices, fmt.Sprintf("0.000006%s", sdk.DefaultBondDenom), "Minimum gas prices to accept for transactions; All fees in a tx must meet this minimum (e.g. 0.01photino,0.001stake)") - cmd.Flags().String(flags.FlagKeyringBackend, flags.DefaultKeyringBackend, "Select keyring's backend (os|file|test)") - cmd.Flags().String(flags.FlagKeyAlgorithm, string(hd.Secp256k1Type), "Key signing algorithm to generate keys for") - - return cmd -} - -const nodeDirPerm = 0o755 - -// Initialize the testnet -func InitTestnet( - clientCtx client.Context, - cmd *cobra.Command, - nodeConfig *tmconfig.Config, - mbm module.BasicManager, - genBalIterator banktypes.GenesisBalancesIterator, - outputDir, - chainID, - minGasPrices, - nodeDirPrefix, - nodeDaemonHome, - startingIPAddress, - keyringBackend, - algoStr string, - numValidators int, -) error { - if chainID == "" { - chainID = "chain-" + tmrand.NewRand().Str(6) - } - - nodeIDs := make([]string, numValidators) - valPubKeys := make([]cryptotypes.PubKey, numValidators) - - simappConfig := srvconfig.DefaultConfig() - simappConfig.MinGasPrices = minGasPrices - simappConfig.API.Enable = true - simappConfig.Telemetry.Enabled = true - simappConfig.Telemetry.PrometheusRetentionTime = 60 - simappConfig.Telemetry.EnableHostnameLabel = false - simappConfig.Telemetry.GlobalLabels = [][]string{{"chain_id", chainID}} - - var ( - genAccounts []authtypes.GenesisAccount - genBalances []banktypes.Balance - genFiles []string - ) - - inBuf := bufio.NewReader(cmd.InOrStdin()) - // generate private keys, node IDs, and initial transactions - for i := 0; i < numValidators; i++ { - nodeDirName := fmt.Sprintf("%s%d", nodeDirPrefix, i) - nodeDir := filepath.Join(outputDir, nodeDirName, nodeDaemonHome) - gentxsDir := filepath.Join(outputDir, "gentxs") - - nodeConfig.SetRoot(nodeDir) - nodeConfig.RPC.ListenAddress = "tcp://0.0.0.0:26657" - - if err := os.MkdirAll(filepath.Join(nodeDir, "config"), nodeDirPerm); err != nil { - _ = os.RemoveAll(outputDir) - return err - } - - nodeConfig.Moniker = nodeDirName - - ip, err := getIP(i, startingIPAddress) - if err != nil { - _ = os.RemoveAll(outputDir) - return err - } - - nodeIDs[i], valPubKeys[i], err = genutil.InitializeNodeValidatorFiles(nodeConfig) - if err != nil { - _ = os.RemoveAll(outputDir) - return err - } - - memo := fmt.Sprintf("%s@%s:26656", nodeIDs[i], ip) - genFiles = append(genFiles, nodeConfig.GenesisFile()) - - kb, err := keyring.New(sdk.KeyringServiceName(), keyringBackend, nodeDir, inBuf) - if err != nil { - return err - } - - keyringAlgos, _ := kb.SupportedAlgorithms() - algo, err := keyring.NewSigningAlgoFromString(algoStr, keyringAlgos) - if err != nil { - return err - } - - addr, secret, err := server.GenerateSaveCoinKey(kb, nodeDirName, true, algo) - if err != nil { - _ = os.RemoveAll(outputDir) - return err - } - - info := map[string]string{"secret": secret} - - cliPrint, err := json.Marshal(info) - if err != nil { - return err - } - - // save private key seed words - if err := writeFile(fmt.Sprintf("%v.json", "key_seed"), nodeDir, cliPrint); err != nil { - return err - } - - accTokens := sdk.TokensFromConsensusPower(1000, sdk.DefaultPowerReduction) - accStakingTokens := sdk.TokensFromConsensusPower(500, sdk.DefaultPowerReduction) - coins := sdk.Coins{ - sdk.NewCoin(fmt.Sprintf("%stoken", nodeDirName), accTokens), - sdk.NewCoin(sdk.DefaultBondDenom, accStakingTokens), - } - - genBalances = append(genBalances, banktypes.Balance{Address: addr.String(), Coins: coins.Sort()}) - genAccounts = append(genAccounts, authtypes.NewBaseAccount(addr, nil, 0, 0)) - - valTokens := sdk.TokensFromConsensusPower(100, sdk.DefaultPowerReduction) - createValMsg, err := stakingtypes.NewMsgCreateValidator( - sdk.ValAddress(addr), - valPubKeys[i], - sdk.NewCoin(sdk.DefaultBondDenom, valTokens), - stakingtypes.NewDescription(nodeDirName, "", "", "", ""), - stakingtypes.NewCommissionRates(sdk.OneDec(), sdk.OneDec(), sdk.OneDec()), - sdk.OneInt(), - ) - if err != nil { - return err - } - - txBuilder := clientCtx.TxConfig.NewTxBuilder() - if err := txBuilder.SetMsgs(createValMsg); err != nil { - return err - } - - txBuilder.SetMemo(memo) - - txFactory := tx.Factory{} - txFactory = txFactory. - WithChainID(chainID). - WithMemo(memo). - WithKeybase(kb). - WithTxConfig(clientCtx.TxConfig) - - if err := tx.Sign(txFactory, nodeDirName, txBuilder, true); err != nil { - return err - } - - txBz, err := clientCtx.TxConfig.TxJSONEncoder()(txBuilder.GetTx()) - if err != nil { - return err - } - - if err := writeFile(fmt.Sprintf("%v.json", nodeDirName), gentxsDir, txBz); err != nil { - return err - } - - srvconfig.WriteConfigFile(filepath.Join(nodeDir, "config/app.toml"), simappConfig) - } - - if err := initGenFiles(clientCtx, mbm, chainID, genAccounts, genBalances, genFiles, numValidators); err != nil { - return err - } - - err := collectGenFiles( - clientCtx, nodeConfig, chainID, nodeIDs, valPubKeys, numValidators, - outputDir, nodeDirPrefix, nodeDaemonHome, genBalIterator, - ) - if err != nil { - return err - } - - cmd.PrintErrf("Successfully initialized %d node directories\n", numValidators) - return nil -} - -func initGenFiles( - clientCtx client.Context, mbm module.BasicManager, chainID string, - genAccounts []authtypes.GenesisAccount, genBalances []banktypes.Balance, - genFiles []string, numValidators int, -) error { - appGenState := mbm.DefaultGenesis(clientCtx.JSONCodec) - - // set the accounts in the genesis state - var authGenState authtypes.GenesisState - clientCtx.JSONCodec.MustUnmarshalJSON(appGenState[authtypes.ModuleName], &authGenState) - - accounts, err := authtypes.PackAccounts(genAccounts) - if err != nil { - return err - } - - authGenState.Accounts = accounts - appGenState[authtypes.ModuleName] = clientCtx.JSONCodec.MustMarshalJSON(&authGenState) - - // set the balances in the genesis state - var bankGenState banktypes.GenesisState - clientCtx.JSONCodec.MustUnmarshalJSON(appGenState[banktypes.ModuleName], &bankGenState) - - bankGenState.Balances = genBalances - appGenState[banktypes.ModuleName] = clientCtx.JSONCodec.MustMarshalJSON(&bankGenState) - - appGenStateJSON, err := json.MarshalIndent(appGenState, "", " ") - if err != nil { - return err - } - - genDoc := types.GenesisDoc{ - ChainID: chainID, - AppState: appGenStateJSON, - Validators: nil, - } - - // generate empty genesis files for each validator and save - for i := 0; i < numValidators; i++ { - if err := genDoc.SaveAs(genFiles[i]); err != nil { - return err - } - } - return nil -} - -func collectGenFiles( - clientCtx client.Context, nodeConfig *tmconfig.Config, chainID string, - nodeIDs []string, valPubKeys []cryptotypes.PubKey, numValidators int, - outputDir, nodeDirPrefix, nodeDaemonHome string, genBalIterator banktypes.GenesisBalancesIterator, -) error { - var appState json.RawMessage - genTime := tmtime.Now() - - for i := 0; i < numValidators; i++ { - nodeDirName := fmt.Sprintf("%s%d", nodeDirPrefix, i) - nodeDir := filepath.Join(outputDir, nodeDirName, nodeDaemonHome) - gentxsDir := filepath.Join(outputDir, "gentxs") - nodeConfig.Moniker = nodeDirName - - nodeConfig.SetRoot(nodeDir) - - nodeID, valPubKey := nodeIDs[i], valPubKeys[i] - initCfg := genutiltypes.NewInitConfig(chainID, gentxsDir, nodeID, valPubKey) - - genDoc, err := types.GenesisDocFromFile(nodeConfig.GenesisFile()) - if err != nil { - return err - } - - nodeAppState, err := genutil.GenAppStateFromConfig(clientCtx.JSONCodec, clientCtx.TxConfig, nodeConfig, initCfg, *genDoc, genBalIterator) - if err != nil { - return err - } - - if appState == nil { - // set the canonical application state (they should not differ) - appState = nodeAppState - } - - genFile := nodeConfig.GenesisFile() - - // overwrite each validator's genesis file to have a canonical genesis time - if err := genutil.ExportGenesisFileWithTime(genFile, chainID, nil, appState, genTime); err != nil { - return err - } - } - - return nil -} - -func getIP(i int, startingIPAddr string) (ip string, err error) { - if len(startingIPAddr) == 0 { - ip, err = server.ExternalIP() - if err != nil { - return "", err - } - return ip, nil - } - return calculateIP(startingIPAddr, i) -} - -func calculateIP(ip string, i int) (string, error) { - ipv4 := net.ParseIP(ip).To4() - if ipv4 == nil { - return "", fmt.Errorf("%v: non ipv4 address", ip) - } - - for j := 0; j < i; j++ { - ipv4[3]++ - } - - return ipv4.String(), nil -} - -func writeFile(name string, dir string, contents []byte) error { - writePath := filepath.Join(dir) - file := filepath.Join(writePath, name) - - err := tmos.EnsureDir(writePath, 0o755) - if err != nil { - return err - } - - err = tmos.WriteFile(file, contents, 0o644) - if err != nil { - return err - } - - return nil -} diff --git a/testing/simapp/simd/main.go b/testing/simapp/simd/main.go index c6450c79f58..07b2ab96848 100644 --- a/testing/simapp/simd/main.go +++ b/testing/simapp/simd/main.go @@ -6,14 +6,14 @@ import ( "github.com/cosmos/cosmos-sdk/server" svrcmd "github.com/cosmos/cosmos-sdk/server/cmd" - "github.com/cosmos/ibc-go/v4/testing/simapp" - "github.com/cosmos/ibc-go/v4/testing/simapp/simd/cmd" + "github.com/cosmos/ibc-go/v7/testing/simapp" + "github.com/cosmos/ibc-go/v7/testing/simapp/simd/cmd" ) func main() { rootCmd, _ := cmd.NewRootCmd() - if err := svrcmd.Execute(rootCmd, simapp.DefaultNodeHome); err != nil { + if err := svrcmd.Execute(rootCmd, "simd", simapp.DefaultNodeHome); err != nil { switch e := err.(type) { case server.ErrorCode: os.Exit(e.Code) diff --git a/testing/simapp/state.go b/testing/simapp/state.go index 0b1830d7c4b..3e8707d40c1 100644 --- a/testing/simapp/state.go +++ b/testing/simapp/state.go @@ -4,10 +4,11 @@ import ( "encoding/json" "fmt" "io" - "io/ioutil" "math/rand" + "os" "time" + "cosmossdk.io/math" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" sdk "github.com/cosmos/cosmos-sdk/types" @@ -19,7 +20,7 @@ import ( tmjson "github.com/tendermint/tendermint/libs/json" tmtypes "github.com/tendermint/tendermint/types" - simappparams "github.com/cosmos/ibc-go/v4/testing/simapp/params" + simappparams "github.com/cosmos/ibc-go/v7/testing/simapp/params" ) // AppStateFn returns the initial application state using a genesis or the simulation parameters. @@ -54,7 +55,7 @@ func AppStateFn(cdc codec.JSONCodec, simManager *module.SimulationManager) simty case config.ParamsFile != "": appParams := make(simtypes.AppParams) - bz, err := ioutil.ReadFile(config.ParamsFile) + bz, err := os.ReadFile(config.ParamsFile) if err != nil { panic(err) } @@ -165,7 +166,7 @@ func AppStateRandomizedFn( Rand: r, GenState: genesisState, Accounts: accs, - InitialStake: initialStake, + InitialStake: math.NewInt(initialStake), NumBonded: numInitiallyBonded, GenTimestamp: genesisTimestamp, } @@ -183,7 +184,7 @@ func AppStateRandomizedFn( // AppStateFromGenesisFileFn util function to generate the genesis AppState // from a genesis.json file. func AppStateFromGenesisFileFn(r io.Reader, cdc codec.JSONCodec, genesisFile string) (tmtypes.GenesisDoc, []simtypes.Account) { - bytes, err := ioutil.ReadFile(genesisFile) + bytes, err := os.ReadFile(genesisFile) if err != nil { panic(err) } diff --git a/testing/simapp/test_helpers.go b/testing/simapp/test_helpers.go index 58e1ac2eb23..3a6ff204a87 100644 --- a/testing/simapp/test_helpers.go +++ b/testing/simapp/test_helpers.go @@ -1,23 +1,22 @@ package simapp import ( - "bytes" - "encoding/hex" "encoding/json" - "fmt" - "strconv" + "math/rand" "testing" "time" bam "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/types/errors" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" - minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" "github.com/stretchr/testify/require" abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/libs/log" @@ -25,13 +24,13 @@ import ( tmtypes "github.com/tendermint/tendermint/types" dbm "github.com/tendermint/tm-db" - "github.com/cosmos/ibc-go/v4/testing/simapp/helpers" + "github.com/cosmos/ibc-go/v7/testing/mock" ) // DefaultConsensusParams defines the default Tendermint consensus params used in // SimApp testing. -var DefaultConsensusParams = &abci.ConsensusParams{ - Block: &abci.BlockParams{ +var DefaultConsensusParams = &tmproto.ConsensusParams{ + Block: &tmproto.BlockParams{ MaxBytes: 200000, MaxGas: 2000000, }, @@ -50,7 +49,7 @@ var DefaultConsensusParams = &abci.ConsensusParams{ func setup(withGenesis bool, invCheckPeriod uint) (*SimApp, GenesisState) { db := dbm.NewMemDB() encCdc := MakeTestEncodingConfig() - app := NewSimApp(log.NewNopLogger(), db, nil, true, map[int64]bool{}, DefaultNodeHome, invCheckPeriod, encCdc, EmptyAppOptions{}) + app := NewSimApp(log.NewNopLogger(), db, nil, true, map[int64]bool{}, DefaultNodeHome, invCheckPeriod, encCdc, simtestutil.EmptyAppOptions{}) if withGenesis { return app, NewDefaultGenesisState(encCdc.Marshaler) } @@ -59,47 +58,37 @@ func setup(withGenesis bool, invCheckPeriod uint) (*SimApp, GenesisState) { // Setup initializes a new SimApp. A Nop logger is set in SimApp. func Setup(isCheckTx bool) *SimApp { - app, genesisState := setup(!isCheckTx, 5) - if !isCheckTx { - // init chain must be called to stop deliverState from being nil - stateBytes, err := json.MarshalIndent(genesisState, "", " ") - if err != nil { - panic(err) - } + privVal := mock.NewPV() + pubKey, _ := privVal.GetPubKey() + + // create validator set with single validator + validator := tmtypes.NewValidator(pubKey, 1) + valSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{validator}) - // Initialize the chain - app.InitChain( - abci.RequestInitChain{ - Validators: []abci.ValidatorUpdate{}, - ConsensusParams: DefaultConsensusParams, - AppStateBytes: stateBytes, - }, - ) + // generate genesis account + senderPrivKey := secp256k1.GenPrivKey() + acc := authtypes.NewBaseAccount(senderPrivKey.PubKey().Address().Bytes(), senderPrivKey.PubKey(), 0, 0) + balance := banktypes.Balance{ + Address: acc.GetAddress().String(), + Coins: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100000000000000))), } + app := SetupWithGenesisValSet(valSet, []authtypes.GenesisAccount{acc}, balance) + return app } -// SetupWithGenesisAccounts initializes a new SimApp with the provided genesis -// accounts and possible balances. -func SetupWithGenesisAccounts(genAccs []authtypes.GenesisAccount, balances ...banktypes.Balance) *SimApp { - app, genesisState := setup(true, 0) - authGenesis := authtypes.NewGenesisState(authtypes.DefaultParams(), genAccs) - genesisState[authtypes.ModuleName] = app.AppCodec().MustMarshalJSON(authGenesis) - - totalSupply := sdk.NewCoins() - for _, b := range balances { - totalSupply = totalSupply.Add(b.Coins...) - } - - bankGenesis := banktypes.NewGenesisState(banktypes.DefaultGenesisState().Params, balances, totalSupply, []banktypes.Metadata{}) - genesisState[banktypes.ModuleName] = app.AppCodec().MustMarshalJSON(bankGenesis) +// SetupWithGenesisValSet initializes a new SimApp with a validator set and genesis accounts +// that also act as delegators. For simplicity, each validator is bonded with a delegation +// of one consensus engine unit in the default token of the simapp from first genesis +// account. A Nop logger is set in SimApp. +func SetupWithGenesisValSet(valSet *tmtypes.ValidatorSet, genAccs []authtypes.GenesisAccount, balances ...banktypes.Balance) *SimApp { + app, genesisState := setup(true, 5) + genesisState = genesisStateWithValSet(app, genesisState, valSet, genAccs, balances...) - stateBytes, err := json.MarshalIndent(genesisState, "", " ") - if err != nil { - panic(err) - } + stateBytes, _ := json.MarshalIndent(genesisState, "", " ") + // init chain will set the validator set and initialize the genesis accounts app.InitChain( abci.RequestInitChain{ Validators: []abci.ValidatorUpdate{}, @@ -108,128 +97,111 @@ func SetupWithGenesisAccounts(genAccs []authtypes.GenesisAccount, balances ...ba }, ) + // commit genesis changes app.Commit() - app.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{Height: app.LastBlockHeight() + 1}}) + app.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{ + Height: app.LastBlockHeight() + 1, + AppHash: app.LastCommitID().Hash, + ValidatorsHash: valSet.Hash(), + NextValidatorsHash: valSet.Hash(), + }}) return app } -type GenerateAccountStrategy func(int) []sdk.AccAddress - -// createRandomAccounts is a strategy used by addTestAddrs() in order to generated addresses in random order. -func createRandomAccounts(accNum int) []sdk.AccAddress { - testAddrs := make([]sdk.AccAddress, accNum) - for i := 0; i < accNum; i++ { - pk := ed25519.GenPrivKey().PubKey() - testAddrs[i] = sdk.AccAddress(pk.Address()) - } - - return testAddrs -} - -// createIncrementalAccounts is a strategy used by addTestAddrs() in order to generated addresses in ascending order. -func createIncrementalAccounts(accNum int) []sdk.AccAddress { - var addresses []sdk.AccAddress - var buffer bytes.Buffer - - // start at 100 so we can make up to 999 test addresses with valid test addresses - for i := 100; i < (accNum + 100); i++ { - numString := strconv.Itoa(i) - buffer.WriteString("A58856F0FD53BF058B4909A21AEC019107BA6") // base address string +func genesisStateWithValSet(app *SimApp, genesisState GenesisState, + valSet *tmtypes.ValidatorSet, genAccs []authtypes.GenesisAccount, + balances ...banktypes.Balance, +) GenesisState { + // set genesis accounts + authGenesis := authtypes.NewGenesisState(authtypes.DefaultParams(), genAccs) + genesisState[authtypes.ModuleName] = app.AppCodec().MustMarshalJSON(authGenesis) - buffer.WriteString(numString) // adding on final two digits to make addresses unique - res, _ := sdk.AccAddressFromHex(buffer.String()) - bech := res.String() - addr, _ := TestAddr(buffer.String(), bech) + validators := make([]stakingtypes.Validator, 0, len(valSet.Validators)) + delegations := make([]stakingtypes.Delegation, 0, len(valSet.Validators)) + + bondAmt := sdk.DefaultPowerReduction + + for _, val := range valSet.Validators { + pk, _ := cryptocodec.FromTmPubKeyInterface(val.PubKey) + pkAny, _ := codectypes.NewAnyWithValue(pk) + validator := stakingtypes.Validator{ + OperatorAddress: sdk.ValAddress(val.Address).String(), + ConsensusPubkey: pkAny, + Jailed: false, + Status: stakingtypes.Bonded, + Tokens: bondAmt, + DelegatorShares: sdk.OneDec(), + Description: stakingtypes.Description{}, + UnbondingHeight: int64(0), + UnbondingTime: time.Unix(0, 0).UTC(), + Commission: stakingtypes.NewCommission(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()), + MinSelfDelegation: sdk.ZeroInt(), + } + validators = append(validators, validator) + delegations = append(delegations, stakingtypes.NewDelegation(genAccs[0].GetAddress(), val.Address.Bytes(), sdk.OneDec())) - addresses = append(addresses, addr) - buffer.Reset() } + // set validators and delegations + stakingGenesis := stakingtypes.NewGenesisState(stakingtypes.DefaultParams(), validators, delegations) + genesisState[stakingtypes.ModuleName] = app.AppCodec().MustMarshalJSON(stakingGenesis) - return addresses -} - -// AddTestAddrsFromPubKeys adds the addresses into the SimApp providing only the public keys. -func AddTestAddrsFromPubKeys(app *SimApp, ctx sdk.Context, pubKeys []cryptotypes.PubKey, accAmt sdk.Int) { - initCoins := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), accAmt)) - - for _, pk := range pubKeys { - initAccountWithCoins(app, ctx, sdk.AccAddress(pk.Address()), initCoins) + totalSupply := sdk.NewCoins() + for _, b := range balances { + // add genesis acc tokens to total supply + totalSupply = totalSupply.Add(b.Coins...) } -} - -// AddTestAddrs constructs and returns accNum amount of accounts with an -// initial balance of accAmt in random order -func AddTestAddrs(app *SimApp, ctx sdk.Context, accNum int, accAmt sdk.Int) []sdk.AccAddress { - return addTestAddrs(app, ctx, accNum, accAmt, createRandomAccounts) -} -// AddTestAddrs constructs and returns accNum amount of accounts with an -// initial balance of accAmt in random order -func AddTestAddrsIncremental(app *SimApp, ctx sdk.Context, accNum int, accAmt sdk.Int) []sdk.AccAddress { - return addTestAddrs(app, ctx, accNum, accAmt, createIncrementalAccounts) -} - -func addTestAddrs(app *SimApp, ctx sdk.Context, accNum int, accAmt sdk.Int, strategy GenerateAccountStrategy) []sdk.AccAddress { - testAddrs := strategy(accNum) - - initCoins := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), accAmt)) - - for _, addr := range testAddrs { - initAccountWithCoins(app, ctx, addr, initCoins) + for range delegations { + // add delegated tokens to total supply + totalSupply = totalSupply.Add(sdk.NewCoin(sdk.DefaultBondDenom, bondAmt)) } - return testAddrs -} + // add bonded amount to bonded pool module account + balances = append(balances, banktypes.Balance{ + Address: authtypes.NewModuleAddress(stakingtypes.BondedPoolName).String(), + Coins: sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, bondAmt)}, + }) -func initAccountWithCoins(app *SimApp, ctx sdk.Context, addr sdk.AccAddress, coins sdk.Coins) { - err := app.BankKeeper.MintCoins(ctx, minttypes.ModuleName, coins) - if err != nil { - panic(err) - } + // update total supply + bankGenesis := banktypes.NewGenesisState(banktypes.DefaultGenesisState().Params, balances, totalSupply, []banktypes.Metadata{}, []banktypes.SendEnabled{}) + genesisState[banktypes.ModuleName] = app.AppCodec().MustMarshalJSON(bankGenesis) - err = app.BankKeeper.SendCoinsFromModuleToAccount(ctx, minttypes.ModuleName, addr, coins) - if err != nil { - panic(err) - } + return genesisState } -// ConvertAddrsToValAddrs converts the provided addresses to ValAddress. -func ConvertAddrsToValAddrs(addrs []sdk.AccAddress) []sdk.ValAddress { - valAddrs := make([]sdk.ValAddress, len(addrs)) +// SetupWithGenesisAccounts initializes a new SimApp with the provided genesis +// accounts and possible balances. +func SetupWithGenesisAccounts(genAccs []authtypes.GenesisAccount, balances ...banktypes.Balance) *SimApp { + app, genesisState := setup(true, 0) + authGenesis := authtypes.NewGenesisState(authtypes.DefaultParams(), genAccs) + genesisState[authtypes.ModuleName] = app.AppCodec().MustMarshalJSON(authGenesis) - for i, addr := range addrs { - valAddrs[i] = sdk.ValAddress(addr) + totalSupply := sdk.NewCoins() + for _, b := range balances { + totalSupply = totalSupply.Add(b.Coins...) } - return valAddrs -} + bankGenesis := banktypes.NewGenesisState(banktypes.DefaultGenesisState().Params, balances, totalSupply, []banktypes.Metadata{}, []banktypes.SendEnabled{}) + genesisState[banktypes.ModuleName] = app.AppCodec().MustMarshalJSON(bankGenesis) -func TestAddr(addr string, bech string) (sdk.AccAddress, error) { - res, err := sdk.AccAddressFromHex(addr) + stateBytes, err := json.MarshalIndent(genesisState, "", " ") if err != nil { - return nil, err - } - bechexpected := res.String() - if bech != bechexpected { - return nil, fmt.Errorf("bech encoding doesn't match reference") + panic(err) } - bechres, err := sdk.AccAddressFromBech32(bech) - if err != nil { - return nil, err - } - if !bytes.Equal(bechres, res) { - return nil, err - } + app.InitChain( + abci.RequestInitChain{ + Validators: []abci.ValidatorUpdate{}, + ConsensusParams: DefaultConsensusParams, + AppStateBytes: stateBytes, + }, + ) - return res, nil -} + app.Commit() + app.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{Height: app.LastBlockHeight() + 1}}) -// CheckBalance checks the balance of an account. -func CheckBalance(t *testing.T, app *SimApp, addr sdk.AccAddress, balances sdk.Coins) { - ctxCheck := app.BaseApp.NewContext(true, tmproto.Header{}) - require.True(t, balances.IsEqual(app.BankKeeper.GetAllBalances(ctxCheck, addr))) + return app } // SignAndDeliver signs and delivers a transaction. No simulation occurs as the @@ -237,110 +209,32 @@ func CheckBalance(t *testing.T, app *SimApp, addr sdk.AccAddress, balances sdk.C // // CONTRACT: BeginBlock must be called before this function. func SignAndDeliver( - t *testing.T, txCfg client.TxConfig, app *bam.BaseApp, header tmproto.Header, msgs []sdk.Msg, - chainID string, accNums, accSeqs []uint64, expSimPass, expPass bool, priv ...cryptotypes.PrivKey, + tb testing.TB, txCfg client.TxConfig, app *bam.BaseApp, msgs []sdk.Msg, + chainID string, accNums, accSeqs []uint64, expPass bool, priv ...cryptotypes.PrivKey, ) (sdk.GasInfo, *sdk.Result, error) { - tx, err := helpers.GenTx( + tx, err := simtestutil.GenSignedMockTx( + rand.New(rand.NewSource(time.Now().UnixNano())), txCfg, msgs, sdk.Coins{sdk.NewInt64Coin(sdk.DefaultBondDenom, 0)}, - helpers.DefaultGenTxGas, + simtestutil.DefaultGenTxGas, chainID, accNums, accSeqs, priv..., ) - require.NoError(t, err) + require.NoError(tb, err) // Simulate a sending a transaction - gInfo, res, err := app.Deliver(txCfg.TxEncoder(), tx) + gInfo, res, err := app.SimDeliver(txCfg.TxEncoder(), tx) if expPass { - require.NoError(t, err) - require.NotNil(t, res) + require.NoError(tb, err) + require.NotNil(tb, res) } else { - require.Error(t, err) - require.Nil(t, res) + require.Error(tb, err) + require.Nil(tb, res) } return gInfo, res, err } - -// GenSequenceOfTxs generates a set of signed transactions of messages, such -// that they differ only by having the sequence numbers incremented between -// every transaction. -func GenSequenceOfTxs(txGen client.TxConfig, msgs []sdk.Msg, accNums []uint64, initSeqNums []uint64, numToGenerate int, priv ...cryptotypes.PrivKey) ([]sdk.Tx, error) { - txs := make([]sdk.Tx, numToGenerate) - var err error - for i := 0; i < numToGenerate; i++ { - txs[i], err = helpers.GenTx( - txGen, - msgs, - sdk.Coins{sdk.NewInt64Coin(sdk.DefaultBondDenom, 0)}, - helpers.DefaultGenTxGas, - "", - accNums, - initSeqNums, - priv..., - ) - if err != nil { - break - } - incrementAllSequenceNumbers(initSeqNums) - } - - return txs, err -} - -func incrementAllSequenceNumbers(initSeqNums []uint64) { - for i := 0; i < len(initSeqNums); i++ { - initSeqNums[i]++ - } -} - -// CreateTestPubKeys returns a total of numPubKeys public keys in ascending order. -func CreateTestPubKeys(numPubKeys int) []cryptotypes.PubKey { - var publicKeys []cryptotypes.PubKey - var buffer bytes.Buffer - - // start at 10 to avoid changing 1 to 01, 2 to 02, etc - for i := 100; i < (numPubKeys + 100); i++ { - numString := strconv.Itoa(i) - buffer.WriteString("0B485CFC0EECC619440448436F8FC9DF40566F2369E72400281454CB552AF") // base pubkey string - buffer.WriteString(numString) // adding on final two digits to make pubkeys unique - publicKeys = append(publicKeys, NewPubKeyFromHex(buffer.String())) - buffer.Reset() - } - - return publicKeys -} - -// NewPubKeyFromHex returns a PubKey from a hex string. -func NewPubKeyFromHex(pk string) (res cryptotypes.PubKey) { - pkBytes, err := hex.DecodeString(pk) - if err != nil { - panic(err) - } - if len(pkBytes) != ed25519.PubKeySize { - panic(errors.Wrap(errors.ErrInvalidPubKey, "invalid pubkey size")) - } - return &ed25519.PubKey{Key: pkBytes} -} - -// EmptyAppOptions is a stub implementing AppOptions -type EmptyAppOptions struct{} - -// Get implements AppOptions -func (ao EmptyAppOptions) Get(o string) interface{} { - return nil -} - -// FundAccount is a utility function that funds an account by minting and sending the coins to the address -// TODO(fdymylja): instead of using the mint module account, which has the permission of minting, create a "faucet" account -func FundAccount(app *SimApp, ctx sdk.Context, addr sdk.AccAddress, amounts sdk.Coins) error { - err := app.BankKeeper.MintCoins(ctx, minttypes.ModuleName, amounts) - if err != nil { - return err - } - return app.BankKeeper.SendCoinsFromModuleToAccount(ctx, minttypes.ModuleName, addr, amounts) -} diff --git a/testing/simapp/upgrades/upgrades.go b/testing/simapp/upgrades/upgrades.go new file mode 100644 index 00000000000..adc81349bdd --- /dev/null +++ b/testing/simapp/upgrades/upgrades.go @@ -0,0 +1,23 @@ +package upgrades + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" +) + +const ( + // DefaultUpgradeName is the default upgrade name used for upgrade tests which do not require special handling. + DefaultUpgradeName = "normal upgrade" +) + +// CreateDefaultUpgradeHandler creates an upgrade handler which can be used for regular upgrade tests +// that do not require special logic +func CreateDefaultUpgradeHandler( + mm *module.Manager, + configurator module.Configurator, +) upgradetypes.UpgradeHandler { + return func(ctx sdk.Context, _ upgradetypes.Plan, vm module.VersionMap) (module.VersionMap, error) { + return mm.RunMigrations(ctx, configurator, vm) + } +} diff --git a/testing/simapp/upgrades/v6/upgrades.go b/testing/simapp/upgrades/v6/upgrades.go new file mode 100644 index 00000000000..953bccf37e9 --- /dev/null +++ b/testing/simapp/upgrades/v6/upgrades.go @@ -0,0 +1,36 @@ +package v6 + +import ( + "github.com/cosmos/cosmos-sdk/codec" + storetypes "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + capabilitykeeper "github.com/cosmos/cosmos-sdk/x/capability/keeper" + upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" + + v6 "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/controller/migrations/v6" +) + +const ( + // UpgradeName defines the on-chain upgrade name for the SimApp v6 upgrade. + UpgradeName = "v6" +) + +// CreateUpgradeHandler creates an upgrade handler for the v6 SimApp upgrade. +// NOTE: The v6.MigrateICS27ChannelCapabiliity function can be omitted if chains do not yet implement an ICS27 controller module +func CreateUpgradeHandler( + mm *module.Manager, + configurator module.Configurator, + cdc codec.BinaryCodec, + capabilityStoreKey *storetypes.KVStoreKey, + capabilityKeeper *capabilitykeeper.Keeper, + moduleName string, +) upgradetypes.UpgradeHandler { + return func(ctx sdk.Context, _ upgradetypes.Plan, vm module.VersionMap) (module.VersionMap, error) { + if err := v6.MigrateICS27ChannelCapability(ctx, cdc, capabilityStoreKey, capabilityKeeper, moduleName); err != nil { + return nil, err + } + + return mm.RunMigrations(ctx, configurator, vm) + } +} diff --git a/testing/simapp/upgrades/v7/upgrades.go b/testing/simapp/upgrades/v7/upgrades.go new file mode 100644 index 00000000000..4621c06339b --- /dev/null +++ b/testing/simapp/upgrades/v7/upgrades.go @@ -0,0 +1,42 @@ +package v7 + +import ( + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + consensusparamskeeper "github.com/cosmos/cosmos-sdk/x/consensus/keeper" + paramskeeper "github.com/cosmos/cosmos-sdk/x/params/keeper" + paramstypes "github.com/cosmos/cosmos-sdk/x/params/types" + upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" + + clientkeeper "github.com/cosmos/ibc-go/v7/modules/core/02-client/keeper" + ibctmmigrations "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint/migrations" +) + +const ( + // UpgradeName defines the on-chain upgrade name for the SimApp v7 upgrade. + UpgradeName = "v7" +) + +// CreateUpgradeHandler creates an upgrade handler for the v7 SimApp upgrade. +func CreateUpgradeHandler( + mm *module.Manager, + configurator module.Configurator, + cdc codec.BinaryCodec, + clientKeeper clientkeeper.Keeper, + consensusParamsKeeper consensusparamskeeper.Keeper, + paramsKeeper paramskeeper.Keeper, +) upgradetypes.UpgradeHandler { + return func(ctx sdk.Context, _ upgradetypes.Plan, vm module.VersionMap) (module.VersionMap, error) { + // OPTIONAL: prune expired tendermint consensus states to save storage space + if _, err := ibctmmigrations.PruneExpiredConsensusStates(ctx, cdc, clientKeeper); err != nil { + return nil, err + } + + legacyBaseAppSubspace := paramsKeeper.Subspace(baseapp.Paramspace).WithKeyTable(paramstypes.ConsensusParamsKeyTable()) + baseapp.MigrateParams(ctx, legacyBaseAppSubspace, &consensusParamsKeeper) + + return mm.RunMigrations(ctx, configurator, vm) + } +} diff --git a/testing/simapp/utils.go b/testing/simapp/utils.go index 668feb3f7f6..313d334408a 100644 --- a/testing/simapp/utils.go +++ b/testing/simapp/utils.go @@ -3,7 +3,7 @@ package simapp import ( "encoding/json" "fmt" - "io/ioutil" + "os" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" @@ -12,8 +12,6 @@ import ( simtypes "github.com/cosmos/cosmos-sdk/types/simulation" "github.com/tendermint/tendermint/libs/log" dbm "github.com/tendermint/tm-db" - - "github.com/cosmos/ibc-go/v4/testing/simapp/helpers" ) // SetupSimulation creates the config, db (levelDB), temporary directory and logger for @@ -25,7 +23,7 @@ func SetupSimulation(dirPrefix, dbName string) (simtypes.Config, dbm.DB, string, } config := NewConfigFromFlags() - config.ChainID = helpers.SimAppChainID + config.ChainID = "simulation-app" var logger log.Logger if FlagVerboseValue { @@ -34,12 +32,12 @@ func SetupSimulation(dirPrefix, dbName string) (simtypes.Config, dbm.DB, string, logger = log.NewNopLogger() } - dir, err := ioutil.TempDir("", dirPrefix) + dir, err := os.MkdirTemp("", dirPrefix) if err != nil { return simtypes.Config{}, nil, "", nil, false, err } - db, err := sdk.NewLevelDB(dbName, dir) + db, err := dbm.NewDB(dbName, dbm.BackendType(config.DBBackend), dir) if err != nil { return simtypes.Config{}, nil, "", nil, false, err } @@ -56,7 +54,7 @@ func SimulationOperations(app App, cdc codec.JSONCodec, config simtypes.Config) } if config.ParamsFile != "" { - bz, err := ioutil.ReadFile(config.ParamsFile) + bz, err := os.ReadFile(config.ParamsFile) if err != nil { panic(err) } @@ -67,8 +65,8 @@ func SimulationOperations(app App, cdc codec.JSONCodec, config simtypes.Config) } } - simState.ParamChanges = app.SimulationManager().GenerateParamChanges(config.Seed) - simState.Contents = app.SimulationManager().GetProposalContents(simState) + //nolint: staticcheck // SA1019: app.SimulationManager().GetProposalContents is deprecated: Use GetProposalMsgs instead. GetProposalContents returns each module's proposal content generator function with their default operation weight and key. + simState.LegacyProposalContents = app.SimulationManager().GetProposalContents(simState) return app.SimulationManager().WeightedOperations(simState) } @@ -84,7 +82,7 @@ func CheckExportSimulation( return err } - if err := ioutil.WriteFile(config.ExportStatePath, []byte(exported.AppState), 0o600); err != nil { + if err := os.WriteFile(config.ExportStatePath, []byte(exported.AppState), 0o600); err != nil { return err } } @@ -96,7 +94,7 @@ func CheckExportSimulation( return err } - if err := ioutil.WriteFile(config.ExportParamsPath, paramsBz, 0o600); err != nil { + if err := os.WriteFile(config.ExportParamsPath, paramsBz, 0o600); err != nil { return err } } diff --git a/testing/solomachine.go b/testing/solomachine.go index a97f63c3919..e63b04ab51b 100644 --- a/testing/solomachine.go +++ b/testing/solomachine.go @@ -2,6 +2,7 @@ package ibctesting import ( "testing" + "time" "github.com/cosmos/cosmos-sdk/codec" codectypes "github.com/cosmos/cosmos-sdk/codec/types" @@ -9,14 +10,25 @@ import ( "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" "github.com/cosmos/cosmos-sdk/crypto/types/multisig" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/tx/signing" "github.com/stretchr/testify/require" - clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" - commitmenttypes "github.com/cosmos/ibc-go/v4/modules/core/23-commitment/types" - host "github.com/cosmos/ibc-go/v4/modules/core/24-host" - "github.com/cosmos/ibc-go/v4/modules/core/exported" - solomachinetypes "github.com/cosmos/ibc-go/v4/modules/light-clients/06-solomachine/types" + transfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + connectiontypes "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + commitmenttypes "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + "github.com/cosmos/ibc-go/v7/modules/core/exported" + solomachine "github.com/cosmos/ibc-go/v7/modules/light-clients/06-solomachine" + ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" +) + +var ( + clientIDSolomachine = "client-on-solomachine" // clientID generated on solo machine side + connectionIDSolomachine = "connection-on-solomachine" // connectionID generated on solo machine side + channelIDSolomachine = "channel-on-solomachine" // channelID generated on solo machine side ) // Solomachine is a testing helper used to simulate a counterparty @@ -82,18 +94,17 @@ func GenerateKeys(t *testing.T, n uint64) ([]cryptotypes.PrivKey, []cryptotypes. return privKeys, pubKeys, pk } -// ClientState returns a new solo machine ClientState instance. Default usage does not allow update -// after governance proposal -func (solo *Solomachine) ClientState() *solomachinetypes.ClientState { - return solomachinetypes.NewClientState(solo.Sequence, solo.ConsensusState(), false) +// ClientState returns a new solo machine ClientState instance. +func (solo *Solomachine) ClientState() *solomachine.ClientState { + return solomachine.NewClientState(solo.Sequence, solo.ConsensusState()) } // ConsensusState returns a new solo machine ConsensusState instance -func (solo *Solomachine) ConsensusState() *solomachinetypes.ConsensusState { +func (solo *Solomachine) ConsensusState() *solomachine.ConsensusState { publicKey, err := codectypes.NewAnyWithValue(solo.PublicKey) require.NoError(solo.t, err) - return &solomachinetypes.ConsensusState{ + return &solomachine.ConsensusState{ PublicKey: publicKey, Diversifier: solo.Diversifier, Timestamp: solo.Time, @@ -105,28 +116,55 @@ func (solo *Solomachine) GetHeight() exported.Height { return clienttypes.NewHeight(0, solo.Sequence) } +// CreateClient creates an on-chain client on the provided chain. +func (solo *Solomachine) CreateClient(chain *TestChain) string { + msgCreateClient, err := clienttypes.NewMsgCreateClient(solo.ClientState(), solo.ConsensusState(), chain.SenderAccount.GetAddress().String()) + require.NoError(solo.t, err) + + res, err := chain.SendMsgs(msgCreateClient) + require.NoError(solo.t, err) + require.NotNil(solo.t, res) + + clientID, err := ParseClientIDFromEvents(res.GetEvents()) + require.NoError(solo.t, err) + + return clientID +} + +// UpdateClient sends a MsgUpdateClient to the provided chain and updates the given clientID. +func (solo *Solomachine) UpdateClient(chain *TestChain, clientID string) { + smHeader := solo.CreateHeader(solo.Diversifier) + msgUpdateClient, err := clienttypes.NewMsgUpdateClient(clientID, smHeader, chain.SenderAccount.GetAddress().String()) + require.NoError(solo.t, err) + + res, err := chain.SendMsgs(msgUpdateClient) + require.NoError(solo.t, err) + require.NotNil(solo.t, res) +} + // CreateHeader generates a new private/public key pair and creates the // necessary signature to construct a valid solo machine header. -func (solo *Solomachine) CreateHeader() *solomachinetypes.Header { +// A new diversifier will be used as well +func (solo *Solomachine) CreateHeader(newDiversifier string) *solomachine.Header { // generate new private keys and signature for header newPrivKeys, newPubKeys, newPubKey := GenerateKeys(solo.t, uint64(len(solo.PrivateKeys))) publicKey, err := codectypes.NewAnyWithValue(newPubKey) require.NoError(solo.t, err) - data := &solomachinetypes.HeaderData{ + data := &solomachine.HeaderData{ NewPubKey: publicKey, - NewDiversifier: solo.Diversifier, + NewDiversifier: newDiversifier, } dataBz, err := solo.cdc.Marshal(data) require.NoError(solo.t, err) - signBytes := &solomachinetypes.SignBytes{ + signBytes := &solomachine.SignBytes{ Sequence: solo.Sequence, Timestamp: solo.Time, Diversifier: solo.Diversifier, - DataType: solomachinetypes.HEADER, + Path: []byte(solomachine.SentinelHeaderPath), Data: dataBz, } @@ -135,83 +173,282 @@ func (solo *Solomachine) CreateHeader() *solomachinetypes.Header { sig := solo.GenerateSignature(bz) - header := &solomachinetypes.Header{ - Sequence: solo.Sequence, + header := &solomachine.Header{ Timestamp: solo.Time, Signature: sig, NewPublicKey: publicKey, - NewDiversifier: solo.Diversifier, + NewDiversifier: newDiversifier, } // assumes successful header update solo.Sequence++ + solo.Time++ solo.PrivateKeys = newPrivKeys solo.PublicKeys = newPubKeys solo.PublicKey = newPubKey + solo.Diversifier = newDiversifier return header } // CreateMisbehaviour constructs testing misbehaviour for the solo machine client // by signing over two different data bytes at the same sequence. -func (solo *Solomachine) CreateMisbehaviour() *solomachinetypes.Misbehaviour { - path := solo.GetClientStatePath("counterparty") - dataOne, err := solomachinetypes.ClientStateDataBytes(solo.cdc, path, solo.ClientState()) +func (solo *Solomachine) CreateMisbehaviour() *solomachine.Misbehaviour { + merklePath := solo.GetClientStatePath("counterparty") + path, err := solo.cdc.Marshal(&merklePath) require.NoError(solo.t, err) - path = solo.GetConsensusStatePath("counterparty", clienttypes.NewHeight(0, 1)) - dataTwo, err := solomachinetypes.ConsensusStateDataBytes(solo.cdc, path, solo.ConsensusState()) + data, err := solo.cdc.Marshal(solo.ClientState()) require.NoError(solo.t, err) - signBytes := &solomachinetypes.SignBytes{ + signBytes := &solomachine.SignBytes{ Sequence: solo.Sequence, Timestamp: solo.Time, Diversifier: solo.Diversifier, - DataType: solomachinetypes.CLIENT, - Data: dataOne, + Path: path, + Data: data, } bz, err := solo.cdc.Marshal(signBytes) require.NoError(solo.t, err) sig := solo.GenerateSignature(bz) - signatureOne := solomachinetypes.SignatureAndData{ + signatureOne := solomachine.SignatureAndData{ Signature: sig, - DataType: solomachinetypes.CLIENT, - Data: dataOne, + Path: path, + Data: data, Timestamp: solo.Time, } // misbehaviour signaturess can have different timestamps solo.Time++ - signBytes = &solomachinetypes.SignBytes{ + merklePath = solo.GetConsensusStatePath("counterparty", clienttypes.NewHeight(0, 1)) + path, err = solo.cdc.Marshal(&merklePath) + require.NoError(solo.t, err) + + data, err = solo.cdc.Marshal(solo.ConsensusState()) + require.NoError(solo.t, err) + + signBytes = &solomachine.SignBytes{ Sequence: solo.Sequence, Timestamp: solo.Time, Diversifier: solo.Diversifier, - DataType: solomachinetypes.CONSENSUS, - Data: dataTwo, + Path: path, + Data: data, } bz, err = solo.cdc.Marshal(signBytes) require.NoError(solo.t, err) sig = solo.GenerateSignature(bz) - signatureTwo := solomachinetypes.SignatureAndData{ + signatureTwo := solomachine.SignatureAndData{ Signature: sig, - DataType: solomachinetypes.CONSENSUS, - Data: dataTwo, + Path: path, + Data: data, Timestamp: solo.Time, } - return &solomachinetypes.Misbehaviour{ - ClientId: solo.ClientID, + return &solomachine.Misbehaviour{ Sequence: solo.Sequence, SignatureOne: &signatureOne, SignatureTwo: &signatureTwo, } } +// ConnOpenInit initializes a connection on the provided chain given a solo machine clientID. +func (solo *Solomachine) ConnOpenInit(chain *TestChain, clientID string) string { + msgConnOpenInit := connectiontypes.NewMsgConnectionOpenInit( + clientID, + clientIDSolomachine, // clientID generated on solo machine side + chain.GetPrefix(), DefaultOpenInitVersion, DefaultDelayPeriod, + chain.SenderAccount.GetAddress().String(), + ) + + res, err := chain.SendMsgs(msgConnOpenInit) + require.NoError(solo.t, err) + require.NotNil(solo.t, res) + + connectionID, err := ParseConnectionIDFromEvents(res.GetEvents()) + require.NoError(solo.t, err) + + return connectionID +} + +// ConnOpenAck performs the connection open ack handshake step on the tendermint chain for the associated +// solo machine client. +func (solo *Solomachine) ConnOpenAck(chain *TestChain, clientID, connectionID string) { + proofTry := solo.GenerateConnOpenTryProof(clientID, connectionID) + + clientState := ibctm.NewClientState(chain.ChainID, DefaultTrustLevel, TrustingPeriod, UnbondingPeriod, MaxClockDrift, chain.LastHeader.GetHeight().(clienttypes.Height), commitmenttypes.GetSDKSpecs(), UpgradePath) + proofClient := solo.GenerateClientStateProof(clientState) + + consensusState := chain.LastHeader.ConsensusState() + consensusHeight := chain.LastHeader.GetHeight() + proofConsensus := solo.GenerateConsensusStateProof(consensusState, consensusHeight) + + msgConnOpenAck := connectiontypes.NewMsgConnectionOpenAck( + connectionID, connectionIDSolomachine, clientState, + proofTry, proofClient, proofConsensus, + clienttypes.ZeroHeight(), clientState.GetLatestHeight().(clienttypes.Height), + ConnectionVersion, + chain.SenderAccount.GetAddress().String(), + ) + + res, err := chain.SendMsgs(msgConnOpenAck) + require.NoError(solo.t, err) + require.NotNil(solo.t, res) +} + +// ChanOpenInit initializes a channel on the provided chain given a solo machine connectionID. +func (solo *Solomachine) ChanOpenInit(chain *TestChain, connectionID string) string { + msgChanOpenInit := channeltypes.NewMsgChannelOpenInit( + transfertypes.PortID, + transfertypes.Version, + channeltypes.UNORDERED, + []string{connectionID}, + transfertypes.PortID, + chain.SenderAccount.GetAddress().String(), + ) + + res, err := chain.SendMsgs(msgChanOpenInit) + require.NoError(solo.t, err) + require.NotNil(solo.t, res) + + if res, ok := res.MsgResponses[0].GetCachedValue().(*channeltypes.MsgChannelOpenInitResponse); ok { + return res.ChannelId + } + + return "" +} + +// ChanOpenAck performs the channel open ack handshake step on the tendermint chain for the associated +// solo machine client. +func (solo *Solomachine) ChanOpenAck(chain *TestChain, channelID string) { + proofTry := solo.GenerateChanOpenTryProof(transfertypes.PortID, transfertypes.Version, channelID) + msgChanOpenAck := channeltypes.NewMsgChannelOpenAck( + transfertypes.PortID, + channelID, + channelIDSolomachine, + transfertypes.Version, + proofTry, + clienttypes.ZeroHeight(), + chain.SenderAccount.GetAddress().String(), + ) + + res, err := chain.SendMsgs(msgChanOpenAck) + require.NoError(solo.t, err) + require.NotNil(solo.t, res) +} + +// ChanCloseConfirm performs the channel close confirm handshake step on the tendermint chain for the associated +// solo machine client. +func (solo *Solomachine) ChanCloseConfirm(chain *TestChain, portID, channelID string) { + proofInit := solo.GenerateChanClosedProof(portID, transfertypes.Version, channelID) + msgChanCloseConfirm := channeltypes.NewMsgChannelCloseConfirm( + portID, + channelID, + proofInit, + clienttypes.ZeroHeight(), + chain.SenderAccount.GetAddress().String(), + ) + + res, err := chain.SendMsgs(msgChanCloseConfirm) + require.NoError(solo.t, err) + require.NotNil(solo.t, res) +} + +// SendTransfer constructs a MsgTransfer and sends the message to the given chain. Any number of optional +// functions can be provided which will modify the MsgTransfer before SendMsgs is called. +func (solo *Solomachine) SendTransfer(chain *TestChain, portID, channelID string, fns ...func(*transfertypes.MsgTransfer)) channeltypes.Packet { + msgTransfer := transfertypes.MsgTransfer{ + SourcePort: portID, + SourceChannel: channelID, + Token: sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100)), + Sender: chain.SenderAccount.GetAddress().String(), + Receiver: chain.SenderAccount.GetAddress().String(), + TimeoutHeight: clienttypes.ZeroHeight(), + TimeoutTimestamp: uint64(chain.GetContext().BlockTime().Add(time.Hour).UnixNano()), + } + + for _, fn := range fns { + fn(&msgTransfer) + } + + res, err := chain.SendMsgs(&msgTransfer) + require.NoError(solo.t, err) + + packet, err := ParsePacketFromEvents(res.GetEvents()) + require.NoError(solo.t, err) + + return packet +} + +// RecvPacket creates a commitment proof and broadcasts a new MsgRecvPacket. +func (solo *Solomachine) RecvPacket(chain *TestChain, packet channeltypes.Packet) { + proofCommitment := solo.GenerateCommitmentProof(packet) + msgRecvPacket := channeltypes.NewMsgRecvPacket( + packet, + proofCommitment, + clienttypes.ZeroHeight(), + chain.SenderAccount.GetAddress().String(), + ) + + res, err := chain.SendMsgs(msgRecvPacket) + require.NoError(solo.t, err) + require.NotNil(solo.t, res) +} + +// AcknowledgePacket creates an acknowledgement proof and broadcasts a MsgAcknowledgement. +func (solo *Solomachine) AcknowledgePacket(chain *TestChain, packet channeltypes.Packet) { + proofAck := solo.GenerateAcknowledgementProof(packet) + transferAck := channeltypes.NewResultAcknowledgement([]byte{byte(1)}).Acknowledgement() + msgAcknowledgement := channeltypes.NewMsgAcknowledgement( + packet, transferAck, + proofAck, + clienttypes.ZeroHeight(), + chain.SenderAccount.GetAddress().String(), + ) + + res, err := chain.SendMsgs(msgAcknowledgement) + require.NoError(solo.t, err) + require.NotNil(solo.t, res) +} + +// TimeoutPacket creates a unreceived packet proof and broadcasts a MsgTimeout. +func (solo *Solomachine) TimeoutPacket(chain *TestChain, packet channeltypes.Packet) { + proofUnreceived := solo.GenerateReceiptAbsenceProof(packet) + msgTimeout := channeltypes.NewMsgTimeout( + packet, + 1, // nextSequenceRecv is unused for UNORDERED channels + proofUnreceived, + clienttypes.ZeroHeight(), + chain.SenderAccount.GetAddress().String(), + ) + + res, err := chain.SendMsgs(msgTimeout) + require.NoError(solo.t, err) + require.NotNil(solo.t, res) +} + +// TimeoutPacket creates a channel closed and unreceived packet proof and broadcasts a MsgTimeoutOnClose. +func (solo *Solomachine) TimeoutPacketOnClose(chain *TestChain, packet channeltypes.Packet, channelID string) { + proofClosed := solo.GenerateChanClosedProof(transfertypes.PortID, transfertypes.Version, channelID) + proofUnreceived := solo.GenerateReceiptAbsenceProof(packet) + msgTimeout := channeltypes.NewMsgTimeoutOnClose( + packet, + 1, // nextSequenceRecv is unused for UNORDERED channels + proofUnreceived, + proofClosed, + clienttypes.ZeroHeight(), + chain.SenderAccount.GetAddress().String(), + ) + + res, err := chain.SendMsgs(msgTimeout) + require.NoError(solo.t, err) + require.NotNil(solo.t, res) +} + // GenerateSignature uses the stored private keys to generate a signature // over the sign bytes with each key. If the amount of keys is greater than // 1 then a multisig data type is returned. @@ -247,6 +484,160 @@ func (solo *Solomachine) GenerateSignature(signBytes []byte) []byte { return bz } +// GenerateProof takes in solo machine sign bytes, generates a signature and marshals it as a proof. +// The solo machine sequence is incremented. +func (solo *Solomachine) GenerateProof(signBytes *solomachine.SignBytes) []byte { + bz, err := solo.cdc.Marshal(signBytes) + require.NoError(solo.t, err) + + sig := solo.GenerateSignature(bz) + signatureDoc := &solomachine.TimestampedSignatureData{ + SignatureData: sig, + Timestamp: solo.Time, + } + proof, err := solo.cdc.Marshal(signatureDoc) + require.NoError(solo.t, err) + + solo.Sequence++ + + return proof +} + +// GenerateClientStateProof generates the proof of the client state required for the connection open try and ack handshake steps. +// The client state should be the self client states of the tendermint chain. +func (solo *Solomachine) GenerateClientStateProof(clientState exported.ClientState) []byte { + data, err := clienttypes.MarshalClientState(solo.cdc, clientState) + require.NoError(solo.t, err) + + signBytes := &solomachine.SignBytes{ + Sequence: solo.Sequence, + Timestamp: solo.Time, + Diversifier: solo.Diversifier, + Path: []byte(solo.GetClientStatePath(clientIDSolomachine).String()), + Data: data, + } + + return solo.GenerateProof(signBytes) +} + +// GenerateConsensusStateProof generates the proof of the consensus state required for the connection open try and ack handshake steps. +// The consensus state should be the self consensus states of the tendermint chain. +func (solo *Solomachine) GenerateConsensusStateProof(consensusState exported.ConsensusState, consensusHeight exported.Height) []byte { + data, err := clienttypes.MarshalConsensusState(solo.cdc, consensusState) + require.NoError(solo.t, err) + + signBytes := &solomachine.SignBytes{ + Sequence: solo.Sequence, + Timestamp: solo.Time, + Diversifier: solo.Diversifier, + Path: []byte(solo.GetConsensusStatePath(clientIDSolomachine, consensusHeight).String()), + Data: data, + } + + return solo.GenerateProof(signBytes) +} + +// GenerateConnOpenTryProof generates the proofTry required for the connection open ack handshake step. +// The clientID, connectionID provided represent the clientID and connectionID created on the counterparty chain, that is the tendermint chain. +func (solo *Solomachine) GenerateConnOpenTryProof(counterpartyClientID, counterpartyConnectionID string) []byte { + counterparty := connectiontypes.NewCounterparty(counterpartyClientID, counterpartyConnectionID, prefix) + connection := connectiontypes.NewConnectionEnd(connectiontypes.TRYOPEN, clientIDSolomachine, counterparty, []*connectiontypes.Version{ConnectionVersion}, DefaultDelayPeriod) + + data, err := solo.cdc.Marshal(&connection) + require.NoError(solo.t, err) + + signBytes := &solomachine.SignBytes{ + Sequence: solo.Sequence, + Timestamp: solo.Time, + Diversifier: solo.Diversifier, + Path: []byte(solo.GetConnectionStatePath(connectionIDSolomachine).String()), + Data: data, + } + + return solo.GenerateProof(signBytes) +} + +// GenerateChanOpenTryProof generates the proofTry required for the channel open ack handshake step. +// The channelID provided represents the channelID created on the counterparty chain, that is the tendermint chain. +func (solo *Solomachine) GenerateChanOpenTryProof(portID, version, counterpartyChannelID string) []byte { + counterparty := channeltypes.NewCounterparty(portID, counterpartyChannelID) + channel := channeltypes.NewChannel(channeltypes.TRYOPEN, channeltypes.UNORDERED, counterparty, []string{connectionIDSolomachine}, version) + + data, err := solo.cdc.Marshal(&channel) + require.NoError(solo.t, err) + + signBytes := &solomachine.SignBytes{ + Sequence: solo.Sequence, + Timestamp: solo.Time, + Diversifier: solo.Diversifier, + Path: []byte(solo.GetChannelStatePath(portID, channelIDSolomachine).String()), + Data: data, + } + + return solo.GenerateProof(signBytes) +} + +// GenerateChanClosedProof generates a channel closed proof. +// The channelID provided represents the channelID created on the counterparty chain, that is the tendermint chain. +func (solo *Solomachine) GenerateChanClosedProof(portID, version, counterpartyChannelID string) []byte { + counterparty := channeltypes.NewCounterparty(portID, counterpartyChannelID) + channel := channeltypes.NewChannel(channeltypes.CLOSED, channeltypes.UNORDERED, counterparty, []string{connectionIDSolomachine}, version) + + data, err := solo.cdc.Marshal(&channel) + require.NoError(solo.t, err) + + signBytes := &solomachine.SignBytes{ + Sequence: solo.Sequence, + Timestamp: solo.Time, + Diversifier: solo.Diversifier, + Path: []byte(solo.GetChannelStatePath(portID, channelIDSolomachine).String()), + Data: data, + } + + return solo.GenerateProof(signBytes) +} + +// GenerateCommitmentProof generates a commitment proof for the provided packet. +func (solo *Solomachine) GenerateCommitmentProof(packet channeltypes.Packet) []byte { + commitment := channeltypes.CommitPacket(solo.cdc, packet) + + signBytes := &solomachine.SignBytes{ + Sequence: solo.Sequence, + Timestamp: solo.Time, + Diversifier: solo.Diversifier, + Path: []byte(solo.GetPacketCommitmentPath(packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()).String()), + Data: commitment, + } + + return solo.GenerateProof(signBytes) +} + +// GenerateAcknowledgementProof generates an acknowledgement proof. +func (solo *Solomachine) GenerateAcknowledgementProof(packet channeltypes.Packet) []byte { + transferAck := channeltypes.NewResultAcknowledgement([]byte{byte(1)}).Acknowledgement() + signBytes := &solomachine.SignBytes{ + Sequence: solo.Sequence, + Timestamp: solo.Time, + Diversifier: solo.Diversifier, + Path: []byte(solo.GetPacketAcknowledgementPath(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()).String()), + Data: channeltypes.CommitAcknowledgement(transferAck), + } + + return solo.GenerateProof(signBytes) +} + +// GenerateReceiptAbsenceProof generates a receipt absence proof for the provided packet. +func (solo *Solomachine) GenerateReceiptAbsenceProof(packet channeltypes.Packet) []byte { + signBytes := &solomachine.SignBytes{ + Sequence: solo.Sequence, + Timestamp: solo.Time, + Diversifier: solo.Diversifier, + Path: []byte(solo.GetPacketReceiptPath(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()).String()), + Data: nil, + } + return solo.GenerateProof(signBytes) +} + // GetClientStatePath returns the commitment path for the client state. func (solo *Solomachine) GetClientStatePath(counterpartyClientIdentifier string) commitmenttypes.MerklePath { path, err := commitmenttypes.ApplyPrefix(prefix, commitmenttypes.NewMerklePath(host.FullClientStatePath(counterpartyClientIdentifier))) @@ -282,8 +673,8 @@ func (solo *Solomachine) GetChannelStatePath(portID, channelID string) commitmen } // GetPacketCommitmentPath returns the commitment path for a packet commitment. -func (solo *Solomachine) GetPacketCommitmentPath(portID, channelID string) commitmenttypes.MerklePath { - commitmentPath := commitmenttypes.NewMerklePath(host.PacketCommitmentPath(portID, channelID, solo.Sequence)) +func (solo *Solomachine) GetPacketCommitmentPath(portID, channelID string, sequence uint64) commitmenttypes.MerklePath { + commitmentPath := commitmenttypes.NewMerklePath(host.PacketCommitmentPath(portID, channelID, sequence)) path, err := commitmenttypes.ApplyPrefix(prefix, commitmentPath) require.NoError(solo.t, err) @@ -291,8 +682,8 @@ func (solo *Solomachine) GetPacketCommitmentPath(portID, channelID string) commi } // GetPacketAcknowledgementPath returns the commitment path for a packet acknowledgement. -func (solo *Solomachine) GetPacketAcknowledgementPath(portID, channelID string) commitmenttypes.MerklePath { - ackPath := commitmenttypes.NewMerklePath(host.PacketAcknowledgementPath(portID, channelID, solo.Sequence)) +func (solo *Solomachine) GetPacketAcknowledgementPath(portID, channelID string, sequence uint64) commitmenttypes.MerklePath { + ackPath := commitmenttypes.NewMerklePath(host.PacketAcknowledgementPath(portID, channelID, sequence)) path, err := commitmenttypes.ApplyPrefix(prefix, ackPath) require.NoError(solo.t, err) @@ -301,8 +692,8 @@ func (solo *Solomachine) GetPacketAcknowledgementPath(portID, channelID string) // GetPacketReceiptPath returns the commitment path for a packet receipt // and an absent receipts. -func (solo *Solomachine) GetPacketReceiptPath(portID, channelID string) commitmenttypes.MerklePath { - receiptPath := commitmenttypes.NewMerklePath(host.PacketReceiptPath(portID, channelID, solo.Sequence)) +func (solo *Solomachine) GetPacketReceiptPath(portID, channelID string, sequence uint64) commitmenttypes.MerklePath { + receiptPath := commitmenttypes.NewMerklePath(host.PacketReceiptPath(portID, channelID, sequence)) path, err := commitmenttypes.ApplyPrefix(prefix, receiptPath) require.NoError(solo.t, err) diff --git a/testing/types/expected_keepers.go b/testing/types/expected_keepers.go new file mode 100644 index 00000000000..07034b57a25 --- /dev/null +++ b/testing/types/expected_keepers.go @@ -0,0 +1,12 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +// StakingKeeper defines the expected staking keeper interface used in the +// IBC testing package +type StakingKeeper interface { + GetHistoricalInfo(ctx sdk.Context, height int64) (stakingtypes.HistoricalInfo, bool) +} diff --git a/testing/utils.go b/testing/utils.go index f9f64bf72bd..b0d810b4cad 100644 --- a/testing/utils.go +++ b/testing/utils.go @@ -10,14 +10,14 @@ import ( // ApplyValSetChanges takes in tmtypes.ValidatorSet and []abci.ValidatorUpdate and will return a new tmtypes.ValidatorSet which has the // provided validator updates applied to the provided validator set. -func ApplyValSetChanges(t *testing.T, valSet *tmtypes.ValidatorSet, valUpdates []abci.ValidatorUpdate) *tmtypes.ValidatorSet { +func ApplyValSetChanges(tb testing.TB, valSet *tmtypes.ValidatorSet, valUpdates []abci.ValidatorUpdate) *tmtypes.ValidatorSet { updates, err := tmtypes.PB2TM.ValidatorUpdates(valUpdates) - require.NoError(t, err) + require.NoError(tb, err) // must copy since validator set will mutate with UpdateWithChangeSet newVals := valSet.Copy() err = newVals.UpdateWithChangeSet(updates) - require.NoError(t, err) + require.NoError(tb, err) return newVals } diff --git a/testing/values.go b/testing/values.go index 481db7915e8..fa69c502069 100644 --- a/testing/values.go +++ b/testing/values.go @@ -1,6 +1,6 @@ /* - This file contains the variables, constants, and default values - used in the testing package and commonly defined in tests. +This file contains the variables, constants, and default values +used in the testing package and commonly defined in tests. */ package ibctesting @@ -9,12 +9,12 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" - ibctransfertypes "github.com/cosmos/ibc-go/v4/modules/apps/transfer/types" - connectiontypes "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types" - commitmenttypes "github.com/cosmos/ibc-go/v4/modules/core/23-commitment/types" - ibctmtypes "github.com/cosmos/ibc-go/v4/modules/light-clients/07-tendermint/types" - "github.com/cosmos/ibc-go/v4/testing/mock" - "github.com/cosmos/ibc-go/v4/testing/simapp" + ibctransfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" + connectiontypes "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" + commitmenttypes "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/types" + ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" + "github.com/cosmos/ibc-go/v7/testing/mock" + "github.com/cosmos/ibc-go/v7/testing/simapp" ) const ( @@ -46,9 +46,12 @@ const ( var ( DefaultOpenInitVersion *connectiontypes.Version - // Default params variables used to create a TM client - DefaultTrustLevel ibctmtypes.Fraction = ibctmtypes.DefaultTrustLevel - TestCoin = sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100)) + // DefaultTrustLevel sets params variables used to create a TM client + DefaultTrustLevel = ibctm.DefaultTrustLevel + + TestAccAddress = "cosmos17dtl0mjt3t77kpuhg2edqzjpszulwhgzuj9ljs" + TestCoin = sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100)) + TestCoins = sdk.NewCoins(TestCoin) UpgradePath = []string{"upgrade", "upgradedIBCState"} diff --git a/third_party/proto/buf.yaml b/third_party/proto/buf.yaml deleted file mode 100644 index b111c46ba08..00000000000 --- a/third_party/proto/buf.yaml +++ /dev/null @@ -1,33 +0,0 @@ -# Generated by "buf config migrate-v1beta1". Edit as necessary, and -# remove this comment when you're finished. -# -# This module represents the "third_party/proto" root found in -# the previous configuration. -version: v1 -build: - excludes: - - google/protobuf -breaking: - use: - - FILE - ignore: - - proofs.proto # confio/ics23 - - gogoproto - - google - - tendermint -lint: - use: - - DEFAULT - - COMMENTS - - FILE_LOWER_SNAKE_CASE - except: - - UNARY_RPC - - COMMENT_FIELD - - SERVICE_SUFFIX - - PACKAGE_VERSION_SUFFIX - - RPC_REQUEST_STANDARD_NAME - ignore: - - proofs.proto # confio/ics23 - - gogoproto - - google - - tendermint \ No newline at end of file diff --git a/third_party/proto/cosmos/auth/v1beta1/auth.proto b/third_party/proto/cosmos/auth/v1beta1/auth.proto deleted file mode 100644 index 79071456f6e..00000000000 --- a/third_party/proto/cosmos/auth/v1beta1/auth.proto +++ /dev/null @@ -1,51 +0,0 @@ -syntax = "proto3"; -package cosmos.auth.v1beta1; - -import "cosmos_proto/cosmos.proto"; -import "gogoproto/gogo.proto"; -import "google/protobuf/any.proto"; - -option go_package = "github.com/cosmos/cosmos-sdk/x/auth/types"; - -// BaseAccount defines a base account type. It contains all the necessary fields -// for basic account functionality. Any custom account type should extend this -// type for additional functionality (e.g. vesting). -message BaseAccount { - option (gogoproto.goproto_getters) = false; - option (gogoproto.goproto_stringer) = false; - option (gogoproto.equal) = false; - - option (cosmos_proto.implements_interface) = "AccountI"; - - string address = 1; - google.protobuf.Any pub_key = 2 - [(gogoproto.jsontag) = "public_key,omitempty", (gogoproto.moretags) = "yaml:\"public_key\""]; - uint64 account_number = 3 [(gogoproto.moretags) = "yaml:\"account_number\""]; - uint64 sequence = 4; -} - -// ModuleAccount defines an account for modules that holds coins on a pool. -message ModuleAccount { - option (gogoproto.goproto_getters) = false; - option (gogoproto.goproto_stringer) = false; - option (cosmos_proto.implements_interface) = "ModuleAccountI"; - - BaseAccount base_account = 1 [(gogoproto.embed) = true, (gogoproto.moretags) = "yaml:\"base_account\""]; - string name = 2; - repeated string permissions = 3; -} - -// Params defines the parameters for the auth module. -message Params { - option (gogoproto.equal) = true; - option (gogoproto.goproto_stringer) = false; - - uint64 max_memo_characters = 1 [(gogoproto.moretags) = "yaml:\"max_memo_characters\""]; - uint64 tx_sig_limit = 2 [(gogoproto.moretags) = "yaml:\"tx_sig_limit\""]; - uint64 tx_size_cost_per_byte = 3 [(gogoproto.moretags) = "yaml:\"tx_size_cost_per_byte\""]; - uint64 sig_verify_cost_ed25519 = 4 - [(gogoproto.customname) = "SigVerifyCostED25519", (gogoproto.moretags) = "yaml:\"sig_verify_cost_ed25519\""]; - uint64 sig_verify_cost_secp256k1 = 5 - [(gogoproto.customname) = "SigVerifyCostSecp256k1", (gogoproto.moretags) = "yaml:\"sig_verify_cost_secp256k1\""]; -} - diff --git a/third_party/proto/cosmos/auth/v1beta1/genesis.proto b/third_party/proto/cosmos/auth/v1beta1/genesis.proto deleted file mode 100644 index 5d6823e6b35..00000000000 --- a/third_party/proto/cosmos/auth/v1beta1/genesis.proto +++ /dev/null @@ -1,18 +0,0 @@ -syntax = "proto3"; -package cosmos.auth.v1beta1; - -import "google/protobuf/any.proto"; -import "gogoproto/gogo.proto"; -import "cosmos/auth/v1beta1/auth.proto"; - -option go_package = "github.com/cosmos/cosmos-sdk/x/auth/types"; - -// GenesisState defines the auth module's genesis state. -message GenesisState { - // params defines all the paramaters of the module. - Params params = 1 [(gogoproto.nullable) = false]; - - // accounts are the accounts present at genesis. - repeated google.protobuf.Any accounts = 2; -} - diff --git a/third_party/proto/cosmos/auth/v1beta1/query.proto b/third_party/proto/cosmos/auth/v1beta1/query.proto deleted file mode 100644 index 2c438c7b5ed..00000000000 --- a/third_party/proto/cosmos/auth/v1beta1/query.proto +++ /dev/null @@ -1,48 +0,0 @@ -syntax = "proto3"; -package cosmos.auth.v1beta1; - -import "gogoproto/gogo.proto"; -import "google/protobuf/any.proto"; -import "google/api/annotations.proto"; -import "cosmos/auth/v1beta1/auth.proto"; -import "cosmos_proto/cosmos.proto"; - -option go_package = "github.com/cosmos/cosmos-sdk/x/auth/types"; - -// Query defines the gRPC querier service. -service Query { - // Account returns account details based on address. - rpc Account(QueryAccountRequest) returns (QueryAccountResponse) { - option (google.api.http).get = "/cosmos/auth/v1beta1/accounts/{address}"; - } - - // Params queries all parameters. - rpc Params(QueryParamsRequest) returns (QueryParamsResponse) { - option (google.api.http).get = "/cosmos/auth/v1beta1/params"; - } -} - -// QueryAccountRequest is the request type for the Query/Account RPC method. -message QueryAccountRequest { - option (gogoproto.equal) = false; - option (gogoproto.goproto_getters) = false; - - // address defines the address to query for. - string address = 1; -} - -// QueryAccountResponse is the response type for the Query/Account RPC method. -message QueryAccountResponse { - // account defines the account of the corresponding address. - google.protobuf.Any account = 1 [(cosmos_proto.accepts_interface) = "AccountI"]; -} - -// QueryParamsRequest is the request type for the Query/Params RPC method. -message QueryParamsRequest {} - -// QueryParamsResponse is the response type for the Query/Params RPC method. -message QueryParamsResponse { - // params defines the parameters of the module. - Params params = 1 [(gogoproto.nullable) = false]; -} - diff --git a/third_party/proto/cosmos/base/query/v1beta1/pagination.proto b/third_party/proto/cosmos/base/query/v1beta1/pagination.proto deleted file mode 100644 index 2a8cbccedd8..00000000000 --- a/third_party/proto/cosmos/base/query/v1beta1/pagination.proto +++ /dev/null @@ -1,50 +0,0 @@ -syntax = "proto3"; -package cosmos.base.query.v1beta1; - -option go_package = "github.com/cosmos/cosmos-sdk/types/query"; - -// PageRequest is to be embedded in gRPC request messages for efficient -// pagination. Ex: -// -// message SomeRequest { -// Foo some_parameter = 1; -// PageRequest pagination = 2; -// } -message PageRequest { - // key is a value returned in PageResponse.next_key to begin - // querying the next page most efficiently. Only one of offset or key - // should be set. - bytes key = 1; - - // offset is a numeric offset that can be used when key is unavailable. - // It is less efficient than using key. Only one of offset or key should - // be set. - uint64 offset = 2; - - // limit is the total number of results to be returned in the result page. - // If left empty it will default to a value to be set by each app. - uint64 limit = 3; - - // count_total is set to true to indicate that the result set should include - // a count of the total number of items available for pagination in UIs. - // count_total is only respected when offset is used. It is ignored when key - // is set. - bool count_total = 4; -} - -// PageResponse is to be embedded in gRPC response messages where the -// corresponding request message has used PageRequest. -// -// message SomeResponse { -// repeated Bar results = 1; -// PageResponse page = 2; -// } -message PageResponse { - // next_key is the key to be passed to PageRequest.key to - // query the next page most efficiently - bytes next_key = 1; - - // total is total number of results available if PageRequest.count_total - // was set, its value is undefined otherwise - uint64 total = 2; -} diff --git a/third_party/proto/cosmos/base/v1beta1/coin.proto b/third_party/proto/cosmos/base/v1beta1/coin.proto deleted file mode 100644 index fab75284b7f..00000000000 --- a/third_party/proto/cosmos/base/v1beta1/coin.proto +++ /dev/null @@ -1,40 +0,0 @@ -syntax = "proto3"; -package cosmos.base.v1beta1; - -import "gogoproto/gogo.proto"; - -option go_package = "github.com/cosmos/cosmos-sdk/types"; -option (gogoproto.goproto_stringer_all) = false; -option (gogoproto.stringer_all) = false; - -// Coin defines a token with a denomination and an amount. -// -// NOTE: The amount field is an Int which implements the custom method -// signatures required by gogoproto. -message Coin { - option (gogoproto.equal) = true; - - string denom = 1; - string amount = 2 [(gogoproto.customtype) = "Int", (gogoproto.nullable) = false]; -} - -// DecCoin defines a token with a denomination and a decimal amount. -// -// NOTE: The amount field is an Dec which implements the custom method -// signatures required by gogoproto. -message DecCoin { - option (gogoproto.equal) = true; - - string denom = 1; - string amount = 2 [(gogoproto.customtype) = "Dec", (gogoproto.nullable) = false]; -} - -// IntProto defines a Protobuf wrapper around an Int object. -message IntProto { - string int = 1 [(gogoproto.customtype) = "Int", (gogoproto.nullable) = false]; -} - -// DecProto defines a Protobuf wrapper around a Dec object. -message DecProto { - string dec = 1 [(gogoproto.customtype) = "Dec", (gogoproto.nullable) = false]; -} diff --git a/third_party/proto/cosmos/upgrade/v1beta1/upgrade.proto b/third_party/proto/cosmos/upgrade/v1beta1/upgrade.proto deleted file mode 100644 index d322dedc30d..00000000000 --- a/third_party/proto/cosmos/upgrade/v1beta1/upgrade.proto +++ /dev/null @@ -1,59 +0,0 @@ -syntax = "proto3"; -package cosmos.upgrade.v1beta1; - -import "gogoproto/gogo.proto"; -import "google/protobuf/timestamp.proto"; - -option go_package = "github.com/cosmos/cosmos-sdk/x/upgrade/types"; -option (gogoproto.goproto_stringer_all) = false; -option (gogoproto.goproto_getters_all) = false; - -// Plan specifies information about a planned upgrade and when it should occur. -message Plan { - option (gogoproto.equal) = true; - - // Sets the name for the upgrade. This name will be used by the upgraded - // version of the software to apply any special "on-upgrade" commands during - // the first BeginBlock method after the upgrade is applied. It is also used - // to detect whether a software version can handle a given upgrade. If no - // upgrade handler with this name has been set in the software, it will be - // assumed that the software is out-of-date when the upgrade Time or Height is - // reached and the software will exit. - string name = 1; - - // The time after which the upgrade must be performed. - // Leave set to its zero value to use a pre-defined Height instead. - google.protobuf.Timestamp time = 2 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false]; - - // The height at which the upgrade must be performed. - // Only used if Time is not set. - int64 height = 3; - - // Any application specific upgrade info to be included on-chain - // such as a git commit that validators could automatically upgrade to - string info = 4; - - // UpgradedClientState field has been deprecated. IBC upgrade logic has been - // moved to the IBC module in the sub module 02-client. - reserved 5; - reserved "option"; -} - -// SoftwareUpgradeProposal is a gov Content type for initiating a software -// upgrade. -message SoftwareUpgradeProposal { - option (gogoproto.equal) = true; - - string title = 1; - string description = 2; - Plan plan = 3 [(gogoproto.nullable) = false]; -} - -// CancelSoftwareUpgradeProposal is a gov Content type for cancelling a software -// upgrade. -message CancelSoftwareUpgradeProposal { - option (gogoproto.equal) = true; - - string title = 1; - string description = 2; -} diff --git a/third_party/proto/cosmos_proto/cosmos.proto b/third_party/proto/cosmos_proto/cosmos.proto deleted file mode 100644 index 167b170757b..00000000000 --- a/third_party/proto/cosmos_proto/cosmos.proto +++ /dev/null @@ -1,16 +0,0 @@ -syntax = "proto3"; -package cosmos_proto; - -import "google/protobuf/descriptor.proto"; - -option go_package = "github.com/regen-network/cosmos-proto"; - -extend google.protobuf.MessageOptions { - string interface_type = 93001; - - string implements_interface = 93002; -} - -extend google.protobuf.FieldOptions { - string accepts_interface = 93001; -} diff --git a/third_party/proto/gogoproto/gogo.proto b/third_party/proto/gogoproto/gogo.proto deleted file mode 100644 index 49e78f99fe5..00000000000 --- a/third_party/proto/gogoproto/gogo.proto +++ /dev/null @@ -1,145 +0,0 @@ -// Protocol Buffers for Go with Gadgets -// -// Copyright (c) 2013, The GoGo Authors. All rights reserved. -// http://github.com/gogo/protobuf -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -syntax = "proto2"; -package gogoproto; - -import "google/protobuf/descriptor.proto"; - -option java_package = "com.google.protobuf"; -option java_outer_classname = "GoGoProtos"; -option go_package = "github.com/gogo/protobuf/gogoproto"; - -extend google.protobuf.EnumOptions { - optional bool goproto_enum_prefix = 62001; - optional bool goproto_enum_stringer = 62021; - optional bool enum_stringer = 62022; - optional string enum_customname = 62023; - optional bool enumdecl = 62024; -} - -extend google.protobuf.EnumValueOptions { - optional string enumvalue_customname = 66001; -} - -extend google.protobuf.FileOptions { - optional bool goproto_getters_all = 63001; - optional bool goproto_enum_prefix_all = 63002; - optional bool goproto_stringer_all = 63003; - optional bool verbose_equal_all = 63004; - optional bool face_all = 63005; - optional bool gostring_all = 63006; - optional bool populate_all = 63007; - optional bool stringer_all = 63008; - optional bool onlyone_all = 63009; - - optional bool equal_all = 63013; - optional bool description_all = 63014; - optional bool testgen_all = 63015; - optional bool benchgen_all = 63016; - optional bool marshaler_all = 63017; - optional bool unmarshaler_all = 63018; - optional bool stable_marshaler_all = 63019; - - optional bool sizer_all = 63020; - - optional bool goproto_enum_stringer_all = 63021; - optional bool enum_stringer_all = 63022; - - optional bool unsafe_marshaler_all = 63023; - optional bool unsafe_unmarshaler_all = 63024; - - optional bool goproto_extensions_map_all = 63025; - optional bool goproto_unrecognized_all = 63026; - optional bool gogoproto_import = 63027; - optional bool protosizer_all = 63028; - optional bool compare_all = 63029; - optional bool typedecl_all = 63030; - optional bool enumdecl_all = 63031; - - optional bool goproto_registration = 63032; - optional bool messagename_all = 63033; - - optional bool goproto_sizecache_all = 63034; - optional bool goproto_unkeyed_all = 63035; -} - -extend google.protobuf.MessageOptions { - optional bool goproto_getters = 64001; - optional bool goproto_stringer = 64003; - optional bool verbose_equal = 64004; - optional bool face = 64005; - optional bool gostring = 64006; - optional bool populate = 64007; - optional bool stringer = 67008; - optional bool onlyone = 64009; - - optional bool equal = 64013; - optional bool description = 64014; - optional bool testgen = 64015; - optional bool benchgen = 64016; - optional bool marshaler = 64017; - optional bool unmarshaler = 64018; - optional bool stable_marshaler = 64019; - - optional bool sizer = 64020; - - optional bool unsafe_marshaler = 64023; - optional bool unsafe_unmarshaler = 64024; - - optional bool goproto_extensions_map = 64025; - optional bool goproto_unrecognized = 64026; - - optional bool protosizer = 64028; - optional bool compare = 64029; - - optional bool typedecl = 64030; - - optional bool messagename = 64033; - - optional bool goproto_sizecache = 64034; - optional bool goproto_unkeyed = 64035; -} - -extend google.protobuf.FieldOptions { - optional bool nullable = 65001; - optional bool embed = 65002; - optional string customtype = 65003; - optional string customname = 65004; - optional string jsontag = 65005; - optional string moretags = 65006; - optional string casttype = 65007; - optional string castkey = 65008; - optional string castvalue = 65009; - - optional bool stdtime = 65010; - optional bool stdduration = 65011; - optional bool wktpointer = 65012; - - optional string castrepeated = 65013; -} diff --git a/third_party/proto/google/api/annotations.proto b/third_party/proto/google/api/annotations.proto deleted file mode 100644 index 85c361b47fe..00000000000 --- a/third_party/proto/google/api/annotations.proto +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) 2015, Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -syntax = "proto3"; - -package google.api; - -import "google/api/http.proto"; -import "google/protobuf/descriptor.proto"; - -option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; -option java_multiple_files = true; -option java_outer_classname = "AnnotationsProto"; -option java_package = "com.google.api"; -option objc_class_prefix = "GAPI"; - -extend google.protobuf.MethodOptions { - // See `HttpRule`. - HttpRule http = 72295728; -} diff --git a/third_party/proto/google/api/http.proto b/third_party/proto/google/api/http.proto deleted file mode 100644 index 2bd3a19bfa5..00000000000 --- a/third_party/proto/google/api/http.proto +++ /dev/null @@ -1,318 +0,0 @@ -// Copyright 2018 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -syntax = "proto3"; - -package google.api; - -option cc_enable_arenas = true; -option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; -option java_multiple_files = true; -option java_outer_classname = "HttpProto"; -option java_package = "com.google.api"; -option objc_class_prefix = "GAPI"; - - -// Defines the HTTP configuration for an API service. It contains a list of -// [HttpRule][google.api.HttpRule], each specifying the mapping of an RPC method -// to one or more HTTP REST API methods. -message Http { - // A list of HTTP configuration rules that apply to individual API methods. - // - // **NOTE:** All service configuration rules follow "last one wins" order. - repeated HttpRule rules = 1; - - // When set to true, URL path parmeters will be fully URI-decoded except in - // cases of single segment matches in reserved expansion, where "%2F" will be - // left encoded. - // - // The default behavior is to not decode RFC 6570 reserved characters in multi - // segment matches. - bool fully_decode_reserved_expansion = 2; -} - -// `HttpRule` defines the mapping of an RPC method to one or more HTTP -// REST API methods. The mapping specifies how different portions of the RPC -// request message are mapped to URL path, URL query parameters, and -// HTTP request body. The mapping is typically specified as an -// `google.api.http` annotation on the RPC method, -// see "google/api/annotations.proto" for details. -// -// The mapping consists of a field specifying the path template and -// method kind. The path template can refer to fields in the request -// message, as in the example below which describes a REST GET -// operation on a resource collection of messages: -// -// -// service Messaging { -// rpc GetMessage(GetMessageRequest) returns (Message) { -// option (google.api.http).get = "/v1/messages/{message_id}/{sub.subfield}"; -// } -// } -// message GetMessageRequest { -// message SubMessage { -// string subfield = 1; -// } -// string message_id = 1; // mapped to the URL -// SubMessage sub = 2; // `sub.subfield` is url-mapped -// } -// message Message { -// string text = 1; // content of the resource -// } -// -// The same http annotation can alternatively be expressed inside the -// `GRPC API Configuration` YAML file. -// -// http: -// rules: -// - selector: .Messaging.GetMessage -// get: /v1/messages/{message_id}/{sub.subfield} -// -// This definition enables an automatic, bidrectional mapping of HTTP -// JSON to RPC. Example: -// -// HTTP | RPC -// -----|----- -// `GET /v1/messages/123456/foo` | `GetMessage(message_id: "123456" sub: SubMessage(subfield: "foo"))` -// -// In general, not only fields but also field paths can be referenced -// from a path pattern. Fields mapped to the path pattern cannot be -// repeated and must have a primitive (non-message) type. -// -// Any fields in the request message which are not bound by the path -// pattern automatically become (optional) HTTP query -// parameters. Assume the following definition of the request message: -// -// -// service Messaging { -// rpc GetMessage(GetMessageRequest) returns (Message) { -// option (google.api.http).get = "/v1/messages/{message_id}"; -// } -// } -// message GetMessageRequest { -// message SubMessage { -// string subfield = 1; -// } -// string message_id = 1; // mapped to the URL -// int64 revision = 2; // becomes a parameter -// SubMessage sub = 3; // `sub.subfield` becomes a parameter -// } -// -// -// This enables a HTTP JSON to RPC mapping as below: -// -// HTTP | RPC -// -----|----- -// `GET /v1/messages/123456?revision=2&sub.subfield=foo` | `GetMessage(message_id: "123456" revision: 2 sub: SubMessage(subfield: "foo"))` -// -// Note that fields which are mapped to HTTP parameters must have a -// primitive type or a repeated primitive type. Message types are not -// allowed. In the case of a repeated type, the parameter can be -// repeated in the URL, as in `...?param=A¶m=B`. -// -// For HTTP method kinds which allow a request body, the `body` field -// specifies the mapping. Consider a REST update method on the -// message resource collection: -// -// -// service Messaging { -// rpc UpdateMessage(UpdateMessageRequest) returns (Message) { -// option (google.api.http) = { -// put: "/v1/messages/{message_id}" -// body: "message" -// }; -// } -// } -// message UpdateMessageRequest { -// string message_id = 1; // mapped to the URL -// Message message = 2; // mapped to the body -// } -// -// -// The following HTTP JSON to RPC mapping is enabled, where the -// representation of the JSON in the request body is determined by -// protos JSON encoding: -// -// HTTP | RPC -// -----|----- -// `PUT /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id: "123456" message { text: "Hi!" })` -// -// The special name `*` can be used in the body mapping to define that -// every field not bound by the path template should be mapped to the -// request body. This enables the following alternative definition of -// the update method: -// -// service Messaging { -// rpc UpdateMessage(Message) returns (Message) { -// option (google.api.http) = { -// put: "/v1/messages/{message_id}" -// body: "*" -// }; -// } -// } -// message Message { -// string message_id = 1; -// string text = 2; -// } -// -// -// The following HTTP JSON to RPC mapping is enabled: -// -// HTTP | RPC -// -----|----- -// `PUT /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id: "123456" text: "Hi!")` -// -// Note that when using `*` in the body mapping, it is not possible to -// have HTTP parameters, as all fields not bound by the path end in -// the body. This makes this option more rarely used in practice of -// defining REST APIs. The common usage of `*` is in custom methods -// which don't use the URL at all for transferring data. -// -// It is possible to define multiple HTTP methods for one RPC by using -// the `additional_bindings` option. Example: -// -// service Messaging { -// rpc GetMessage(GetMessageRequest) returns (Message) { -// option (google.api.http) = { -// get: "/v1/messages/{message_id}" -// additional_bindings { -// get: "/v1/users/{user_id}/messages/{message_id}" -// } -// }; -// } -// } -// message GetMessageRequest { -// string message_id = 1; -// string user_id = 2; -// } -// -// -// This enables the following two alternative HTTP JSON to RPC -// mappings: -// -// HTTP | RPC -// -----|----- -// `GET /v1/messages/123456` | `GetMessage(message_id: "123456")` -// `GET /v1/users/me/messages/123456` | `GetMessage(user_id: "me" message_id: "123456")` -// -// # Rules for HTTP mapping -// -// The rules for mapping HTTP path, query parameters, and body fields -// to the request message are as follows: -// -// 1. The `body` field specifies either `*` or a field path, or is -// omitted. If omitted, it indicates there is no HTTP request body. -// 2. Leaf fields (recursive expansion of nested messages in the -// request) can be classified into three types: -// (a) Matched in the URL template. -// (b) Covered by body (if body is `*`, everything except (a) fields; -// else everything under the body field) -// (c) All other fields. -// 3. URL query parameters found in the HTTP request are mapped to (c) fields. -// 4. Any body sent with an HTTP request can contain only (b) fields. -// -// The syntax of the path template is as follows: -// -// Template = "/" Segments [ Verb ] ; -// Segments = Segment { "/" Segment } ; -// Segment = "*" | "**" | LITERAL | Variable ; -// Variable = "{" FieldPath [ "=" Segments ] "}" ; -// FieldPath = IDENT { "." IDENT } ; -// Verb = ":" LITERAL ; -// -// The syntax `*` matches a single path segment. The syntax `**` matches zero -// or more path segments, which must be the last part of the path except the -// `Verb`. The syntax `LITERAL` matches literal text in the path. -// -// The syntax `Variable` matches part of the URL path as specified by its -// template. A variable template must not contain other variables. If a variable -// matches a single path segment, its template may be omitted, e.g. `{var}` -// is equivalent to `{var=*}`. -// -// If a variable contains exactly one path segment, such as `"{var}"` or -// `"{var=*}"`, when such a variable is expanded into a URL path, all characters -// except `[-_.~0-9a-zA-Z]` are percent-encoded. Such variables show up in the -// Discovery Document as `{var}`. -// -// If a variable contains one or more path segments, such as `"{var=foo/*}"` -// or `"{var=**}"`, when such a variable is expanded into a URL path, all -// characters except `[-_.~/0-9a-zA-Z]` are percent-encoded. Such variables -// show up in the Discovery Document as `{+var}`. -// -// NOTE: While the single segment variable matches the semantics of -// [RFC 6570](https://tools.ietf.org/html/rfc6570) Section 3.2.2 -// Simple String Expansion, the multi segment variable **does not** match -// RFC 6570 Reserved Expansion. The reason is that the Reserved Expansion -// does not expand special characters like `?` and `#`, which would lead -// to invalid URLs. -// -// NOTE: the field paths in variables and in the `body` must not refer to -// repeated fields or map fields. -message HttpRule { - // Selects methods to which this rule applies. - // - // Refer to [selector][google.api.DocumentationRule.selector] for syntax details. - string selector = 1; - - // Determines the URL pattern is matched by this rules. This pattern can be - // used with any of the {get|put|post|delete|patch} methods. A custom method - // can be defined using the 'custom' field. - oneof pattern { - // Used for listing and getting information about resources. - string get = 2; - - // Used for updating a resource. - string put = 3; - - // Used for creating a resource. - string post = 4; - - // Used for deleting a resource. - string delete = 5; - - // Used for updating a resource. - string patch = 6; - - // The custom pattern is used for specifying an HTTP method that is not - // included in the `pattern` field, such as HEAD, or "*" to leave the - // HTTP method unspecified for this rule. The wild-card rule is useful - // for services that provide content to Web (HTML) clients. - CustomHttpPattern custom = 8; - } - - // The name of the request field whose value is mapped to the HTTP body, or - // `*` for mapping all fields not captured by the path pattern to the HTTP - // body. NOTE: the referred field must not be a repeated field and must be - // present at the top-level of request message type. - string body = 7; - - // Optional. The name of the response field whose value is mapped to the HTTP - // body of response. Other response fields are ignored. When - // not set, the response message will be used as HTTP body of response. - string response_body = 12; - - // Additional HTTP bindings for the selector. Nested bindings must - // not contain an `additional_bindings` field themselves (that is, - // the nesting may only be one level deep). - repeated HttpRule additional_bindings = 11; -} - -// A custom pattern is used for defining custom HTTP verb. -message CustomHttpPattern { - // The name of this custom HTTP verb. - string kind = 1; - - // The path matched by this custom verb. - string path = 2; -} diff --git a/third_party/proto/google/protobuf/any.proto b/third_party/proto/google/protobuf/any.proto deleted file mode 100644 index 1431810ea45..00000000000 --- a/third_party/proto/google/protobuf/any.proto +++ /dev/null @@ -1,161 +0,0 @@ -// Protocol Buffers - Google's data interchange format -// Copyright 2008 Google Inc. All rights reserved. -// https://developers.google.com/protocol-buffers/ -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -syntax = "proto3"; - -package google.protobuf; - -import "gogoproto/gogo.proto"; - -option csharp_namespace = "Google.Protobuf.WellKnownTypes"; -option go_package = "types"; -option java_package = "com.google.protobuf"; -option java_outer_classname = "AnyProto"; -option java_multiple_files = true; -option objc_class_prefix = "GPB"; - -// `Any` contains an arbitrary serialized protocol buffer message along with a -// URL that describes the type of the serialized message. -// -// Protobuf library provides support to pack/unpack Any values in the form -// of utility functions or additional generated methods of the Any type. -// -// Example 1: Pack and unpack a message in C++. -// -// Foo foo = ...; -// Any any; -// any.PackFrom(foo); -// ... -// if (any.UnpackTo(&foo)) { -// ... -// } -// -// Example 2: Pack and unpack a message in Java. -// -// Foo foo = ...; -// Any any = Any.pack(foo); -// ... -// if (any.is(Foo.class)) { -// foo = any.unpack(Foo.class); -// } -// -// Example 3: Pack and unpack a message in Python. -// -// foo = Foo(...) -// any = Any() -// any.Pack(foo) -// ... -// if any.Is(Foo.DESCRIPTOR): -// any.Unpack(foo) -// ... -// -// Example 4: Pack and unpack a message in Go -// -// foo := &pb.Foo{...} -// any, err := ptypes.MarshalAny(foo) -// ... -// foo := &pb.Foo{} -// if err := ptypes.UnmarshalAny(any, foo); err != nil { -// ... -// } -// -// The pack methods provided by protobuf library will by default use -// 'type.googleapis.com/full.type.name' as the type URL and the unpack -// methods only use the fully qualified type name after the last '/' -// in the type URL, for example "foo.bar.com/x/y.z" will yield type -// name "y.z". -// -// -// JSON -// ==== -// The JSON representation of an `Any` value uses the regular -// representation of the deserialized, embedded message, with an -// additional field `@type` which contains the type URL. Example: -// -// package google.profile; -// message Person { -// string first_name = 1; -// string last_name = 2; -// } -// -// { -// "@type": "type.googleapis.com/google.profile.Person", -// "firstName": , -// "lastName": -// } -// -// If the embedded message type is well-known and has a custom JSON -// representation, that representation will be embedded adding a field -// `value` which holds the custom JSON in addition to the `@type` -// field. Example (for message [google.protobuf.Duration][]): -// -// { -// "@type": "type.googleapis.com/google.protobuf.Duration", -// "value": "1.212s" -// } -// -message Any { - // A URL/resource name that uniquely identifies the type of the serialized - // protocol buffer message. This string must contain at least - // one "/" character. The last segment of the URL's path must represent - // the fully qualified name of the type (as in - // `path/google.protobuf.Duration`). The name should be in a canonical form - // (e.g., leading "." is not accepted). - // - // In practice, teams usually precompile into the binary all types that they - // expect it to use in the context of Any. However, for URLs which use the - // scheme `http`, `https`, or no scheme, one can optionally set up a type - // server that maps type URLs to message definitions as follows: - // - // * If no scheme is provided, `https` is assumed. - // * An HTTP GET on the URL must yield a [google.protobuf.Type][] - // value in binary format, or produce an error. - // * Applications are allowed to cache lookup results based on the - // URL, or have them precompiled into a binary to avoid any - // lookup. Therefore, binary compatibility needs to be preserved - // on changes to types. (Use versioned type names to manage - // breaking changes.) - // - // Note: this functionality is not currently available in the official - // protobuf release, and it is not used for type URLs beginning with - // type.googleapis.com. - // - // Schemes other than `http`, `https` (or the empty scheme) might be - // used with implementation specific semantics. - // - string type_url = 1; - - // Must be a valid serialized protocol buffer of the above specified type. - bytes value = 2; - - option (gogoproto.typedecl) = false; -} - -option (gogoproto.goproto_registration) = false; diff --git a/third_party/proto/proofs.proto b/third_party/proto/proofs.proto deleted file mode 100644 index da43503ecbd..00000000000 --- a/third_party/proto/proofs.proto +++ /dev/null @@ -1,234 +0,0 @@ -syntax = "proto3"; - -package ics23; -option go_package = "github.com/confio/ics23/go"; - -enum HashOp { - // NO_HASH is the default if no data passed. Note this is an illegal argument some places. - NO_HASH = 0; - SHA256 = 1; - SHA512 = 2; - KECCAK = 3; - RIPEMD160 = 4; - BITCOIN = 5; // ripemd160(sha256(x)) -} - -/** -LengthOp defines how to process the key and value of the LeafOp -to include length information. After encoding the length with the given -algorithm, the length will be prepended to the key and value bytes. -(Each one with it's own encoded length) -*/ -enum LengthOp { - // NO_PREFIX don't include any length info - NO_PREFIX = 0; - // VAR_PROTO uses protobuf (and go-amino) varint encoding of the length - VAR_PROTO = 1; - // VAR_RLP uses rlp int encoding of the length - VAR_RLP = 2; - // FIXED32_BIG uses big-endian encoding of the length as a 32 bit integer - FIXED32_BIG = 3; - // FIXED32_LITTLE uses little-endian encoding of the length as a 32 bit integer - FIXED32_LITTLE = 4; - // FIXED64_BIG uses big-endian encoding of the length as a 64 bit integer - FIXED64_BIG = 5; - // FIXED64_LITTLE uses little-endian encoding of the length as a 64 bit integer - FIXED64_LITTLE = 6; - // REQUIRE_32_BYTES is like NONE, but will fail if the input is not exactly 32 bytes (sha256 output) - REQUIRE_32_BYTES = 7; - // REQUIRE_64_BYTES is like NONE, but will fail if the input is not exactly 64 bytes (sha512 output) - REQUIRE_64_BYTES = 8; -} - -/** -ExistenceProof takes a key and a value and a set of steps to perform on it. -The result of peforming all these steps will provide a "root hash", which can -be compared to the value in a header. - -Since it is computationally infeasible to produce a hash collission for any of the used -cryptographic hash functions, if someone can provide a series of operations to transform -a given key and value into a root hash that matches some trusted root, these key and values -must be in the referenced merkle tree. - -The only possible issue is maliablity in LeafOp, such as providing extra prefix data, -which should be controlled by a spec. Eg. with lengthOp as NONE, - prefix = FOO, key = BAR, value = CHOICE -and - prefix = F, key = OOBAR, value = CHOICE -would produce the same value. - -With LengthOp this is tricker but not impossible. Which is why the "leafPrefixEqual" field -in the ProofSpec is valuable to prevent this mutability. And why all trees should -length-prefix the data before hashing it. -*/ -message ExistenceProof { - bytes key = 1; - bytes value = 2; - LeafOp leaf = 3; - repeated InnerOp path = 4; -} - -/* -NonExistenceProof takes a proof of two neighbors, one left of the desired key, -one right of the desired key. If both proofs are valid AND they are neighbors, -then there is no valid proof for the given key. -*/ -message NonExistenceProof { - bytes key = 1; // TODO: remove this as unnecessary??? we prove a range - ExistenceProof left = 2; - ExistenceProof right = 3; -} - -/* -CommitmentProof is either an ExistenceProof or a NonExistenceProof, or a Batch of such messages -*/ -message CommitmentProof { - oneof proof { - ExistenceProof exist = 1; - NonExistenceProof nonexist = 2; - BatchProof batch = 3; - CompressedBatchProof compressed = 4; - } -} - -/** -LeafOp represents the raw key-value data we wish to prove, and -must be flexible to represent the internal transformation from -the original key-value pairs into the basis hash, for many existing -merkle trees. - -key and value are passed in. So that the signature of this operation is: - leafOp(key, value) -> output - -To process this, first prehash the keys and values if needed (ANY means no hash in this case): - hkey = prehashKey(key) - hvalue = prehashValue(value) - -Then combine the bytes, and hash it - output = hash(prefix || length(hkey) || hkey || length(hvalue) || hvalue) -*/ -message LeafOp { - HashOp hash = 1; - HashOp prehash_key = 2; - HashOp prehash_value = 3; - LengthOp length = 4; - // prefix is a fixed bytes that may optionally be included at the beginning to differentiate - // a leaf node from an inner node. - bytes prefix = 5; -} - -/** -InnerOp represents a merkle-proof step that is not a leaf. -It represents concatenating two children and hashing them to provide the next result. - -The result of the previous step is passed in, so the signature of this op is: - innerOp(child) -> output - -The result of applying InnerOp should be: - output = op.hash(op.prefix || child || op.suffix) - - where the || operator is concatenation of binary data, -and child is the result of hashing all the tree below this step. - -Any special data, like prepending child with the length, or prepending the entire operation with -some value to differentiate from leaf nodes, should be included in prefix and suffix. -If either of prefix or suffix is empty, we just treat it as an empty string -*/ -message InnerOp { - HashOp hash = 1; - bytes prefix = 2; - bytes suffix = 3; -} - - -/** -ProofSpec defines what the expected parameters are for a given proof type. -This can be stored in the client and used to validate any incoming proofs. - - verify(ProofSpec, Proof) -> Proof | Error - -As demonstrated in tests, if we don't fix the algorithm used to calculate the -LeafHash for a given tree, there are many possible key-value pairs that can -generate a given hash (by interpretting the preimage differently). -We need this for proper security, requires client knows a priori what -tree format server uses. But not in code, rather a configuration object. -*/ -message ProofSpec { - // any field in the ExistenceProof must be the same as in this spec. - // except Prefix, which is just the first bytes of prefix (spec can be longer) - LeafOp leaf_spec = 1; - InnerSpec inner_spec = 2; - // max_depth (if > 0) is the maximum number of InnerOps allowed (mainly for fixed-depth tries) - int32 max_depth = 3; - // min_depth (if > 0) is the minimum number of InnerOps allowed (mainly for fixed-depth tries) - int32 min_depth = 4; -} - -/* -InnerSpec contains all store-specific structure info to determine if two proofs from a -given store are neighbors. - -This enables: - - isLeftMost(spec: InnerSpec, op: InnerOp) - isRightMost(spec: InnerSpec, op: InnerOp) - isLeftNeighbor(spec: InnerSpec, left: InnerOp, right: InnerOp) -*/ -message InnerSpec { - // Child order is the ordering of the children node, must count from 0 - // iavl tree is [0, 1] (left then right) - // merk is [0, 2, 1] (left, right, here) - repeated int32 child_order = 1; - int32 child_size = 2; - int32 min_prefix_length = 3; - int32 max_prefix_length = 4; - // empty child is the prehash image that is used when one child is nil (eg. 20 bytes of 0) - bytes empty_child = 5; - // hash is the algorithm that must be used for each InnerOp - HashOp hash = 6; -} - -/* -BatchProof is a group of multiple proof types than can be compressed -*/ -message BatchProof { - repeated BatchEntry entries = 1; -} - -// Use BatchEntry not CommitmentProof, to avoid recursion -message BatchEntry { - oneof proof { - ExistenceProof exist = 1; - NonExistenceProof nonexist = 2; - } -} - - -/****** all items here are compressed forms *******/ - -message CompressedBatchProof { - repeated CompressedBatchEntry entries = 1; - repeated InnerOp lookup_inners = 2; -} - -// Use BatchEntry not CommitmentProof, to avoid recursion -message CompressedBatchEntry { - oneof proof { - CompressedExistenceProof exist = 1; - CompressedNonExistenceProof nonexist = 2; - } -} - -message CompressedExistenceProof { - bytes key = 1; - bytes value = 2; - LeafOp leaf = 3; - // these are indexes into the lookup_inners table in CompressedBatchProof - repeated int32 path = 4; -} - -message CompressedNonExistenceProof { - bytes key = 1; // TODO: remove this as unnecessary??? we prove a range - CompressedExistenceProof left = 2; - CompressedExistenceProof right = 3; -} diff --git a/third_party/proto/tendermint/crypto/keys.proto b/third_party/proto/tendermint/crypto/keys.proto deleted file mode 100644 index 16fd7adf3ee..00000000000 --- a/third_party/proto/tendermint/crypto/keys.proto +++ /dev/null @@ -1,17 +0,0 @@ -syntax = "proto3"; -package tendermint.crypto; - -option go_package = "github.com/tendermint/tendermint/proto/tendermint/crypto"; - -import "gogoproto/gogo.proto"; - -// PublicKey defines the keys available for use with Tendermint Validators -message PublicKey { - option (gogoproto.compare) = true; - option (gogoproto.equal) = true; - - oneof sum { - bytes ed25519 = 1; - bytes secp256k1 = 2; - } -} diff --git a/third_party/proto/tendermint/crypto/proof.proto b/third_party/proto/tendermint/crypto/proof.proto deleted file mode 100644 index 975df768539..00000000000 --- a/third_party/proto/tendermint/crypto/proof.proto +++ /dev/null @@ -1,41 +0,0 @@ -syntax = "proto3"; -package tendermint.crypto; - -option go_package = "github.com/tendermint/tendermint/proto/tendermint/crypto"; - -import "gogoproto/gogo.proto"; - -message Proof { - int64 total = 1; - int64 index = 2; - bytes leaf_hash = 3; - repeated bytes aunts = 4; -} - -message ValueOp { - // Encoded in ProofOp.Key. - bytes key = 1; - - // To encode in ProofOp.Data - Proof proof = 2; -} - -message DominoOp { - string key = 1; - string input = 2; - string output = 3; -} - -// ProofOp defines an operation used for calculating Merkle root -// The data could be arbitrary format, providing nessecary data -// for example neighbouring node hash -message ProofOp { - string type = 1; - bytes key = 2; - bytes data = 3; -} - -// ProofOps is Merkle proof defined by the list of ProofOps -message ProofOps { - repeated ProofOp ops = 1 [(gogoproto.nullable) = false]; -} diff --git a/third_party/proto/tendermint/libs/bits/types.proto b/third_party/proto/tendermint/libs/bits/types.proto deleted file mode 100644 index 3111d113a5b..00000000000 --- a/third_party/proto/tendermint/libs/bits/types.proto +++ /dev/null @@ -1,9 +0,0 @@ -syntax = "proto3"; -package tendermint.libs.bits; - -option go_package = "github.com/tendermint/tendermint/proto/tendermint/libs/bits"; - -message BitArray { - int64 bits = 1; - repeated uint64 elems = 2; -} diff --git a/third_party/proto/tendermint/types/types.proto b/third_party/proto/tendermint/types/types.proto deleted file mode 100644 index 7f7ea74cac2..00000000000 --- a/third_party/proto/tendermint/types/types.proto +++ /dev/null @@ -1,157 +0,0 @@ -syntax = "proto3"; -package tendermint.types; - -option go_package = "github.com/tendermint/tendermint/proto/tendermint/types"; - -import "gogoproto/gogo.proto"; -import "google/protobuf/timestamp.proto"; -import "tendermint/crypto/proof.proto"; -import "tendermint/version/types.proto"; -import "tendermint/types/validator.proto"; - -// BlockIdFlag indicates which BlcokID the signature is for -enum BlockIDFlag { - option (gogoproto.goproto_enum_stringer) = true; - option (gogoproto.goproto_enum_prefix) = false; - - BLOCK_ID_FLAG_UNKNOWN = 0 [(gogoproto.enumvalue_customname) = "BlockIDFlagUnknown"]; - BLOCK_ID_FLAG_ABSENT = 1 [(gogoproto.enumvalue_customname) = "BlockIDFlagAbsent"]; - BLOCK_ID_FLAG_COMMIT = 2 [(gogoproto.enumvalue_customname) = "BlockIDFlagCommit"]; - BLOCK_ID_FLAG_NIL = 3 [(gogoproto.enumvalue_customname) = "BlockIDFlagNil"]; -} - -// SignedMsgType is a type of signed message in the consensus. -enum SignedMsgType { - option (gogoproto.goproto_enum_stringer) = true; - option (gogoproto.goproto_enum_prefix) = false; - - SIGNED_MSG_TYPE_UNKNOWN = 0 [(gogoproto.enumvalue_customname) = "UnknownType"]; - // Votes - SIGNED_MSG_TYPE_PREVOTE = 1 [(gogoproto.enumvalue_customname) = "PrevoteType"]; - SIGNED_MSG_TYPE_PRECOMMIT = 2 [(gogoproto.enumvalue_customname) = "PrecommitType"]; - - // Proposals - SIGNED_MSG_TYPE_PROPOSAL = 32 [(gogoproto.enumvalue_customname) = "ProposalType"]; -} - -// PartsetHeader -message PartSetHeader { - uint32 total = 1; - bytes hash = 2; -} - -message Part { - uint32 index = 1; - bytes bytes = 2; - tendermint.crypto.Proof proof = 3 [(gogoproto.nullable) = false]; -} - -// BlockID -message BlockID { - bytes hash = 1; - PartSetHeader part_set_header = 2 [(gogoproto.nullable) = false]; -} - -// -------------------------------- - -// Header defines the structure of a Tendermint block header. -message Header { - // basic block info - tendermint.version.Consensus version = 1 [(gogoproto.nullable) = false]; - string chain_id = 2 [(gogoproto.customname) = "ChainID"]; - int64 height = 3; - google.protobuf.Timestamp time = 4 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; - - // prev block info - BlockID last_block_id = 5 [(gogoproto.nullable) = false]; - - // hashes of block data - bytes last_commit_hash = 6; // commit from validators from the last block - bytes data_hash = 7; // transactions - - // hashes from the app output from the prev block - bytes validators_hash = 8; // validators for the current block - bytes next_validators_hash = 9; // validators for the next block - bytes consensus_hash = 10; // consensus params for current block - bytes app_hash = 11; // state after txs from the previous block - bytes last_results_hash = 12; // root hash of all results from the txs from the previous block - - // consensus info - bytes evidence_hash = 13; // evidence included in the block - bytes proposer_address = 14; // original proposer of the block -} - -// Data contains the set of transactions included in the block -message Data { - // Txs that will be applied by state @ block.Height+1. - // NOTE: not all txs here are valid. We're just agreeing on the order first. - // This means that block.AppHash does not include these txs. - repeated bytes txs = 1; -} - -// Vote represents a prevote, precommit, or commit vote from validators for -// consensus. -message Vote { - SignedMsgType type = 1; - int64 height = 2; - int32 round = 3; - BlockID block_id = 4 - [(gogoproto.nullable) = false, (gogoproto.customname) = "BlockID"]; // zero if vote is nil. - google.protobuf.Timestamp timestamp = 5 - [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; - bytes validator_address = 6; - int32 validator_index = 7; - bytes signature = 8; -} - -// Commit contains the evidence that a block was committed by a set of validators. -message Commit { - int64 height = 1; - int32 round = 2; - BlockID block_id = 3 [(gogoproto.nullable) = false, (gogoproto.customname) = "BlockID"]; - repeated CommitSig signatures = 4 [(gogoproto.nullable) = false]; -} - -// CommitSig is a part of the Vote included in a Commit. -message CommitSig { - BlockIDFlag block_id_flag = 1; - bytes validator_address = 2; - google.protobuf.Timestamp timestamp = 3 - [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; - bytes signature = 4; -} - -message Proposal { - SignedMsgType type = 1; - int64 height = 2; - int32 round = 3; - int32 pol_round = 4; - BlockID block_id = 5 [(gogoproto.customname) = "BlockID", (gogoproto.nullable) = false]; - google.protobuf.Timestamp timestamp = 6 - [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; - bytes signature = 7; -} - -message SignedHeader { - Header header = 1; - Commit commit = 2; -} - -message LightBlock { - SignedHeader signed_header = 1; - tendermint.types.ValidatorSet validator_set = 2; -} - -message BlockMeta { - BlockID block_id = 1 [(gogoproto.customname) = "BlockID", (gogoproto.nullable) = false]; - int64 block_size = 2; - Header header = 3 [(gogoproto.nullable) = false]; - int64 num_txs = 4; -} - -// TxProof represents a Merkle proof of the presence of a transaction in the Merkle tree. -message TxProof { - bytes root_hash = 1; - bytes data = 2; - tendermint.crypto.Proof proof = 3; -} diff --git a/third_party/proto/tendermint/types/validator.proto b/third_party/proto/tendermint/types/validator.proto deleted file mode 100644 index 49860b96d6b..00000000000 --- a/third_party/proto/tendermint/types/validator.proto +++ /dev/null @@ -1,25 +0,0 @@ -syntax = "proto3"; -package tendermint.types; - -option go_package = "github.com/tendermint/tendermint/proto/tendermint/types"; - -import "gogoproto/gogo.proto"; -import "tendermint/crypto/keys.proto"; - -message ValidatorSet { - repeated Validator validators = 1; - Validator proposer = 2; - int64 total_voting_power = 3; -} - -message Validator { - bytes address = 1; - tendermint.crypto.PublicKey pub_key = 2 [(gogoproto.nullable) = false]; - int64 voting_power = 3; - int64 proposer_priority = 4; -} - -message SimpleValidator { - tendermint.crypto.PublicKey pub_key = 1; - int64 voting_power = 2; -} diff --git a/third_party/proto/tendermint/version/types.proto b/third_party/proto/tendermint/version/types.proto deleted file mode 100644 index 6061868bd43..00000000000 --- a/third_party/proto/tendermint/version/types.proto +++ /dev/null @@ -1,24 +0,0 @@ -syntax = "proto3"; -package tendermint.version; - -option go_package = "github.com/tendermint/tendermint/proto/tendermint/version"; - -import "gogoproto/gogo.proto"; - -// App includes the protocol and software version for the application. -// This information is included in ResponseInfo. The App.Protocol can be -// updated in ResponseEndBlock. -message App { - uint64 protocol = 1; - string software = 2; -} - -// Consensus captures the consensus rules for processing a block in the blockchain, -// including all blockchain data structures and the rules of the application's -// state transition machine. -message Consensus { - option (gogoproto.equal) = true; - - uint64 block = 1; - uint64 app = 2; -}

    1}-jz@ng3E3eKW0)Z#(s?(aaH3y{Zh=_?TrGgaN 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

    F5{V?GWG{xj>zjRUC1%4x%MTsj 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%0DwWu1zdu+c8OJ9nA4BQ9Sc7KsNvK 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%0AVl8_&L48Za$ew)$E)D; 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