From 16f00d9d5ef05e8934721573e4d25d6dd2439133 Mon Sep 17 00:00:00 2001 From: Carlos Rodriguez Date: Tue, 23 Jul 2024 22:09:26 +0200 Subject: [PATCH] Revert "delete e2e" This reverts commit 71db49e7b99f7bd84ffd2dd6da238799610e1128. --- e2e/Makefile | 22 + e2e/README.md | 421 ++++ e2e/dockerutil/dockerutil.go | 70 + e2e/go.mod | 287 +++ e2e/go.sum | 1825 +++++++++++++++++ e2e/internal/directories/directories.go | 40 + e2e/relayer/relayer.go | 197 ++ e2e/sample.config.yaml | 49 + e2e/scripts/init.sh | 27 + e2e/scripts/run-compatibility-tests.sh | 14 + e2e/scripts/run-e2e.sh | 116 ++ e2e/semverutil/semver.go | 61 + e2e/semverutil/semver_test.go | 54 + e2e/tests/core/02-client/client_test.go | 489 +++++ .../core/03-connection/connection_test.go | 147 ++ e2e/tests/data/ics10_grandpa_cw.wasm.gz | Bin 0 -> 275506 bytes e2e/tests/interchain_accounts/base_test.go | 539 +++++ e2e/tests/interchain_accounts/gov_test.go | 125 ++ e2e/tests/interchain_accounts/groups_test.go | 215 ++ .../interchain_accounts/incentivized_test.go | 374 ++++ .../interchain_accounts/localhost_test.go | 481 +++++ e2e/tests/interchain_accounts/params_test.go | 271 +++ e2e/tests/interchain_accounts/query_test.go | 164 ++ .../interchain_accounts/upgrades_test.go | 382 ++++ e2e/tests/transfer/authz_test.go | 377 ++++ e2e/tests/transfer/base_test.go | 644 ++++++ e2e/tests/transfer/forwarding_test.go | 291 +++ e2e/tests/transfer/incentivized_test.go | 770 +++++++ e2e/tests/transfer/localhost_test.go | 167 ++ e2e/tests/transfer/send_enabled_test.go | 98 + e2e/tests/transfer/send_receive_test.go | 154 ++ e2e/tests/transfer/upgradesv1_test.go | 363 ++++ e2e/tests/transfer/upgradesv2_test.go | 508 +++++ e2e/tests/upgrades/genesis_test.go | 261 +++ e2e/tests/upgrades/upgrade_test.go | 1393 +++++++++++++ .../wasm/contracts/ics10_grandpa_cw.wasm.gz | Bin 0 -> 263909 bytes .../contracts/ics10_grandpa_cw_expiry.wasm.gz | Bin 0 -> 267956 bytes .../wasm/contracts/migrate_error.wasm.gz | Bin 0 -> 35116 bytes .../wasm/contracts/migrate_success.wasm.gz | Bin 0 -> 34711 bytes e2e/tests/wasm/feature_releases.go | 7 + e2e/tests/wasm/grandpa_test.go | 650 ++++++ e2e/tests/wasm/upgrade_test.go | 164 ++ e2e/testsuite/codec.go | 127 ++ e2e/testsuite/diagnostics/diagnostics.go | 163 ++ e2e/testsuite/events.go | 22 + e2e/testsuite/query/grpc_query.go | 90 + e2e/testsuite/query/queries.go | 219 ++ e2e/testsuite/sanitize/messages.go | 82 + e2e/testsuite/testconfig.go | 842 ++++++++ e2e/testsuite/testsuite.go | 814 ++++++++ e2e/testsuite/tx.go | 405 ++++ e2e/testvalues/values.go | 142 ++ 52 files changed, 15123 insertions(+) create mode 100644 e2e/Makefile create mode 100644 e2e/README.md create mode 100644 e2e/dockerutil/dockerutil.go create mode 100644 e2e/go.mod create mode 100644 e2e/go.sum create mode 100644 e2e/internal/directories/directories.go create mode 100644 e2e/relayer/relayer.go create mode 100644 e2e/sample.config.yaml create mode 100755 e2e/scripts/init.sh create mode 100755 e2e/scripts/run-compatibility-tests.sh create mode 100755 e2e/scripts/run-e2e.sh create mode 100644 e2e/semverutil/semver.go create mode 100644 e2e/semverutil/semver_test.go create mode 100644 e2e/tests/core/02-client/client_test.go create mode 100644 e2e/tests/core/03-connection/connection_test.go create mode 100755 e2e/tests/data/ics10_grandpa_cw.wasm.gz 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/localhost_test.go create mode 100644 e2e/tests/interchain_accounts/params_test.go create mode 100644 e2e/tests/interchain_accounts/query_test.go create mode 100644 e2e/tests/interchain_accounts/upgrades_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/forwarding_test.go create mode 100644 e2e/tests/transfer/incentivized_test.go create mode 100644 e2e/tests/transfer/localhost_test.go create mode 100644 e2e/tests/transfer/send_enabled_test.go create mode 100644 e2e/tests/transfer/send_receive_test.go create mode 100644 e2e/tests/transfer/upgradesv1_test.go create mode 100644 e2e/tests/transfer/upgradesv2_test.go create mode 100644 e2e/tests/upgrades/genesis_test.go create mode 100644 e2e/tests/upgrades/upgrade_test.go create mode 100644 e2e/tests/wasm/contracts/ics10_grandpa_cw.wasm.gz create mode 100644 e2e/tests/wasm/contracts/ics10_grandpa_cw_expiry.wasm.gz create mode 100644 e2e/tests/wasm/contracts/migrate_error.wasm.gz create mode 100644 e2e/tests/wasm/contracts/migrate_success.wasm.gz create mode 100644 e2e/tests/wasm/feature_releases.go create mode 100644 e2e/tests/wasm/grandpa_test.go create mode 100644 e2e/tests/wasm/upgrade_test.go create mode 100644 e2e/testsuite/codec.go create mode 100644 e2e/testsuite/diagnostics/diagnostics.go create mode 100644 e2e/testsuite/events.go create mode 100644 e2e/testsuite/query/grpc_query.go create mode 100644 e2e/testsuite/query/queries.go create mode 100644 e2e/testsuite/sanitize/messages.go create mode 100644 e2e/testsuite/testconfig.go create mode 100644 e2e/testsuite/testsuite.go create mode 100644 e2e/testsuite/tx.go create mode 100644 e2e/testvalues/values.go diff --git a/e2e/Makefile b/e2e/Makefile new file mode 100644 index 00000000000..34938edb50e --- /dev/null +++ b/e2e/Makefile @@ -0,0 +1,22 @@ +DOCKER := $(shell which docker) +TEST_CONTAINERS=$(shell docker ps --filter "label=ibc-test" -a -q) + +cleanup-ibc-test-containers: + for id in $(TEST_CONTAINERS) ; do \ + $(DOCKER) stop $$id ; \ + $(DOCKER) rm $$id ; \ + done + +init: + ./scripts/init.sh + +e2e-test: init cleanup-ibc-test-containers + ./scripts/run-e2e.sh $(test) $(entrypoint) + +e2e-suite: init cleanup-ibc-test-containers + RUN_SUITE="true" ./scripts/run-e2e.sh "" $(entrypoint) + +compatibility-tests: + ./scripts/run-compatibility-tests.sh $(release_branch) + +.PHONY: cleanup-ibc-test-containers e2e-test compatibility-tests init diff --git a/e2e/README.md b/e2e/README.md new file mode 100644 index 00000000000..7d37307c306 --- /dev/null +++ b/e2e/README.md @@ -0,0 +1,421 @@ +# 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) +6. [Importable Workflow](#importable-workflow) + +# 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`. + +After creating a new test file, be sure to add a build constraint that ensures this file will **not** be included in the package to be built when +running tests locally via `make test`. For an example of this, see any of the existing test files. + +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` + +The tests can be configured using a configuration file or environment variables. + +See [the example](./sample.config.yaml) to get started. The default location the tests look is `~/.ibc-go-e2e-config.yaml` +But this can be specified directly using the `E2E_CONFIG_PATH` environment variable. + +There are several environment variables that alter the behaviour of the make target which will override any +options specified in your config file. + +| 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 A | latest | +| CHAIN_B_TAG | The tag used for chain B | latest | +| CHAIN_BINARY | The binary used in the container | simd | +| RELAYER_TAG | The tag used for the relayer | main | +| RELAYER_ID | The type of relayer to use (rly/hermes) | hermes | + +> 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 versions (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/orgs/cosmos/packages?repo_name=ibc-go). + +### 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 RELAYER_TAG="v2.0.0" +make e2e-test entrypoint=TestInterchainAccountsTestSuite test=TestMsgSubmitTx_SuccessfulTransfer +``` + +If `jq` is installed, you only need to specify the `test`. + +If `fzf` is also installed, you only need to run `make e2e-test` and you will be prompted with interactive test selection. + +```sh +make e2e-test 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, s.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, s.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){ + msgPayPacketFee := feetypes.NewMsgPayPacketFee(testFee, channelA.PortID, channelA.ChannelID, chainAWallet.Bech32Address(chainA.Config().Bech32Prefix), nil) + msgTransfer := 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, msgPayPacketFee, msgTransfer) + + 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/orgs/cosmos/packages?repo_name=ibc-go) 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. + +### Accessing Logs + +- When a test fails in GitHub. The logs of the test will be uploaded (viewable in the summary page of the workflow). Note: There + may be some discrepancy in the logs collected and the output of interchain test. The containers may run for a some + time after the logs are collected, resulting in the displayed logs to differ slightly. + +## Importable Workflow + +This repository contains an [importable workflow](https://github.com/cosmos/ibc-go/blob/185a220244663457372185992cfc85ed9e458bf1/.github/workflows/e2e-compatibility-workflow-call.yaml) that can be used from any other repository to test chain upgrades. The workflow +can be used to test both non-IBC chains, and also IBC-enabled chains. + +### Prerequisites + +- In order to run this workflow, a docker container is required with tags for the versions you want to test. + +- Have an upgrade handler in the chain binary which is being upgraded to. + +> It's worth noting that all github repositories come with a built-in docker registry that makes it convenient to build and push images to. + +[This workflow](https://github.com/cosmos/ibc-go/blob/1da651e5e117872499e3558c2a92f887369ae262/.github/workflows/release.yml#L35-L61) can be used as a reference for how to build a docker image +whenever a git tag is pushed. + +### How to import the workflow + +You can refer to [this example](https://github.com/cosmos/ibc-go/blob/2933906d1ed25ae6dce7b7d93aa429dfa94c5a23/.github/workflows/e2e-upgrade.yaml#L9-L19) when including this workflow in your repo. + +The referenced job will do the following: + +- Create two chains using the image found at `ghcr.io/cosmos/ibc-go-simd:v4.3.0`. +- Perform IBC transfers verifying core functionality. +- Upgrade chain A to `ghcr.io/cosmos/ibc-go-simd:v5.1.0` by executing a governance proposal and using the plan name `normal upgrade`. +- Perform additional IBC transfers and verifies the upgrade and migrations ran successfully. + +> Note: The plan name will always be specific to your chain. In this instance `normal upgrade` is referring to [this upgrade handler](https://github.com/cosmos/ibc-go/blob/e9bc0bac38e84e1380ec08552cae15821143a6b6/testing/simapp/app.go#L923) + +### Workflow Options + +| Workflow Field | Purpose | +|-------------------|---------------------------------------------------| +| chain-image | The docker image to use for the test | +| chain-a-tag | The tag of chain A to use | +| chain-b-tag | The tag of chain B to use | +| chain-upgrade-tag | The tag chain A should be upgraded to | +| chain-binary | The chain binary name | +| upgrade-plan-name | The name of the upgrade plan to execute | +| test-entry-point | Always TestUpgradeTestSuite | +| test | Should be TestIBCChainUpgrade or TestChainUpgrade | + +> TestIBCChainUpgrade should be used for ibc tests, while TestChainUpgrade should be used for single chain tests. diff --git a/e2e/dockerutil/dockerutil.go b/e2e/dockerutil/dockerutil.go new file mode 100644 index 00000000000..48fb97055d3 --- /dev/null +++ b/e2e/dockerutil/dockerutil.go @@ -0,0 +1,70 @@ +package dockerutil + +import ( + "archive/tar" + "context" + "fmt" + "io" + "path" + + dockertypes "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/filters" + dockerclient "github.com/docker/docker/client" +) + +const testLabel = "ibc-test" + +// GetTestContainers returns all docker containers that have been created by interchain test. +// note: the test suite name must be passed as the chains are created in SetupSuite which will label +// them with the name of the test suite rather than the test. +func GetTestContainers(ctx context.Context, suiteName string, dc *dockerclient.Client) ([]dockertypes.Container, error) { + testContainers, err := dc.ContainerList(ctx, dockertypes.ContainerListOptions{ + All: true, + Filters: filters.NewArgs( + // see: https://github.com/strangelove-ventures/interchaintest/blob/0bdc194c2aa11aa32479f32b19e1c50304301981/internal/dockerutil/setup.go#L31-L36 + // for the suiteName needed to identify test containers. + filters.Arg("label", testLabel+"="+suiteName), + ), + }) + if err != nil { + return nil, fmt.Errorf("failed listing containers: %s", err) + } + + return testContainers, nil +} + +// GetContainerLogs returns the logs of a container as a byte array. +func GetContainerLogs(ctx context.Context, dc *dockerclient.Client, containerName string) ([]byte, error) { + readCloser, err := dc.ContainerLogs(ctx, containerName, dockertypes.ContainerLogsOptions{ + ShowStdout: true, + ShowStderr: true, + }) + if err != nil { + return nil, fmt.Errorf("failed reading logs in test cleanup: %s", err) + } + return io.ReadAll(readCloser) +} + +// GetFileContentsFromContainer reads the contents of a specific file from a container. +func GetFileContentsFromContainer(ctx context.Context, dc *dockerclient.Client, containerID, absolutePath string) ([]byte, error) { + readCloser, _, err := dc.CopyFromContainer(ctx, containerID, absolutePath) + if err != nil { + return nil, fmt.Errorf("copying from container: %w", err) + } + + defer readCloser.Close() + + fileName := path.Base(absolutePath) + tr := tar.NewReader(readCloser) + + hdr, err := tr.Next() + if err != nil { + return nil, err + } + + if hdr.Name != fileName { + return nil, fmt.Errorf("expected to find %s but found %s", fileName, hdr.Name) + } + + return io.ReadAll(tr) +} diff --git a/e2e/go.mod b/e2e/go.mod new file mode 100644 index 00000000000..00a3d222c21 --- /dev/null +++ b/e2e/go.mod @@ -0,0 +1,287 @@ +module github.com/cosmos/ibc-go/e2e + +go 1.22.2 + +toolchain go1.22.3 + +// needed temporarily for v9. +replace ( + github.com/misko9/go-substrate-rpc-client/v4 => github.com/DimitrisJim/go-substrate-rpc-client/v4 v4.0.0-20240717100841-406da076c1d5 + github.com/strangelove-ventures/interchaintest/v8 => github.com/DimitrisJim/interchaintest/v8 v8.0.0-20240717102845-beba523a47ff +) + +require ( + cosmossdk.io/errors v1.0.1 + cosmossdk.io/math v1.3.0 + cosmossdk.io/x/upgrade v0.1.3 + github.com/cometbft/cometbft v0.38.10 + github.com/cosmos/cosmos-sdk v0.50.7 + github.com/cosmos/gogoproto v1.5.0 + github.com/cosmos/ibc-go/modules/light-clients/08-wasm v0.0.0-00010101000000-000000000000 + github.com/cosmos/ibc-go/v9 v9.0.0 + github.com/docker/docker v24.0.7+incompatible + github.com/pelletier/go-toml v1.9.5 + github.com/strangelove-ventures/interchaintest/v8 v8.2.1-0.20240419152858-c8b741617cd8 + github.com/stretchr/testify v1.9.0 + go.uber.org/zap v1.27.0 + golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 + golang.org/x/mod v0.19.0 + google.golang.org/grpc v1.65.0 + gopkg.in/yaml.v2 v2.4.0 +) + +require ( + cloud.google.com/go v0.115.0 // indirect + cloud.google.com/go/auth v0.6.0 // indirect + cloud.google.com/go/auth/oauth2adapt v0.2.2 // indirect + cloud.google.com/go/compute/metadata v0.3.0 // indirect + cloud.google.com/go/iam v1.1.9 // indirect + cloud.google.com/go/storage v1.41.0 // indirect + cosmossdk.io/api v0.7.5 // indirect + cosmossdk.io/client/v2 v2.0.0-beta.2 // indirect + cosmossdk.io/collections v0.4.0 // indirect + cosmossdk.io/core v0.11.0 // indirect + cosmossdk.io/depinject v1.0.0-alpha.4 // indirect + cosmossdk.io/log v1.3.1 // indirect + cosmossdk.io/store v1.1.0 // indirect + cosmossdk.io/x/feegrant v0.1.1 // indirect + cosmossdk.io/x/tx v0.13.3 // indirect + filippo.io/edwards25519 v1.1.0 // indirect + github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect + github.com/99designs/keyring v1.2.2 // indirect + github.com/BurntSushi/toml v1.3.2 // indirect + github.com/ChainSafe/go-schnorrkel v1.1.0 // indirect + github.com/ChainSafe/go-schnorrkel/1 v0.0.0-00010101000000-000000000000 // indirect + github.com/ComposableFi/go-subkey/v2 v2.0.0-tm03420 // indirect + github.com/CosmWasm/wasmvm/v2 v2.1.0 // indirect + github.com/DataDog/datadog-go v4.8.3+incompatible // indirect + github.com/DataDog/zstd v1.5.5 // indirect + github.com/FactomProject/basen v0.0.0-20150613233007-fe3947df716e // indirect + github.com/FactomProject/btcutilecc v0.0.0-20130527213604-d3a63a5752ec // indirect + github.com/Microsoft/go-winio v0.6.2 // indirect + github.com/StirlingMarketingGroup/go-namecase v1.0.0 // indirect + github.com/avast/retry-go/v4 v4.5.1 // indirect + github.com/aws/aws-sdk-go v1.49.0 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect + github.com/bgentry/speakeasy v0.2.0 // indirect + github.com/bits-and-blooms/bitset v1.12.0 // indirect + github.com/btcsuite/btcd/btcec/v2 v2.3.3 // indirect + github.com/cenkalti/backoff/v4 v4.3.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/chzyer/readline v1.5.1 // indirect + github.com/cockroachdb/apd/v2 v2.0.2 // indirect + github.com/cockroachdb/errors v1.11.3 // indirect + github.com/cockroachdb/fifo v0.0.0-20240616162244-4768e80dfb9a // indirect + github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect + github.com/cockroachdb/pebble v1.1.1 // indirect + github.com/cockroachdb/redact v1.1.5 // indirect + github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect + github.com/cometbft/cometbft-db v0.12.0 // indirect + github.com/cosmos/btcutil v1.0.5 // indirect + github.com/cosmos/cosmos-db v1.0.2 // indirect + github.com/cosmos/cosmos-proto v1.0.0-beta.5 // indirect + github.com/cosmos/go-bip39 v1.0.0 // indirect + github.com/cosmos/gogogateway v1.2.0 // indirect + github.com/cosmos/iavl v1.2.0 // indirect + github.com/cosmos/ibc-go/modules/capability v1.0.1 // indirect + github.com/cosmos/ics23/go v0.10.0 // indirect + github.com/cosmos/ledger-cosmos-go v0.13.3 // indirect + github.com/danieljoos/wincred v1.2.1 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/deckarep/golang-set v1.8.0 // indirect + github.com/decred/base58 v1.0.5 // indirect + github.com/decred/dcrd/crypto/blake256 v1.0.1 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v2 v2.0.1 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect + github.com/desertbit/timer v1.0.1 // indirect + github.com/dgraph-io/badger/v4 v4.2.0 // indirect + github.com/dgraph-io/ristretto v0.1.1 // indirect + github.com/distribution/reference v0.5.0 // indirect + github.com/docker/distribution v2.8.3+incompatible // indirect + github.com/docker/go-connections v0.5.0 // indirect + github.com/docker/go-units v0.5.0 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect + github.com/dvsekhvalnov/jose2go v1.7.0 // indirect + github.com/emicklei/dot v1.6.2 // indirect + github.com/ethereum/go-ethereum v1.13.14 // indirect + github.com/fatih/color v1.17.0 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/getsentry/sentry-go v0.28.1 // indirect + github.com/go-kit/kit v0.13.0 // indirect + github.com/go-kit/log v0.2.1 // indirect + github.com/go-logfmt/logfmt v0.6.0 // indirect + github.com/go-logr/logr v1.4.1 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect + github.com/gogo/googleapis v1.4.1 // indirect + github.com/gogo/protobuf v1.3.3 // indirect + github.com/golang/glog v1.2.1 // 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.4 // indirect + github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect + github.com/google/btree v1.1.2 // indirect + github.com/google/flatbuffers v24.3.25+incompatible // indirect + github.com/google/go-cmp v0.6.0 // indirect + github.com/google/orderedcode v0.0.1 // indirect + github.com/google/s2a-go v0.1.7 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect + github.com/googleapis/gax-go/v2 v2.12.5 // indirect + github.com/gorilla/handlers v1.5.2 // indirect + github.com/gorilla/mux v1.8.1 // indirect + github.com/gorilla/websocket v1.5.3 // indirect + github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect + github.com/grpc-ecosystem/grpc-gateway v1.16.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.7.4 // indirect + github.com/hashicorp/go-hclog v1.6.3 // indirect + github.com/hashicorp/go-immutable-radix v1.3.1 // indirect + github.com/hashicorp/go-metrics v0.5.3 // indirect + github.com/hashicorp/go-plugin v1.6.1 // indirect + github.com/hashicorp/go-safetemp v1.0.0 // indirect + github.com/hashicorp/go-version v1.6.0 // indirect + github.com/hashicorp/golang-lru v1.0.2 // indirect + github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/hashicorp/yamux v0.1.1 // indirect + github.com/hdevalence/ed25519consensus v0.2.0 // indirect + github.com/holiman/uint256 v1.2.4 // indirect + github.com/huandu/skiplist v1.2.0 // indirect + github.com/iancoleman/strcase v0.3.0 // indirect + github.com/icza/dyno v0.0.0-20230330125955-09f820a8d9c0 // indirect + github.com/improbable-eng/grpc-web v0.15.0 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/ipfs/go-cid v0.4.1 // 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/klauspost/compress v1.17.9 // indirect + github.com/klauspost/cpuid/v2 v2.2.6 // indirect + github.com/kr/pretty v0.3.1 // indirect + github.com/kr/text v0.2.0 // indirect + github.com/lib/pq v1.10.9 // indirect + github.com/libp2p/go-buffer-pool v0.1.0 // indirect + github.com/libp2p/go-libp2p v0.32.1 // indirect + github.com/linxGnu/grocksdb v1.9.2 // 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.20 // indirect + github.com/mimoo/StrobeGo v0.0.0-20220103164710-9a04d6ca976b // indirect + github.com/minio/highwayhash v1.0.2 // indirect + github.com/minio/sha256-simd v1.0.1 // indirect + github.com/misko9/go-substrate-rpc-client/v4 v4.0.0-20230913220906-b988ea7da0c2 // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/mitchellh/go-testing-interface v1.14.1 // 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.1.0 // indirect + github.com/multiformats/go-base36 v0.2.0 // indirect + github.com/multiformats/go-multiaddr v0.12.0 // indirect + github.com/multiformats/go-multibase v0.2.0 // indirect + github.com/multiformats/go-multicodec v0.9.0 // indirect + github.com/multiformats/go-multihash v0.2.3 // indirect + github.com/multiformats/go-varint v0.0.7 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/oasisprotocol/curve25519-voi v0.0.0-20230904125328-1f23a7beb09a // indirect + github.com/oklog/run v1.1.0 // indirect + github.com/opencontainers/go-digest v1.0.0 // indirect + github.com/opencontainers/image-spec v1.1.0-rc5 // indirect + github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/petermattis/goid v0.0.0-20240607163614-bb94eb51e7a7 // indirect + github.com/pierrec/xxHash v0.1.5 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/prometheus/client_golang v1.19.1 // indirect + github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/common v0.55.0 // indirect + github.com/prometheus/procfs v0.15.1 // indirect + github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect + github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect + github.com/rogpeppe/go-internal v1.12.0 // indirect + github.com/rs/cors v1.11.0 // indirect + github.com/rs/zerolog v1.33.0 // indirect + github.com/sagikazarmark/locafero v0.6.0 // indirect + github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/sasha-s/go-deadlock v0.3.1 // indirect + github.com/shamaton/msgpack/v2 v2.2.0 // indirect + github.com/sourcegraph/conc v0.3.0 // indirect + github.com/spaolacci/murmur3 v1.1.0 // indirect + github.com/spf13/afero v1.11.0 // indirect + github.com/spf13/cast v1.6.0 // indirect + github.com/spf13/cobra v1.8.1 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/spf13/viper v1.19.0 // indirect + github.com/subosito/gotenv v1.6.0 // indirect + github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect + github.com/tendermint/go-amino v0.16.0 // indirect + github.com/tidwall/btree v1.7.0 // indirect + github.com/tyler-smith/go-bip32 v1.0.0 // indirect + github.com/tyler-smith/go-bip39 v1.1.0 // indirect + github.com/ulikunitz/xz v0.5.11 // indirect + github.com/zondax/hid v0.9.2 // indirect + github.com/zondax/ledger-go v0.14.3 // indirect + go.etcd.io/bbolt v1.4.0-alpha.1 // indirect + go.opencensus.io v0.24.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect + go.opentelemetry.io/otel v1.24.0 // indirect + go.opentelemetry.io/otel/metric v1.24.0 // indirect + go.opentelemetry.io/otel/trace v1.24.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + golang.org/x/crypto v0.24.0 // indirect + golang.org/x/net v0.26.0 // indirect + golang.org/x/oauth2 v0.21.0 // indirect + golang.org/x/sync v0.7.0 // indirect + golang.org/x/sys v0.21.0 // indirect + golang.org/x/term v0.21.0 // indirect + golang.org/x/text v0.16.0 // indirect + golang.org/x/time v0.5.0 // indirect + golang.org/x/tools v0.22.0 // indirect + google.golang.org/api v0.186.0 // indirect + google.golang.org/genproto v0.0.0-20240701130421-f6361c86f094 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect + google.golang.org/protobuf v1.34.2 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + gotest.tools/v3 v3.5.1 // indirect + lukechampine.com/blake3 v1.2.1 // indirect + lukechampine.com/uint128 v1.3.0 // indirect + modernc.org/cc/v3 v3.41.0 // indirect + modernc.org/ccgo/v3 v3.16.15 // indirect + modernc.org/libc v1.37.1 // indirect + modernc.org/mathutil v1.6.0 // indirect + modernc.org/memory v1.7.2 // indirect + modernc.org/opt v0.1.3 // indirect + modernc.org/sqlite v1.28.0 // indirect + modernc.org/strutil v1.2.0 // indirect + modernc.org/token v1.1.0 // indirect + nhooyr.io/websocket v1.8.11 // indirect + pgregory.net/rapid v1.1.0 // indirect + sigs.k8s.io/yaml v1.4.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/v9 => ../ + +replace github.com/cosmos/ibc-go/modules/light-clients/08-wasm => ../modules/light-clients/08-wasm + +replace github.com/syndtr/goleveldb => github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 + +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 new file mode 100644 index 00000000000..946031aaf67 --- /dev/null +++ b/e2e/go.sum @@ -0,0 +1,1825 @@ +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= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +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.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.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= +cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc= +cloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34hIU= +cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA= +cloud.google.com/go v0.115.0 h1:CnFSK6Xo3lDYRoBKEcAtia6VSC837/ZkJuRduSFnr14= +cloud.google.com/go v0.115.0/go.mod h1:8jIM5vVgoAEoiVxQ/O4BFTfHqulPZgs/ufEzMcFMdWU= +cloud.google.com/go/aiplatform v1.22.0/go.mod h1:ig5Nct50bZlzV6NvKaTwmplLLddFx0YReh9WfTO5jKw= +cloud.google.com/go/aiplatform v1.24.0/go.mod h1:67UUvRBKG6GTayHKV8DBv2RtR1t93YRu5B1P3x99mYY= +cloud.google.com/go/analytics v0.11.0/go.mod h1:DjEWCu41bVbYcKyvlws9Er60YE4a//bK6mnhWvQeFNI= +cloud.google.com/go/analytics v0.12.0/go.mod h1:gkfj9h6XRf9+TS4bmuhPEShsh3hH8PAZzm/41OOhQd4= +cloud.google.com/go/area120 v0.5.0/go.mod h1:DE/n4mp+iqVyvxHN41Vf1CR602GiHQjFPusMFW6bGR4= +cloud.google.com/go/area120 v0.6.0/go.mod h1:39yFJqWVgm0UZqWTOdqkLhjoC7uFfgXRC8g/ZegeAh0= +cloud.google.com/go/artifactregistry v1.6.0/go.mod h1:IYt0oBPSAGYj/kprzsBjZ/4LnG/zOcHyFHjWPCi6SAQ= +cloud.google.com/go/artifactregistry v1.7.0/go.mod h1:mqTOFOnGZx8EtSqK/ZWcsm/4U8B77rbcLP6ruDU2Ixk= +cloud.google.com/go/asset v1.5.0/go.mod h1:5mfs8UvcM5wHhqtSv8J1CtxxaQq3AdBxxQi2jGW/K4o= +cloud.google.com/go/asset v1.7.0/go.mod h1:YbENsRK4+xTiL+Ofoj5Ckf+O17kJtgp3Y3nn4uzZz5s= +cloud.google.com/go/asset v1.8.0/go.mod h1:mUNGKhiqIdbr8X7KNayoYvyc4HbbFO9URsjbytpUaW0= +cloud.google.com/go/assuredworkloads v1.5.0/go.mod h1:n8HOZ6pff6re5KYfBXcFvSViQjDwxFkAkmUFffJRbbY= +cloud.google.com/go/assuredworkloads v1.6.0/go.mod h1:yo2YOk37Yc89Rsd5QMVECvjaMKymF9OP+QXWlKXUkXw= +cloud.google.com/go/assuredworkloads v1.7.0/go.mod h1:z/736/oNmtGAyU47reJgGN+KVoYoxeLBoj4XkKYscNI= +cloud.google.com/go/auth v0.6.0 h1:5x+d6b5zdezZ7gmLWD1m/xNjnaQ2YDhmIz/HH3doy1g= +cloud.google.com/go/auth v0.6.0/go.mod h1:b4acV+jLQDyjwm4OXHYjNvRi4jvGBzHWJRtJcy+2P4g= +cloud.google.com/go/auth/oauth2adapt v0.2.2 h1:+TTV8aXpjeChS9M+aTtN/TjdQnzJvmzKFt//oWu7HX4= +cloud.google.com/go/auth/oauth2adapt v0.2.2/go.mod h1:wcYjgpZI9+Yu7LyYBg4pqSiaRkfEK3GQcpb7C/uyF1Q= +cloud.google.com/go/automl v1.5.0/go.mod h1:34EjfoFGMZ5sgJ9EoLsRtdPSNZLcfflJR39VbVNS2M0= +cloud.google.com/go/automl v1.6.0/go.mod h1:ugf8a6Fx+zP0D59WLhqgTDsQI9w07o64uf/Is3Nh5p8= +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/bigquery v1.42.0/go.mod h1:8dRTJxhtG+vwBKzE5OseQn/hiydoQN3EedCaOdYmxRA= +cloud.google.com/go/billing v1.4.0/go.mod h1:g9IdKBEFlItS8bTtlrZdVLWSSdSyFUZKXNS02zKMOZY= +cloud.google.com/go/billing v1.5.0/go.mod h1:mztb1tBc3QekhjSgmpf/CV4LzWXLzCArwpLmP2Gm88s= +cloud.google.com/go/binaryauthorization v1.1.0/go.mod h1:xwnoWu3Y84jbuHa0zd526MJYmtnVXn0syOjaJgy4+dM= +cloud.google.com/go/binaryauthorization v1.2.0/go.mod h1:86WKkJHtRcv5ViNABtYMhhNWRrD1Vpi//uKEy7aYEfI= +cloud.google.com/go/cloudtasks v1.5.0/go.mod h1:fD92REy1x5woxkKEkLdvavGnPJGEn8Uic9nWuLzqCpY= +cloud.google.com/go/cloudtasks v1.6.0/go.mod h1:C6Io+sxuke9/KNRkbQpihnW93SWDU3uXt92nu85HkYI= +cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= +cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= +cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M= +cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s= +cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= +cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U= +cloud.google.com/go/compute v1.10.0/go.mod h1:ER5CLbMxl90o2jtNbGSbtfOpQKR0t15FOtRsugnLrlU= +cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc= +cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= +cloud.google.com/go/containeranalysis v0.5.1/go.mod h1:1D92jd8gRR/c0fGMlymRgxWD3Qw9C1ff6/T7mLgVL8I= +cloud.google.com/go/containeranalysis v0.6.0/go.mod h1:HEJoiEIu+lEXM+k7+qLCci0h33lX3ZqoYFdmPcoO7s4= +cloud.google.com/go/datacatalog v1.3.0/go.mod h1:g9svFY6tuR+j+hrTw3J2dNcmI0dzmSiyOzm8kpLq0a0= +cloud.google.com/go/datacatalog v1.5.0/go.mod h1:M7GPLNQeLfWqeIm3iuiruhPzkt65+Bx8dAKvScX8jvs= +cloud.google.com/go/datacatalog v1.6.0/go.mod h1:+aEyF8JKg+uXcIdAmmaMUmZ3q1b/lKLtXCmXdnc0lbc= +cloud.google.com/go/dataflow v0.6.0/go.mod h1:9QwV89cGoxjjSR9/r7eFDqqjtvbKxAK2BaYU6PVk9UM= +cloud.google.com/go/dataflow v0.7.0/go.mod h1:PX526vb4ijFMesO1o202EaUmouZKBpjHsTlCtB4parQ= +cloud.google.com/go/dataform v0.3.0/go.mod h1:cj8uNliRlHpa6L3yVhDOBrUXH+BPAO1+KFMQQNSThKo= +cloud.google.com/go/dataform v0.4.0/go.mod h1:fwV6Y4Ty2yIFL89huYlEkwUPtS7YZinZbzzj5S9FzCE= +cloud.google.com/go/datalabeling v0.5.0/go.mod h1:TGcJ0G2NzcsXSE/97yWjIZO0bXj0KbVlINXMG9ud42I= +cloud.google.com/go/datalabeling v0.6.0/go.mod h1:WqdISuk/+WIGeMkpw/1q7bK/tFEZxsrFJOJdY2bXvTQ= +cloud.google.com/go/dataqna v0.5.0/go.mod h1:90Hyk596ft3zUQ8NkFfvICSIfHFh1Bc7C4cK3vbhkeo= +cloud.google.com/go/dataqna v0.6.0/go.mod h1:1lqNpM7rqNLVgWBJyk5NF6Uen2PHym0jtVJonplVsDA= +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/datastream v1.2.0/go.mod h1:i/uTP8/fZwgATHS/XFu0TcNUhuA0twZxxQ3EyCUQMwo= +cloud.google.com/go/datastream v1.3.0/go.mod h1:cqlOX8xlyYF/uxhiKn6Hbv6WjwPPuI9W2M9SAXwaLLQ= +cloud.google.com/go/dialogflow v1.15.0/go.mod h1:HbHDWs33WOGJgn6rfzBW1Kv807BE3O1+xGbn59zZWI4= +cloud.google.com/go/dialogflow v1.16.1/go.mod h1:po6LlzGfK+smoSmTBnbkIZY2w8ffjz/RcGSS+sh1el0= +cloud.google.com/go/dialogflow v1.17.0/go.mod h1:YNP09C/kXA1aZdBgC/VtXX74G/TKn7XVCcVumTflA+8= +cloud.google.com/go/documentai v1.7.0/go.mod h1:lJvftZB5NRiFSX4moiye1SMxHx0Bc3x1+p9e/RfXYiU= +cloud.google.com/go/documentai v1.8.0/go.mod h1:xGHNEB7CtsnySCNrCFdCyyMz44RhFEEX2Q7UD0c5IhU= +cloud.google.com/go/domains v0.6.0/go.mod h1:T9Rz3GasrpYk6mEGHh4rymIhjlnIuB4ofT1wTxDeT4Y= +cloud.google.com/go/domains v0.7.0/go.mod h1:PtZeqS1xjnXuRPKE/88Iru/LdfoRyEHYA9nFQf4UKpg= +cloud.google.com/go/edgecontainer v0.1.0/go.mod h1:WgkZ9tp10bFxqO8BLPqv2LlfmQF1X8lZqwW4r1BTajk= +cloud.google.com/go/edgecontainer v0.2.0/go.mod h1:RTmLijy+lGpQ7BXuTDa4C4ssxyXT34NIuHIgKuP4s5w= +cloud.google.com/go/functions v1.6.0/go.mod h1:3H1UA3qiIPRWD7PeZKLvHZ9SaQhR26XIJcC0A5GbvAk= +cloud.google.com/go/functions v1.7.0/go.mod h1:+d+QBcWM+RsrgZfV9xo6KfA1GlzJfxcfZcRPEhDDfzg= +cloud.google.com/go/gaming v1.5.0/go.mod h1:ol7rGcxP/qHTRQE/RO4bxkXq+Fix0j6D4LFPzYTIrDM= +cloud.google.com/go/gaming v1.6.0/go.mod h1:YMU1GEvA39Qt3zWGyAVA9bpYz/yAhTvaQ1t2sK4KPUA= +cloud.google.com/go/gkeconnect v0.5.0/go.mod h1:c5lsNAg5EwAy7fkqX/+goqFsU1Da/jQFqArp+wGNr/o= +cloud.google.com/go/gkeconnect v0.6.0/go.mod h1:Mln67KyU/sHJEBY8kFZ0xTeyPtzbq9StAVvEULYK16A= +cloud.google.com/go/gkehub v0.9.0/go.mod h1:WYHN6WG8w9bXU0hqNxt8rm5uxnk8IH+lPY9J2TV7BK0= +cloud.google.com/go/gkehub v0.10.0/go.mod h1:UIPwxI0DsrpsVoWpLB0stwKCP+WFVG9+y977wO+hBH0= +cloud.google.com/go/grafeas v0.2.0/go.mod h1:KhxgtF2hb0P191HlY5besjYm6MqTSTj3LSI+M+ByZHc= +cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY= +cloud.google.com/go/iam v0.5.0/go.mod h1:wPU9Vt0P4UmCux7mqtRu6jcpPAb74cP1fh50J3QpkUc= +cloud.google.com/go/iam v1.1.9 h1:oSkYLVtVme29uGYrOcKcvJRht7cHJpYD09GM9JaR0TE= +cloud.google.com/go/iam v1.1.9/go.mod h1:Nt1eDWNYH9nGQg3d/mY7U1hvfGmsaG9o/kLGoLoLXjQ= +cloud.google.com/go/language v1.4.0/go.mod h1:F9dRpNFQmJbkaop6g0JhSBXCNlO90e1KWx5iDdxbWic= +cloud.google.com/go/language v1.6.0/go.mod h1:6dJ8t3B+lUYfStgls25GusK04NLh3eDLQnWM3mdEbhI= +cloud.google.com/go/lifesciences v0.5.0/go.mod h1:3oIKy8ycWGPUyZDR/8RNnTOYevhaMLqh5vLUXs9zvT8= +cloud.google.com/go/lifesciences v0.6.0/go.mod h1:ddj6tSX/7BOnhxCSd3ZcETvtNr8NZ6t/iPhY2Tyfu08= +cloud.google.com/go/mediatranslation v0.5.0/go.mod h1:jGPUhGTybqsPQn91pNXw0xVHfuJ3leR1wj37oU3y1f4= +cloud.google.com/go/mediatranslation v0.6.0/go.mod h1:hHdBCTYNigsBxshbznuIMFNe5QXEowAuNmmC7h8pu5w= +cloud.google.com/go/memcache v1.4.0/go.mod h1:rTOfiGZtJX1AaFUrOgsMHX5kAzaTQ8azHiuDoTPzNsE= +cloud.google.com/go/memcache v1.5.0/go.mod h1:dk3fCK7dVo0cUU2c36jKb4VqKPS22BTkf81Xq617aWM= +cloud.google.com/go/metastore v1.5.0/go.mod h1:2ZNrDcQwghfdtCwJ33nM0+GrBGlVuh8rakL3vdPY3XY= +cloud.google.com/go/metastore v1.6.0/go.mod h1:6cyQTls8CWXzk45G55x57DVQ9gWg7RiH65+YgPsNh9s= +cloud.google.com/go/networkconnectivity v1.4.0/go.mod h1:nOl7YL8odKyAOtzNX73/M5/mGZgqqMeryi6UPZTk/rA= +cloud.google.com/go/networkconnectivity v1.5.0/go.mod h1:3GzqJx7uhtlM3kln0+x5wyFvuVH1pIBJjhCpjzSt75o= +cloud.google.com/go/networksecurity v0.5.0/go.mod h1:xS6fOCoqpVC5zx15Z/MqkfDwH4+m/61A3ODiDV1xmiQ= +cloud.google.com/go/networksecurity v0.6.0/go.mod h1:Q5fjhTr9WMI5mbpRYEbiexTzROf7ZbDzvzCrNl14nyU= +cloud.google.com/go/notebooks v1.2.0/go.mod h1:9+wtppMfVPUeJ8fIWPOq1UnATHISkGXGqTkxeieQ6UY= +cloud.google.com/go/notebooks v1.3.0/go.mod h1:bFR5lj07DtCPC7YAAJ//vHskFBxA5JzYlH68kXVdk34= +cloud.google.com/go/osconfig v1.7.0/go.mod h1:oVHeCeZELfJP7XLxcBGTMBvRO+1nQ5tFG9VQTmYS2Fs= +cloud.google.com/go/osconfig v1.8.0/go.mod h1:EQqZLu5w5XA7eKizepumcvWx+m8mJUhEwiPqWiZeEdg= +cloud.google.com/go/oslogin v1.4.0/go.mod h1:YdgMXWRaElXz/lDk1Na6Fh5orF7gvmJ0FGLIs9LId4E= +cloud.google.com/go/oslogin v1.5.0/go.mod h1:D260Qj11W2qx/HVF29zBg+0fd6YCSjSqLUkY/qEenQU= +cloud.google.com/go/phishingprotection v0.5.0/go.mod h1:Y3HZknsK9bc9dMi+oE8Bim0lczMU6hrX0UpADuMefr0= +cloud.google.com/go/phishingprotection v0.6.0/go.mod h1:9Y3LBLgy0kDTcYET8ZH3bq/7qni15yVUoAxiFxnlSUA= +cloud.google.com/go/privatecatalog v0.5.0/go.mod h1:XgosMUvvPyxDjAVNDYxJ7wBW8//hLDDYmnsNcMGq1K0= +cloud.google.com/go/privatecatalog v0.6.0/go.mod h1:i/fbkZR0hLN29eEWiiwue8Pb+GforiEIBnV9yrRUOKI= +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= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/recaptchaenterprise v1.3.1/go.mod h1:OdD+q+y4XGeAlxRaMn1Y7/GveP6zmq76byL6tjPE7d4= +cloud.google.com/go/recaptchaenterprise/v2 v2.1.0/go.mod h1:w9yVqajwroDNTfGuhmOjPDN//rZGySaf6PtFVcSCa7o= +cloud.google.com/go/recaptchaenterprise/v2 v2.2.0/go.mod h1:/Zu5jisWGeERrd5HnlS3EUGb/D335f9k51B/FVil0jk= +cloud.google.com/go/recaptchaenterprise/v2 v2.3.0/go.mod h1:O9LwGCjrhGHBQET5CA7dd5NwwNQUErSgEDit1DLNTdo= +cloud.google.com/go/recommendationengine v0.5.0/go.mod h1:E5756pJcVFeVgaQv3WNpImkFP8a+RptV6dDLGPILjvg= +cloud.google.com/go/recommendationengine v0.6.0/go.mod h1:08mq2umu9oIqc7tDy8sx+MNJdLG0fUi3vaSVbztHgJ4= +cloud.google.com/go/recommender v1.5.0/go.mod h1:jdoeiBIVrJe9gQjwd759ecLJbxCDED4A6p+mqoqDvTg= +cloud.google.com/go/recommender v1.6.0/go.mod h1:+yETpm25mcoiECKh9DEScGzIRyDKpZ0cEhWGo+8bo+c= +cloud.google.com/go/redis v1.7.0/go.mod h1:V3x5Jq1jzUcg+UNsRvdmsfuFnit1cfe3Z/PGyq/lm4Y= +cloud.google.com/go/redis v1.8.0/go.mod h1:Fm2szCDavWzBk2cDKxrkmWBqoCiL1+Ctwq7EyqBCA/A= +cloud.google.com/go/retail v1.8.0/go.mod h1:QblKS8waDmNUhghY2TI9O3JLlFk8jybHeV4BF19FrE4= +cloud.google.com/go/retail v1.9.0/go.mod h1:g6jb6mKuCS1QKnH/dpu7isX253absFl6iE92nHwlBUY= +cloud.google.com/go/scheduler v1.4.0/go.mod h1:drcJBmxF3aqZJRhmkHQ9b3uSSpQoltBPGPxGAWROx6s= +cloud.google.com/go/scheduler v1.5.0/go.mod h1:ri073ym49NW3AfT6DZi21vLZrG07GXr5p3H1KxN5QlI= +cloud.google.com/go/secretmanager v1.6.0/go.mod h1:awVa/OXF6IiyaU1wQ34inzQNc4ISIDIrId8qE5QGgKA= +cloud.google.com/go/security v1.5.0/go.mod h1:lgxGdyOKKjHL4YG3/YwIL2zLqMFCKs0UbQwgyZmfJl4= +cloud.google.com/go/security v1.7.0/go.mod h1:mZklORHl6Bg7CNnnjLH//0UlAlaXqiG7Lb9PsPXLfD0= +cloud.google.com/go/security v1.8.0/go.mod h1:hAQOwgmaHhztFhiQ41CjDODdWP0+AE1B3sX4OFlq+GU= +cloud.google.com/go/securitycenter v1.13.0/go.mod h1:cv5qNAqjY84FCN6Y9z28WlkKXyWsgLO832YiWwkCWcU= +cloud.google.com/go/securitycenter v1.14.0/go.mod h1:gZLAhtyKv85n52XYWt6RmeBdydyxfPeTrpToDPw4Auc= +cloud.google.com/go/servicedirectory v1.4.0/go.mod h1:gH1MUaZCgtP7qQiI+F+A+OpeKF/HQWgtAddhTbhL2bs= +cloud.google.com/go/servicedirectory v1.5.0/go.mod h1:QMKFL0NUySbpZJ1UZs3oFAmdvVxhhxB6eJ/Vlp73dfg= +cloud.google.com/go/speech v1.6.0/go.mod h1:79tcr4FHCimOp56lwC01xnt/WPJZc4v3gzyT7FoBkCM= +cloud.google.com/go/speech v1.7.0/go.mod h1:KptqL+BAQIhMsj1kOP2la5DSEEerPDuOP/2mmkhHhZQ= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +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.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y= +cloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeLgDvXzfIXc= +cloud.google.com/go/storage v1.27.0/go.mod h1:x9DOL8TK/ygDUMieqwfhdpQryTeEkhGKMi80i/iqR2s= +cloud.google.com/go/storage v1.41.0 h1:RusiwatSu6lHeEXe3kglxakAmAbfV+rhtPqA6i8RBx0= +cloud.google.com/go/storage v1.41.0/go.mod h1:J1WCa/Z2FcgdEDuPUY8DxT5I+d9mFKsCepp5vR6Sq80= +cloud.google.com/go/talent v1.1.0/go.mod h1:Vl4pt9jiHKvOgF9KoZo6Kob9oV4lwd/ZD5Cto54zDRw= +cloud.google.com/go/talent v1.2.0/go.mod h1:MoNF9bhFQbiJ6eFD3uSsg0uBALw4n4gaCaEjBw9zo8g= +cloud.google.com/go/videointelligence v1.6.0/go.mod h1:w0DIDlVRKtwPCn/C4iwZIJdvC69yInhW0cfi+p546uU= +cloud.google.com/go/videointelligence v1.7.0/go.mod h1:k8pI/1wAhjznARtVT9U1llUaFNPh7muw8QyOUpavru4= +cloud.google.com/go/vision v1.2.0/go.mod h1:SmNwgObm5DpFBme2xpyOyasvBc1aPdjvMk2bBk0tKD0= +cloud.google.com/go/vision/v2 v2.2.0/go.mod h1:uCdV4PpN1S0jyCyq8sIM42v2Y6zOLkZs+4R9LrGYwFo= +cloud.google.com/go/vision/v2 v2.3.0/go.mod h1:UO61abBx9QRMFkNBbf1D8B1LXdS2cGiiCRx0vSpZoUo= +cloud.google.com/go/webrisk v1.4.0/go.mod h1:Hn8X6Zr+ziE2aNd8SliSDWpEnSS1u4R9+xXZmFiHmGE= +cloud.google.com/go/webrisk v1.5.0/go.mod h1:iPG6fr52Tv7sGk0H6qUFzmL3HHZev1htXuWDEEsqMTg= +cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1Vwf+KmJENM0= +cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M= +cosmossdk.io/api v0.7.5 h1:eMPTReoNmGUm8DeiQL9DyM8sYDjEhWzL1+nLbI9DqtQ= +cosmossdk.io/api v0.7.5/go.mod h1:IcxpYS5fMemZGqyYtErK7OqvdM0C8kdW3dq8Q/XIG38= +cosmossdk.io/client/v2 v2.0.0-beta.2 h1:jnTKB0PLb7qunQibijAftwmNjUbsIAf1sPjF5HaCW/E= +cosmossdk.io/client/v2 v2.0.0-beta.2/go.mod h1:8QAyewD7rDWeJGqedFBpeqJ9XLIJAkt1TDhCf1gsN9o= +cosmossdk.io/collections v0.4.0 h1:PFmwj2W8szgpD5nOd8GWH6AbYNi1f2J6akWXJ7P5t9s= +cosmossdk.io/collections v0.4.0/go.mod h1:oa5lUING2dP+gdDquow+QjlF45eL1t4TJDypgGd+tv0= +cosmossdk.io/core v0.11.0 h1:vtIafqUi+1ZNAE/oxLOQQ7Oek2n4S48SWLG8h/+wdbo= +cosmossdk.io/core v0.11.0/go.mod h1:LaTtayWBSoacF5xNzoF8tmLhehqlA9z1SWiPuNC6X1w= +cosmossdk.io/depinject v1.0.0-alpha.4 h1:PLNp8ZYAMPTUKyG9IK2hsbciDWqna2z1Wsl98okJopc= +cosmossdk.io/depinject v1.0.0-alpha.4/go.mod h1:HeDk7IkR5ckZ3lMGs/o91AVUc7E596vMaOmslGFM3yU= +cosmossdk.io/errors v1.0.1 h1:bzu+Kcr0kS/1DuPBtUFdWjzLqyUuCiyHjyJB6srBV/0= +cosmossdk.io/errors v1.0.1/go.mod h1:MeelVSZThMi4bEakzhhhE/CKqVv3nOJDA25bIqRDu/U= +cosmossdk.io/log v1.3.1 h1:UZx8nWIkfbbNEWusZqzAx3ZGvu54TZacWib3EzUYmGI= +cosmossdk.io/log v1.3.1/go.mod h1:2/dIomt8mKdk6vl3OWJcPk2be3pGOS8OQaLUM/3/tCM= +cosmossdk.io/math v1.3.0 h1:RC+jryuKeytIiictDslBP9i1fhkVm6ZDmZEoNP316zE= +cosmossdk.io/math v1.3.0/go.mod h1:vnRTxewy+M7BtXBNFybkuhSH4WfedVAAnERHgVFhp3k= +cosmossdk.io/store v1.1.0 h1:LnKwgYMc9BInn9PhpTFEQVbL9UK475G2H911CGGnWHk= +cosmossdk.io/store v1.1.0/go.mod h1:oZfW/4Fc/zYqu3JmQcQdUJ3fqu5vnYTn3LZFFy8P8ng= +cosmossdk.io/x/circuit v0.1.1 h1:KPJCnLChWrxD4jLwUiuQaf5mFD/1m7Omyo7oooefBVQ= +cosmossdk.io/x/circuit v0.1.1/go.mod h1:B6f/urRuQH8gjt4eLIXfZJucrbreuYrKh5CSjaOxr+Q= +cosmossdk.io/x/evidence v0.1.1 h1:Ks+BLTa3uftFpElLTDp9L76t2b58htjVbSZ86aoK/E4= +cosmossdk.io/x/evidence v0.1.1/go.mod h1:OoDsWlbtuyqS70LY51aX8FBTvguQqvFrt78qL7UzeNc= +cosmossdk.io/x/feegrant v0.1.1 h1:EKFWOeo/pup0yF0svDisWWKAA9Zags6Zd0P3nRvVvw8= +cosmossdk.io/x/feegrant v0.1.1/go.mod h1:2GjVVxX6G2fta8LWj7pC/ytHjryA6MHAJroBWHFNiEQ= +cosmossdk.io/x/tx v0.13.3 h1:Ha4mNaHmxBc6RMun9aKuqul8yHiL78EKJQ8g23Zf73g= +cosmossdk.io/x/tx v0.13.3/go.mod h1:I8xaHv0rhUdIvIdptKIqzYy27+n2+zBVaxO6fscFhys= +cosmossdk.io/x/upgrade v0.1.3 h1:q4XpXc6zp0dX6x74uBtfN6+J7ikaQev5Bla6Q0ADLK8= +cosmossdk.io/x/upgrade v0.1.3/go.mod h1:jOdQhnaY5B8CDUoUbed23/Lre0Dk+r6BMQE40iKlVVQ= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= +filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= +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.2 h1:pZd3neh/EmUzWONb35LxQfvuY7kiSXAq3HQd97+XBn0= +github.com/99designs/keyring v1.2.2/go.mod h1:wes/FrByc8j7lFOAGLGSNEg8f/PaI3cgTBqhFkHUrPk= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= +github.com/BurntSushi/toml v1.3.2/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/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/ComposableFi/go-subkey/v2 v2.0.0-tm03420 h1:oknQF/iIhf5lVjbwjsVDzDByupRhga8nhA3NAmwyHDA= +github.com/ComposableFi/go-subkey/v2 v2.0.0-tm03420/go.mod h1:KYkiMX5AbOlXXYfxkrYPrRPV6EbVUALTQh5ptUOJzu8= +github.com/CosmWasm/wasmvm/v2 v2.1.0 h1:bleLhNA36hM8iPjFJsNRi9RjrQW6MtXafw2+wVjAWAE= +github.com/CosmWasm/wasmvm/v2 v2.1.0/go.mod h1:bMhLQL4Yp9CzJi9A83aR7VO9wockOsSlZbT4ztOl6bg= +github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= +github.com/DataDog/datadog-go v4.8.3+incompatible h1:fNGaYSuObuQb5nzeTQqowRAd9bpDIRRV4/gUtIBjh8Q= +github.com/DataDog/datadog-go v4.8.3+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= +github.com/DataDog/zstd v1.5.5 h1:oWf5W7GtOLgp6bciQYDmhHHjdhYkALu6S/5Ni9ZgSvQ= +github.com/DataDog/zstd v1.5.5/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= +github.com/DimitrisJim/go-substrate-rpc-client/v4 v4.0.0-20240717100841-406da076c1d5 h1:u0DTEVAbtD6ZecV9gAgzC6f7sfW+vi+BgdHivIb43Bk= +github.com/DimitrisJim/go-substrate-rpc-client/v4 v4.0.0-20240717100841-406da076c1d5/go.mod h1:vJqS/VE2+iAiQlp0O2l+wW9QkHDotcaSQWaU4Ako6Ss= +github.com/DimitrisJim/interchaintest/v8 v8.0.0-20240717102845-beba523a47ff h1:ITEAJekoSKe5kVo2vUwf37XxnuPwTTzqfd4wLaR2bkk= +github.com/DimitrisJim/interchaintest/v8 v8.0.0-20240717102845-beba523a47ff/go.mod h1:5AnXR5CAbB/Q0syC0akZR4pyPov4nOaB8eifZPz6Pds= +github.com/FactomProject/basen v0.0.0-20150613233007-fe3947df716e h1:ahyvB3q25YnZWly5Gq1ekg6jcmWaGj/vG/MhF4aisoc= +github.com/FactomProject/basen v0.0.0-20150613233007-fe3947df716e/go.mod h1:kGUqhHd//musdITWjFvNTHn90WG9bMLBEPQZ17Cmlpw= +github.com/FactomProject/btcutilecc v0.0.0-20130527213604-d3a63a5752ec h1:1Qb69mGp/UtRPn422BH4/Y4Q3SLUrD9KHuDkm8iodFc= +github.com/FactomProject/btcutilecc v0.0.0-20130527213604-d3a63a5752ec/go.mod h1:CD8UlnlLDiqb36L110uqiP2iSflVjx9g/3U9hCI4q2U= +github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= +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/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 v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA= +github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8= +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/adlio/schema v1.3.3 h1:oBJn8I02PyTB466pZO1UZEn1TV5XLlifBSyMrmHl/1I= +github.com/adlio/schema v1.3.3/go.mod h1:1EsRssiv9/Ce2CMzq5DoL7RiMshhuigQxrR4DMV9fHg= +github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= +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/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/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/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.5.1 h1:AxIx0HGi4VZ3I02jr78j5lZ3M6x1E0Ivxa6b0pUUh7o= +github.com/avast/retry-go/v4 v4.5.1/go.mod h1:/sipNsvNB3RRuT5iNcb6h73nw3IBmXJ/H3XrCQYSOpc= +github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= +github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go v1.44.122/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= +github.com/aws/aws-sdk-go v1.49.0 h1:g9BkW1fo9GqKfwg2+zCD+TW/D36Ux+vtfJ8guF4AYmY= +github.com/aws/aws-sdk-go v1.49.0/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= +github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= +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/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/bgentry/speakeasy v0.2.0 h1:tgObeVOf8WAvtuAX6DhJ4xks4CFNwPDZiqzGqIHE51E= +github.com/bgentry/speakeasy v0.2.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bits-and-blooms/bitset v1.12.0 h1:U/q1fAF7xXRhFCrhROzIfffYnu+dlS38vCZtmFVPHmA= +github.com/bits-and-blooms/bitset v1.12.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= +github.com/btcsuite/btcd/btcec/v2 v2.3.3 h1:6+iXlDKE8RMtKsvK0gshlXIuPbyWM/h84Ensb7o3sC0= +github.com/btcsuite/btcd/btcec/v2 v2.3.3/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= +github.com/btcsuite/btcd/btcutil v1.1.3 h1:xfbtw8lwpp0G6NwSHb+UE67ryTFHJAiNuipusjXSohQ= +github.com/btcsuite/btcd/btcutil v1.1.3/go.mod h1:UR7dsSJzJUfMmFiiLlIrMq1lS9jh9EdCV7FStZSnpi0= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +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/bufbuild/protocompile v0.6.0 h1:Uu7WiSQ6Yj9DbkdnOe7U4mNKp58y9WDMKDn28/ZlunY= +github.com/bufbuild/protocompile v0.6.0/go.mod h1:YNP35qEYoYGme7QMtz5SBCoN4kL4g12jTtjuzRNdjpE= +github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= +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.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +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.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.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/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM= +github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI= +github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04= +github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8= +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/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/cmars/basen v0.0.0-20150613233007-fe3947df716e h1:0XBUw73chJ1VYSsfvcPvVT7auykAJce9FpRr10L6Qhw= +github.com/cmars/basen v0.0.0-20150613233007-fe3947df716e/go.mod h1:P13beTBKr5Q18lJe1rIoLUqjM+CB1zYrRg44ZqGuQSA= +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/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/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= +github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f h1:otljaYPt5hWxV3MUfO5dFPFiOXg9CyG5/kCfayTqsJ4= +github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= +github.com/cockroachdb/errors v1.11.3 h1:5bA+k2Y6r+oz/6Z/RFlNeVCesGARKuC6YymtcDrbC/I= +github.com/cockroachdb/errors v1.11.3/go.mod h1:m4UIW4CDjx+R5cybPsNrRbreomiFqt8o1h1wUVazSd8= +github.com/cockroachdb/fifo v0.0.0-20240616162244-4768e80dfb9a h1:f52TdbU4D5nozMAhO9TvTJ2ZMCXtN4VIAmfrrZ0JXQ4= +github.com/cockroachdb/fifo v0.0.0-20240616162244-4768e80dfb9a/go.mod h1:9/y3cnZ5GKakj/H4y9r9GTjCvAFta7KLgSHPJJYc52M= +github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE= +github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= +github.com/cockroachdb/pebble v1.1.1 h1:XnKU22oiCLy2Xn8vp1re67cXg4SAasg/WDt1NtcRFaw= +github.com/cockroachdb/pebble v1.1.1/go.mod h1:4exszw1r40423ZsmkG/09AFEG83I0uDgfujJdbL6kYU= +github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30= +github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= +github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= +github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= +github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= +github.com/cometbft/cometbft v0.38.10 h1:2ePuglchT+j0Iao+cfmt/nw5U7K2lnGDzXSUPGVdXaU= +github.com/cometbft/cometbft v0.38.10/go.mod h1:jHPx9vQpWzPHEAiYI/7EDKaB1NXhK6o3SArrrY8ExKc= +github.com/cometbft/cometbft-db v0.12.0 h1:v77/z0VyfSU7k682IzZeZPFZrQAKiQwkqGN0QzAjMi0= +github.com/cometbft/cometbft-db v0.12.0/go.mod h1:aX2NbCrjNVd2ZajYxt1BsiFf/Z+TQ2MN0VxdicheYuw= +github.com/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvAXZJ/eg= +github.com/containerd/continuity v0.3.0/go.mod h1:wJEAIwKOm/pBZuBd0JmeTvnLquTB1Ag8espWhkykbPM= +github.com/coreos/go-semver v0.2.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/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +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-db v1.0.2 h1:hwMjozuY1OlJs/uh6vddqnk9j7VamLv+0DBlbEXbAKs= +github.com/cosmos/cosmos-db v1.0.2/go.mod h1:Z8IXcFJ9PqKK6BIsVOB3QXtkKoqUOp1vRvPT39kOXEA= +github.com/cosmos/cosmos-proto v1.0.0-beta.5 h1:eNcayDLpip+zVLRLYafhzLvQlSmyab+RC5W7ZfmxJLA= +github.com/cosmos/cosmos-proto v1.0.0-beta.5/go.mod h1:hQGLpiIUloJBMdQMMWb/4wRApmI9hjHH05nefC0Ojec= +github.com/cosmos/cosmos-sdk v0.50.7 h1:LsBGKxifENR/DN4E1RZaitsyL93HU44x0p8EnMHp4V4= +github.com/cosmos/cosmos-sdk v0.50.7/go.mod h1:84xDDJEHttRT7NDGwBaUOLVOMN0JNE9x7NbsYIxXs1s= +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/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.5.0 h1:SDVwzEqZDDBoslaeZg+dGE55hdzHfgUA40pEanMh52o= +github.com/cosmos/gogoproto v1.5.0/go.mod h1:iUM31aofn3ymidYG6bUR5ZFrk+Om8p5s754eMUcyp8I= +github.com/cosmos/iavl v1.2.0 h1:kVxTmjTh4k0Dh1VNL046v6BXqKziqMDzxo93oh3kOfM= +github.com/cosmos/iavl v1.2.0/go.mod h1:HidWWLVAtODJqFD6Hbne2Y0q3SdxByJepHUOeoH4LiI= +github.com/cosmos/ibc-go/modules/capability v1.0.1 h1:ibwhrpJ3SftEEZRxCRkH0fQZ9svjthrX2+oXdZvzgGI= +github.com/cosmos/ibc-go/modules/capability v1.0.1/go.mod h1:rquyOV262nGJplkumH+/LeYs04P3eV8oB7ZM4Ygqk4E= +github.com/cosmos/ics23/go v0.10.0 h1:iXqLLgp2Lp+EdpIuwXTYIQU+AiHj9mOC2X9ab++bZDM= +github.com/cosmos/ics23/go v0.10.0/go.mod h1:ZfJSmng/TBNTBkFemHHHj5YY7VAU/MBU980F4VU1NG0= +github.com/cosmos/ledger-cosmos-go v0.13.3 h1:7ehuBGuyIytsXbd4MP43mLeoN2LTOEnk5nvue4rK+yM= +github.com/cosmos/ledger-cosmos-go v0.13.3/go.mod h1:HENcEP+VtahZFw38HZ3+LS3Iv5XV6svsnkk9vdJtLr8= +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.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +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/danieljoos/wincred v1.2.1 h1:dl9cBrupW8+r5250DYkYxocLeZ1Y4vB1kxgtjxw8GQs= +github.com/danieljoos/wincred v1.2.1/go.mod h1:uGaFL9fDn3OLTvzCGulzE+SzjEe5NGlh5FdCcyfPwps= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +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/deckarep/golang-set/v2 v2.1.0 h1:g47V4Or+DUdzbs8FxCCmgb6VYd+ptPAngjM6dtGktsI= +github.com/deckarep/golang-set/v2 v2.1.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= +github.com/decred/base58 v1.0.5 h1:hwcieUM3pfPnE/6p3J100zoRfGkQxBulZHo7GZfOqic= +github.com/decred/base58 v1.0.5/go.mod h1:s/8lukEHFA6bUQQb/v3rjUySJ2hu+RioCzLukAVkrfw= +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/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= +github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= +github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= +github.com/decred/dcrd/dcrec/secp256k1/v2 v2.0.1 h1:18HurQ6DfHeNvwIjvOmrgr44bPdtVaQAe/WWwHg9goM= +github.com/decred/dcrd/dcrec/secp256k1/v2 v2.0.1/go.mod h1:XmyzkaXBy7ZvHdrTAlXAjpog8qKSAWa3ze7yqzWmgmc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= +github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f/go.mod h1:xH/i4TFMt8koVQZ6WFms69WAsDWr2XsYL3Hkl7jkoLE= +github.com/desertbit/timer v1.0.1 h1:yRpYNn5Vaaj6QXecdLMPMJsW81JLiI1eokUft5nBmeo= +github.com/desertbit/timer v1.0.1/go.mod h1:htRrYeY5V/t4iu1xCJ5XsQvp4xve8QulXXctAzxqcwE= +github.com/dgraph-io/badger/v4 v4.2.0 h1:kJrlajbXXL9DFTNuhhu9yCx7JJa4qpYWxtE8BzuWsEs= +github.com/dgraph-io/badger/v4 v4.2.0/go.mod h1:qfCqhPoWDFJRx1gp5QwwyGo8xk1lbHUxvK9nK0OGAak= +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/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0= +github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= +github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= +github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker v24.0.7+incompatible h1:Wo6l37AuwP3JaMnZa226lzVXGA3F9Ig1seQen0cKYlM= +github.com/docker/docker v24.0.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= +github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= +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/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/dvsekhvalnov/jose2go v1.7.0 h1:bnQc8+GMnidJZA8zc6lLEAb4xNrIqHwO+9TzqvtQZPo= +github.com/dvsekhvalnov/jose2go v1.7.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 v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= +github.com/emicklei/dot v1.6.2 h1:08GN+DD79cy/tzN6uLCT84+2Wk9u+wvqP+Hkx/dIR8A= +github.com/emicklei/dot v1.6.2/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s= +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= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +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.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/ethereum/go-ethereum v1.13.14 h1:EwiY3FZP94derMCIam1iW4HFVrSgIcpsu0HwTQtm6CQ= +github.com/ethereum/go-ethereum v1.13.14/go.mod h1:TN8ZiHrdJwSe8Cb6x+p0hs5CxhJZPbqB7hHkaUXcmIU= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= +github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/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/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +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.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/getsentry/sentry-go v0.28.1 h1:zzaSm/vHmGllRM6Tpx1492r0YDzauArdBfkJRtY6P5k= +github.com/getsentry/sentry-go v0.28.1/go.mod h1:1fQZ+7l7eeJ3wYi82q5Hg8GqAPgefRq+FP/QhafYVgg= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +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/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= +github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= +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= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= +github.com/go-kit/kit v0.13.0 h1:OoneCcHKHQ03LfBpoQCUfCluwd2Vt3ohz+kvbJneZAU= +github.com/go-kit/kit v0.13.0/go.mod h1:phqEHMMUbyrCFCTgH48JueqrM3md2HcAZ8N3XE4FKDg= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +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.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= +github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= +github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= +github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +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-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= +github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +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/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= +github.com/gogo/googleapis v1.4.1-0.20201022092350-68b0159b7869/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c= +github.com/gogo/googleapis v1.4.1 h1:1Yx4Myt7BxzvUr5ldGSbwYiZG6t9wGBZ+8/fX3Wvtq0= +github.com/gogo/googleapis v1.4.1/go.mod h1:2lpHqI5OcWCtVElxXnPt+s8oJvMpySlOyM6xDCrzib4= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/glog v1.2.1 h1:OptwRhECazUx5ix5TTWC3EZhsZEHWcYWY4FQHTIubm4= +github.com/golang/glog v1.2.1/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= +github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/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= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +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= +github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +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/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk= +github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb/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/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/flatbuffers v24.3.25+incompatible h1:CX395cjN9Kke9mmalRoL3d81AtFUxJM+yDthflgJGkI= +github.com/google/flatbuffers v24.3.25+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= +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= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +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.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/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/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +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.3.3 h1:DIhPTQrbPkgs2yJYdXU/eNACCG5DVQjySNRNlflZ9Fc= +github.com/google/martian/v3 v3.3.3/go.mod h1:iEPrYcgCF7jA9OtScMFQyAlZZ4YXTKEtJ1E6RWzmBA0= +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= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +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-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-20231023181126-ff6d637d2a7b h1:RMpPgZTSApbPf7xaVel+QkoGPRLFLrwFO89uDUHEGf0= +github.com/google/pprof v0.0.0-20231023181126-ff6d637d2a7b/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= +github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= +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/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= +github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= +github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg= +github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= +github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= +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.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM= +github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM= +github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c= +github.com/googleapis/gax-go/v2 v2.5.1/go.mod h1:h6B0KMMFNtI2ddbGJn3T3ZbwkeT6yqEF02fYlzkUCyo= +github.com/googleapis/gax-go/v2 v2.6.0/go.mod h1:1mjbznJAPHFpesgE5ucqfYEscaz5kMdcIDwU/6+DDoY= +github.com/googleapis/gax-go/v2 v2.12.5 h1:8gw9KZK8TiVKB6q3zHY3SBzLnrGp6HQjyfYBYGmXdxA= +github.com/googleapis/gax-go/v2 v2.12.5/go.mod h1:BUDKcWo+RaKq5SC9vVYL0wLADa3VcfswbOMMRmB9H3E= +github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= +github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyEE= +github.com/gorilla/handlers v1.5.2/go.mod h1:dX+xVpaxdSw+q0Qek8SSsl3dfMk3jNddUkMzo0GtH0w= +github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= +github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= +github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= +github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +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.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= +github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +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= +github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c h1:6rhixN/i8ZofjG1Y75iExal34USq5p+wiN1tpie8IrU= +github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c/go.mod h1:NMPJylDgVpX0MLRlPy15sqSwOFv/U1GZ2m21JhFfek0= +github.com/gtank/merlin v0.1.1-0.20191105220539-8318aed1a79f/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s= +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.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= +github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +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-getter v1.7.4 h1:3yQjWuxICvSpYwqSayAdKRFcvBl1y/vogCxczWSmix0= +github.com/hashicorp/go-getter v1.7.4/go.mod h1:W7TalhMmbPmsSMdNjD0ZskARur/9GJ17cfHTRtXV744= +github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= +github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +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-metrics v0.5.3 h1:M5uADWMOGCTUNU1YuC4hfknOeHNaX54LDm4oYSucoNE= +github.com/hashicorp/go-metrics v0.5.3/go.mod h1:KEjodfebIOuBYSAe/bHTm+HChmKSxAOXPBieMLYozDE= +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-plugin v1.6.1 h1:P7MR2UP6gNKGPp+y7EZw2kOiq4IR9WiqLvp0XOsVdwI= +github.com/hashicorp/go-plugin v1.6.1/go.mod h1:XPHFku2tFo3o3QKFgSYo+cghcUhw1NA1hZyMK0PWAw0= +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-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/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE= +github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +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 v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= +github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= +github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= +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/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= +github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= +github.com/hdevalence/ed25519consensus v0.2.0 h1:37ICyZqdyj0lAZ8P4D1d1id3HqbbG1N3iBb1Tb4rdcU= +github.com/hdevalence/ed25519consensus v0.2.0/go.mod h1:w3BHWjwJbFU29IRHL1Iqkw3sus+7FctEyM4RqDxYNzo= +github.com/holiman/uint256 v1.2.4 h1:jUc4Nk8fm9jZabQuqr2JzednajVmBpC+oiTiXZJEApU= +github.com/holiman/uint256 v1.2.4/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= +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/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSASxEI= +github.com/iancoleman/strcase v0.3.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/icza/dyno v0.0.0-20230330125955-09f820a8d9c0 h1:nHoRIX8iXob3Y2kdt9KsjyIb7iApSvb3vgsd93xb5Ow= +github.com/icza/dyno v0.0.0-20230330125955-09f820a8d9c0/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.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= +github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s= +github.com/ipfs/go-cid v0.4.1/go.mod h1:uQHwDeX4c6CtyrFwdqyhpNcxVewur1M7l7fNU7LKwZk= +github.com/jhump/protoreflect v1.15.3 h1:6SFRuqU45u9hIZPJAoZ8c28T3nK64BNdp9w6jFonzls= +github.com/jhump/protoreflect v1.15.3/go.mod h1:4ORHmSBmlCW8fh3xHmJMGyul1zNqZK4Elxc8qKP+p1k= +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/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/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/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/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/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= +github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= +github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc= +github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +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/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.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +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/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= +github.com/lib/pq v1.10.9/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 v0.32.1 h1:wy1J4kZIZxOaej6NveTWCZmHiJ/kY7GoAqXgqNCnPps= +github.com/libp2p/go-libp2p v0.32.1/go.mod h1:hXXC3kXPlBZ1eu8Q2hptGrMB4mZ3048JUoS4EKaHW5c= +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/linxGnu/grocksdb v1.9.2 h1:O3mzvO0wuzQ9mtlHbDrShixyVjVbmuqTjFrzlf43wZ8= +github.com/linxGnu/grocksdb v1.9.2/go.mod h1:QYiYypR2d4v63Wj1adOOfzglnoII0gLj3PNh4fZkcFA= +github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= +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.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.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-runewidth v0.0.2/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.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y= +github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM= +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/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 v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= +github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +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/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= +github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= +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.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/moby/term v0.0.0-20221205130635-1aeaba878587 h1:HfkjXDfhgVaN5rmueG8cL8KKeFNecRCXFhaJ2qZ5SKA= +github.com/moby/term v0.0.0-20221205130635-1aeaba878587/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +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/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +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.1.0 h1:pVx9xoSPqEIQG8o+UbAe7DNi51oej1NtK+aGkbLYxPE= +github.com/multiformats/go-base32 v0.1.0/go.mod h1:Kj3tFY6zNr+ABYMqeUNeGvkIC/UYgtWibDcT0rExnbI= +github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9rQyccr0= +github.com/multiformats/go-base36 v0.2.0/go.mod h1:qvnKE++v+2MWCfePClUEjE78Z7P2a1UV0xHgWc0hkp4= +github.com/multiformats/go-multiaddr v0.12.0 h1:1QlibTFkoXJuDjjYsMHhE73TnzJQl8FSWatk/0gxGzE= +github.com/multiformats/go-multiaddr v0.12.0/go.mod h1:WmZXgObOQOYp9r3cslLlppkrz1FYSHmE834dfz/lWu8= +github.com/multiformats/go-multibase v0.2.0 h1:isdYCVLvksgWlMW9OZRYJEa9pZETFivncJHmHnnd87g= +github.com/multiformats/go-multibase v0.2.0/go.mod h1:bFBZX4lKCA/2lyOFSAoKH5SS6oPyjtnzK/XTFDPkNuk= +github.com/multiformats/go-multicodec v0.9.0 h1:pb/dlPnzee/Sxv/j4PmkDRxCOi3hXTz3IbPKOXWJkmg= +github.com/multiformats/go-multicodec v0.9.0/go.mod h1:L3QTQvMIaVBkXOXXtVmYE+LI16i14xuaojr/H7Ai54k= +github.com/multiformats/go-multihash v0.2.3 h1:7Lyc8XfX/IY2jWb/gI7JP+o7JEq9hOa7BFvVU9RSh+U= +github.com/multiformats/go-multihash v0.2.3/go.mod h1:dXgKXCXjBzdscBLk9JkjINiEsCKRVch90MdaGiKsvSM= +github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/nEGOHFS8= +github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +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/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= +github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= +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/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= +github.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY= +github.com/nxadm/tail v1.4.11/go.mod h1:OTaG3NK980DZzxbRq6lEuzgU+mug70nY11sMd4JXXHc= +github.com/oasisprotocol/curve25519-voi v0.0.0-20230904125328-1f23a7beb09a h1:dlRvE5fWabOchtH7znfiFCcOvmIYgOeAS5ifBXBlh9Q= +github.com/oasisprotocol/curve25519-voi v0.0.0-20230904125328-1f23a7beb09a/go.mod h1:hVoHR2EVESiICEMbg137etN/Lx+lSrHPTD39Z/uE+2s= +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/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= +github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= +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.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= +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.27.8 h1:gegWiwZjBsf2DgiSbf5hpokZ98JVDMcWkUiigk6/KXc= +github.com/onsi/gomega v1.27.8/go.mod h1:2J8vzI/s+2shY9XHRApDkdgPo1TKT7P2u6fXeJKFnNQ= +github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= +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.1.0-rc5 h1:Ygwkfw9bpDvs+c9E34SdgGOj41dX/cbdlwvlWt0pnFI= +github.com/opencontainers/image-spec v1.1.0-rc5/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= +github.com/opencontainers/runc v1.1.3 h1:vIXrkId+0/J2Ymu2m7VjGvbSlAId9XNRPhn2p4b+d8w= +github.com/opencontainers/runc v1.1.3/go.mod h1:1J5XiS+vdZ3wCyZybsuxXZWGrgSr8fFJHLXuG2PsnNg= +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/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/ory/dockertest v3.3.5+incompatible h1:iLLK6SQwIhcbrG783Dghaaa3WPzGc+4Emza6EbVUUGA= +github.com/ory/dockertest v3.3.5+incompatible/go.mod h1:1vX4m9wsvi00u5bseYwXaSnhNrne+V0E6LAcBILJdPs= +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.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.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= +github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= +github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o= +github.com/petermattis/goid v0.0.0-20240607163614-bb94eb51e7a7 h1:CtBLeckhC0zAXgp5V8uR30CNYH0JgCJoxCg5+6i2zQk= +github.com/petermattis/goid v0.0.0-20240607163614-bb94eb51e7a7/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= +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/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= +github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +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/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +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 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.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= +github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= +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/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= +github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= +github.com/prometheus/common v0.2.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.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= +github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= +github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= +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.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.3.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= +github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= +github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +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/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-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +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.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= +github.com/rs/cors v1.11.0 h1:0B9GE/r9Bc2UxRMMtymBkHTenPkHDv0CW4Y98GBY+po= +github.com/rs/cors v1.11.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= +github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8= +github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= +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/locafero v0.6.0 h1:ON7AQg37yzcRPU69mt7gwhFEBwxI6P9T4Qu3N51bwOk= +github.com/sagikazarmark/locafero v0.6.0/go.mod h1:77OmuIc6VTraTXKXIs/uvUxKGUXjE1GbemJYHqdNjX0= +github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= +github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= +github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= +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/shamaton/msgpack/v2 v2.2.0 h1:IP1m01pHwCrMa6ZccP9B3bqxEMKMSmMVAVKk54g3L/Y= +github.com/shamaton/msgpack/v2 v2.2.0/go.mod h1:6khjYnkx73f7VQU7wjcFS9DFjs+59naVWJv1TB7qdOI= +github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU= +github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+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.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.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/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/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= +github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= +github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= +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.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= +github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= +github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= +github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= +github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= +github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +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.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= +github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= +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/handy v0.0.0-20190108123426-d5acb3125c2a/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.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.1.5-0.20170601210322-f6abca593680/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +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= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +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.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +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/tidwall/btree v1.7.0 h1:L1fkJH/AuEh5zBnnBbmTwQ5Lt+bRJ5A8EWecslvo9iI= +github.com/tidwall/btree v1.7.0/go.mod h1:twD9XRA5jj9VUQGELzDO4HPQTNJsoWWfYEL+EUQ2cKY= +github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= +github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= +github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= +github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= +github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= +github.com/tyler-smith/go-bip32 v1.0.0 h1:sDR9juArbUgX+bO/iblgZnMPeWY1KZMUC2AFUJdv5KE= +github.com/tyler-smith/go-bip32 v1.0.0/go.mod h1:onot+eHknzV4BVPwrzqY5OoVpyCvnwD7lMawL5aQupE= +github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= +github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U= +github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= +github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= +github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= +github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8= +github.com/ulikunitz/xz v0.5.11/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/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +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.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/zondax/hid v0.9.2 h1:WCJFnEDMiqGF64nlZz28E9qLVZ0KSJ7xpc5DLEyma2U= +github.com/zondax/hid v0.9.2/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM= +github.com/zondax/ledger-go v0.14.3 h1:wEpJt2CEcBJ428md/5MgSLsXLBos98sBOyxNmCjfUCw= +github.com/zondax/ledger-go v0.14.3/go.mod h1:IKKaoxupuB43g4NxeQmbLXv7T9AlQyie1UpHb342ycI= +go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.4.0-alpha.1 h1:3yrqQzbRRPFPdOMWS/QQIVxVnzSkAZQYeWlZFv1kbj4= +go.etcd.io/bbolt v1.4.0-alpha.1/go.mod h1:S/Z/Nm3iuOnyO1W4XuFfPci51Gj6F1Hv0z8hisyYYOw= +go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= +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= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +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/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 h1:4Pp6oUg3+e/6M4C0A/3kJ2VYa++dsWVTtGgLVj5xtHg= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0/go.mod h1:Mjt1i1INqiaoZOMGR1RIUJN+i3ChKoFRqzrRQhlkbs0= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= +go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo= +go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= +go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI= +go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco= +go.opentelemetry.io/otel/sdk v1.24.0 h1:YMPPDNymmQN3ZgczicBY3B6sf9n62Dlj9pWD3ucgoDw= +go.opentelemetry.io/otel/sdk v1.24.0/go.mod h1:KVrIYw6tEubO9E96HQpcmpTKDVn9gdv35HoYiQWGDFg= +go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI= +go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= +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/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +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.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +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.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +golang.org/x/crypto v0.0.0-20170613210332-850760c427c5/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-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-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +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-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-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= +golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/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= +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= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +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/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw= +golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 h1:yixxcjnhBmY0nkL253HFVIm0JsFHwrHdT3Yh6szTnfY= +golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI= +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= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +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= +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.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.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8= +golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +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-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= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +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-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-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= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +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= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +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-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-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +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-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= +golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= +golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= +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= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +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-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.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= +golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= +golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.1.0/go.mod h1:G9FE4dLTsbXUu90h/Pf85g4w1D+SSAgR+q46nJZ8M4A= +golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs= +golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +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= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +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= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/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-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-20190624142023-c5567b49c5d0/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-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-20191026070338-33540a1f6037/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-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-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-20200930185726-fdedc70b468f/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-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/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-20210423082822-04245dca01da/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-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-20210823070655-63515b42dcdf/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-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/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-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-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/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-20220328115105-d36c6a25d886/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-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/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-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/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-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= +golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= +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= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +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/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +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.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +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-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= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +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-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-20191108193012-7d206e10da11/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= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +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-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= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +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-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-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/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.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA= +golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= +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-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= +golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= +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= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +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.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= +google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= +google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g= +google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA= +google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8= +google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs= +google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= +google.golang.org/api v0.77.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= +google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw= +google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg= +google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o= +google.golang.org/api v0.85.0/go.mod h1:AqZf8Ep9uZ2pyTvgL+x0D3Zt0eoT9b5E8fmzfu6FO2g= +google.golang.org/api v0.90.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= +google.golang.org/api v0.93.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= +google.golang.org/api v0.95.0/go.mod h1:eADj+UBuxkh5zlrSntJghuNeg8HwQ1w5lTKkuqaETEI= +google.golang.org/api v0.96.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= +google.golang.org/api v0.97.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= +google.golang.org/api v0.98.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= +google.golang.org/api v0.100.0/go.mod h1:ZE3Z2+ZOr87Rx7dqFsdRQkRBk36kDtp/h+QpHbB7a70= +google.golang.org/api v0.186.0 h1:n2OPp+PPXX0Axh4GuSsL5QL8xQCTb2oDwyzPnQvqUug= +google.golang.org/api v0.186.0/go.mod h1:hvRbBmgoje49RV3xqVXrmP6w93n6ehGgIVPYrGtBFFc= +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= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +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/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= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +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= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +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-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-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-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-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +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-20210924002016-3dee208752a0/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-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220314164441-57ef72a4c106/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= +google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= +google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220617124728-180714bec0ad/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220628213854-d9e0b6570c03/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220722212130-b98a9ff5e252/go.mod h1:GkXuJDJ6aQ7lnJcRF+SJVgFdQhypqgl3LB1C9vabdRE= +google.golang.org/genproto v0.0.0-20220801145646-83ce21fca29f/go.mod h1:iHe1svFLAZg9VWz891+QbRMwUv9O/1Ww+/mngYeThbc= +google.golang.org/genproto v0.0.0-20220815135757-37a418bb8959/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220817144833-d7fd3f11b9b1/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220829144015-23454907ede3/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220829175752-36a9c930ecbf/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220913154956-18f8339a66a5/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220914142337-ca0e39ece12f/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220915135415-7fd63a7952de/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220916172020-2692e8806bfa/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220919141832-68c03719ef51/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220920201722-2b89144ce006/go.mod h1:ht8XFiar2npT/g4vkk7O0WYS1sHOHbdujxbEp7CJWbw= +google.golang.org/genproto v0.0.0-20220926165614-551eb538f295/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= +google.golang.org/genproto v0.0.0-20220926220553-6981cbe3cfce/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= +google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e/go.mod h1:3526vdqwhZAwq4wsRUaVG555sVgsNmIjRtO7t/JH29U= +google.golang.org/genproto v0.0.0-20221014173430-6e2ab493f96b/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= +google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= +google.golang.org/genproto v0.0.0-20221025140454-527a21cfbd71/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= +google.golang.org/genproto v0.0.0-20240701130421-f6361c86f094 h1:6whtk83KtD3FkGrVb2hFXuQ+ZMbCNdakARIn/aHMmG8= +google.golang.org/genproto v0.0.0-20240701130421-f6361c86f094/go.mod h1:Zs4wYw8z1zr6RNF4cwYb31mvN/EGaKAdQjNCF3DW6K4= +google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 h1:0+ozOGcrp+Y8Aq8TLNN2Aliibms5LEzsq99ZZmAGYm0= +google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094/go.mod h1:fJ/e3If/Q67Mj99hin0hMhiNyCRmt6BQ2aWIJshUSJw= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 h1:BwIjyKYGsK9dMCBOorzRri8MQwmi7mT9rGHsCEinZkA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= +google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +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.44.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.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= +google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= +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= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +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/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.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +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-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.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= +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= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +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/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= +gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= +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= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +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= +launchpad.net/gocheck v0.0.0-20140225173054-000000000087 h1:Izowp2XBH6Ya6rv+hqbceQyw/gSGoXfH/UPoTGduL54= +launchpad.net/gocheck v0.0.0-20140225173054-000000000087/go.mod h1:hj7XX3B/0A+80Vse0e+BUHsHMTEhd0O4cpUHr/e/BUM= +lukechampine.com/blake3 v1.2.1 h1:YuqqRuaqsGV71BV/nm9xlI0MKUv4QC54jQnBChWbGnI= +lukechampine.com/blake3 v1.2.1/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= +lukechampine.com/uint128 v1.3.0 h1:cDdUVfRwDUDovz610ABgFD17nXD4/uDgVHl2sC3+sbo= +lukechampine.com/uint128 v1.3.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= +modernc.org/cc/v3 v3.41.0 h1:QoR1Sn3YWlmA1T4vLaKZfawdVtSiGx8H+cEojbC7v1Q= +modernc.org/cc/v3 v3.41.0/go.mod h1:Ni4zjJYJ04CDOhG7dn640WGfwBzfE0ecX8TyMB0Fv0Y= +modernc.org/ccgo/v3 v3.16.15 h1:KbDR3ZAVU+wiLyMESPtbtE/Add4elztFyfsWoNTgxS0= +modernc.org/ccgo/v3 v3.16.15/go.mod h1:yT7B+/E2m43tmMOT51GMoM98/MtHIcQQSleGnddkUNI= +modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk= +modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= +modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM= +modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= +modernc.org/libc v1.37.1 h1:Wi3qhejztgB3hOYQGMc8NwePETHAWXmlU+GQnBNTrw8= +modernc.org/libc v1.37.1/go.mod h1:YAXkAZ8ktnkCKaN9sw/UDeUVkGYJ/YquGO4FTi5nmHE= +modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= +modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= +modernc.org/memory v1.7.2 h1:Klh90S215mmH8c9gO98QxQFsY+W451E8AnzjoE2ee1E= +modernc.org/memory v1.7.2/go.mod h1:NO4NVCQy0N7ln+T9ngWqOQfi7ley4vpwvARR+Hjw95E= +modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4= +modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= +modernc.org/sqlite v1.28.0 h1:Zx+LyDDmXczNnEQdvPuEfcFVA2ZPyaD7UCZDjef3BHQ= +modernc.org/sqlite v1.28.0/go.mod h1:Qxpazz0zH8Z1xCFyi5GSL3FzbtZ3fvbjmywNogldEW0= +modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA= +modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0= +modernc.org/tcl v1.15.2 h1:C4ybAYCGJw968e+Me18oW55kD/FexcHbqH2xak1ROSY= +modernc.org/tcl v1.15.2/go.mod h1:3+k/ZaEbKrC8ePv8zJWPtBSW0V7Gg9g8rkmhI1Kfs3c= +modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= +modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= +modernc.org/z v1.7.3 h1:zDJf6iHjrnB+WRD88stbXokugjyc0/pB91ri1gO6LZY= +modernc.org/z v1.7.3/go.mod h1:Ipv4tsdxZRbQyLq9Q1M6gdbkxYzdlrciF2Hi/lS7nWE= +nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= +nhooyr.io/websocket v1.8.11 h1:f/qXNc2/3DpoSZkHt1DQu6rj4zGC8JmkkLkWss0MgN0= +nhooyr.io/websocket v1.8.11/go.mod h1:rN9OFWIUwuxg4fR5tELlYC04bXYowCP9GX47ivo2l+c= +pgregory.net/rapid v1.1.0 h1:CMa0sjHSru3puNx+J0MIAuiiEV4N0qj8/cMWGBBCsjw= +pgregory.net/rapid v1.1.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +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.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= +sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= +sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= diff --git a/e2e/internal/directories/directories.go b/e2e/internal/directories/directories.go new file mode 100644 index 00000000000..d4724ec280e --- /dev/null +++ b/e2e/internal/directories/directories.go @@ -0,0 +1,40 @@ +package directories + +import ( + "fmt" + "os" + "path" + "strings" + "testing" +) + +const ( + e2eDir = "e2e" + + // DefaultGenesisExportPath is the default path to which Genesis debug files will be exported to. + DefaultGenesisExportPath = "diagnostics/genesis.json" +) + +// E2E finds the e2e directory above the test. +func E2E(t *testing.T) (string, error) { + t.Helper() + + wd, err := os.Getwd() + if err != nil { + return "", err + } + + const maxAttempts = 100 + count := 0 + for ; !strings.HasSuffix(wd, e2eDir) && count < maxAttempts; wd = path.Dir(wd) { + count++ + } + + // arbitrary value to avoid getting stuck in an infinite loop if this is called + // in a context where the e2e directory does not exist. + if count > maxAttempts { + return "", fmt.Errorf("unable to find e2e directory after %d tries", maxAttempts) + } + + return wd, nil +} diff --git a/e2e/relayer/relayer.go b/e2e/relayer/relayer.go new file mode 100644 index 00000000000..cd959428690 --- /dev/null +++ b/e2e/relayer/relayer.go @@ -0,0 +1,197 @@ +package relayer + +import ( + "context" + "fmt" + "testing" + + dockerclient "github.com/docker/docker/client" + "github.com/pelletier/go-toml" + "github.com/strangelove-ventures/interchaintest/v8" + "github.com/strangelove-ventures/interchaintest/v8/ibc" + "github.com/strangelove-ventures/interchaintest/v8/relayer" + "github.com/strangelove-ventures/interchaintest/v8/relayer/hermes" + "go.uber.org/zap" +) + +const ( + Rly = "rly" + Hermes = "hermes" + Hyperspace = "hyperspace" + + HermesRelayerRepository = "ghcr.io/informalsystems/hermes" + hermesRelayerUser = "2000:2000" + RlyRelayerRepository = "ghcr.io/cosmos/relayer" + rlyRelayerUser = "100:1000" + + // TODO: https://github.com/cosmos/ibc-go/issues/4965 + HyperspaceRelayerRepository = "ghcr.io/misko9/hyperspace" + hyperspaceRelayerUser = "1000:1000" + + // relativeHermesConfigFilePath is the path to the hermes config file relative to the home directory within the container. + relativeHermesConfigFilePath = ".hermes/config.toml" +) + +// 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 `yaml:"tag"` + // ID specifies the type of relayer that this is. + ID string `yaml:"id"` + // Image is the image that should be used for the relayer. + Image string `yaml:"image"` +} + +// 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 { + t.Helper() + switch cfg.ID { + case Rly: + return newCosmosRelayer(t, cfg.Tag, logger, dockerClient, network, cfg.Image) + case Hermes: + return newHermesRelayer(t, cfg.Tag, logger, dockerClient, network, cfg.Image) + case Hyperspace: + return newHyperspaceRelayer(t, cfg.Tag, logger, dockerClient, network, cfg.Image) + default: + panic(fmt.Errorf("unknown relayer specified: %s", cfg.ID)) + } +} + +// ApplyPacketFilter applies a packet filter to the hermes config file, which specifies a complete set of channels +// to watch for packets. +func ApplyPacketFilter(ctx context.Context, t *testing.T, r ibc.Relayer, chainID string, channels []ibc.ChannelOutput) error { + t.Helper() + + h, ok := r.(*hermes.Relayer) + if !ok { + t.Logf("relayer %T does not support packet filtering, or it has not been implemented yet.", r) + return nil + } + + return modifyHermesConfigFile(ctx, h, func(config map[string]interface{}) error { + chains, ok := config["chains"].([]map[string]interface{}) + if !ok { + return fmt.Errorf("failed to get chains from hermes config") + } + var chain map[string]interface{} + for _, c := range chains { + if c["id"] == chainID { + chain = c + break + } + } + + if chain == nil { + return fmt.Errorf("failed to find chain with id %s", chainID) + } + + var channelEndpoints [][]string + for _, c := range channels { + channelEndpoints = append(channelEndpoints, []string{c.PortID, c.ChannelID}) + } + + // [chains.packet_filter] + // # policy = 'allow' + // # list = [ + // # ['ica*', '*'], + // # ['transfer', 'channel-0'], + // # ] + + // TODO(chatton): explicitly enable watching of ICA channels + // this will ensure the ICA tests pass, but this will need to be modified to make sure + // ICA tests will succeed in parallel. + channelEndpoints = append(channelEndpoints, []string{"ica*", "*"}) + + // we explicitly override the full list, this allows this function to provide a complete set of channels to watch. + chain["packet_filter"] = map[string]interface{}{ + "policy": "allow", + "list": channelEndpoints, + } + + return nil + }) +} + +// modifyHermesConfigFile reads the hermes config file, applies a modification function and returns an error if any. +func modifyHermesConfigFile(ctx context.Context, h *hermes.Relayer, modificationFn func(map[string]interface{}) error) error { + bz, err := h.ReadFileFromHomeDir(ctx, relativeHermesConfigFilePath) + if err != nil { + return fmt.Errorf("failed to read hermes config file: %w", err) + } + + var config map[string]interface{} + if err := toml.Unmarshal(bz, &config); err != nil { + return fmt.Errorf("failed to unmarshal hermes config bytes") + } + + if modificationFn != nil { + if err := modificationFn(config); err != nil { + return fmt.Errorf("failed to modify hermes config: %w", err) + } + } + + bz, err = toml.Marshal(config) + if err != nil { + return fmt.Errorf("failed to marshal hermes config bytes") + } + + return h.WriteFileToHomeDir(ctx, relativeHermesConfigFilePath, bz) +} + +// 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, relayerImage string) ibc.Relayer { + t.Helper() + + customImageOption := relayer.CustomDockerImage(relayerImage, tag, rlyRelayerUser) + 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(t *testing.T, tag string, logger *zap.Logger, dockerClient *dockerclient.Client, network, relayerImage string) ibc.Relayer { + t.Helper() + + customImageOption := relayer.CustomDockerImage(relayerImage, tag, hermesRelayerUser) + relayerFactory := interchaintest.NewBuiltinRelayerFactory(ibc.Hermes, logger, customImageOption) + + return relayerFactory.Build( + t, dockerClient, network, + ) +} + +// newHyperspaceRelayer returns an instance of the hyperspace relayer. +func newHyperspaceRelayer(t *testing.T, tag string, logger *zap.Logger, dockerClient *dockerclient.Client, network, relayerImage string) ibc.Relayer { + t.Helper() + + customImageOption := relayer.CustomDockerImage(relayerImage, tag, hyperspaceRelayerUser) + relayerFactory := interchaintest.NewBuiltinRelayerFactory(ibc.Hyperspace, logger, customImageOption) + + return relayerFactory.Build( + t, dockerClient, network, + ) +} + +// Map is a mapping from test names to a relayer set for that test. +type Map map[string]map[ibc.Wallet]bool + +// AddRelayer adds the given relayer to the relayer set for the given test name. +func (r Map) AddRelayer(testName string, ibcrelayer ibc.Wallet) { + if _, ok := r[testName]; !ok { + r[testName] = make(map[ibc.Wallet]bool) + } + r[testName][ibcrelayer] = true +} + +// containsRelayer returns true if the given relayer is in the relayer set for the given test name. +func (r Map) ContainsRelayer(testName string, wallet ibc.Wallet) bool { + if relayerSet, ok := r[testName]; ok { + return relayerSet[wallet] + } + return false +} diff --git a/e2e/sample.config.yaml b/e2e/sample.config.yaml new file mode 100644 index 00000000000..c8af5fa1db1 --- /dev/null +++ b/e2e/sample.config.yaml @@ -0,0 +1,49 @@ +# This file contains configuration for running e2e tests. +# Many of these fields can be overridden with environment variables. +# All fields that support this have the corresponding environment variable name in a comment beside the field. + +--- +chains: + # the entry at index 0 corresponds to CHAIN_A +- chainId: chainA-1 + numValidators: 1 + numFullNodes: 0 + image: ghcr.io/cosmos/ibc-go-simd # override with CHAIN_IMAGE + tag: main # override with CHAIN_A_TAG + binary: simd # override with CHAIN_BINARY + + # the entry at index 1 corresponds to CHAIN_B +- chainId: chainB-1 + numValidators: 1 + numFullNodes: 0 + image: ghcr.io/cosmos/ibc-go-simd # override with CHAIN_IMAGE + tag: main # override with CHAIN_B_TAG + binary: simd # override with CHAIN_BINARY + +activeRelayer: hermes # override with RELAYER_ID +relayers: + - id: hermes + image: ghcr.io/informalsystems/hermes + tag: "1.10.0" + - id: rly + image: ghcr.io/cosmos/relayer + tag: "latest" + - id: hyperspace + image: ghcr.io/misko9/hyperspace + tag: "20231122v39" + +cometbft: + logLevel: info + +debug: + # setting this value to true will force log collection even if the test passes. + dumpLogs: false + # settings this value to true will keep the containers running after the test finishes. + keepContainers: true + +# Required only for upgrade tests. +# Chain A will be upgraded the specified tag. +# The plan name must be registered as an upgrade handler in the given tag. +upgrade: + planName: "" + tag: "" diff --git a/e2e/scripts/init.sh b/e2e/scripts/init.sh new file mode 100755 index 00000000000..be4efd994fa --- /dev/null +++ b/e2e/scripts/init.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +set -euo pipefail + +# TODO: when we are using config files for CI we can remove this. +# ref: https://github.com/cosmos/ibc-go/issues/4697 +# if running in CI we just use env vars. +if [[ "${CI:-}" = "true" ]]; then + exit 0 +fi + +# ensure_config_file makes sure there is a config file for the e2e tests either by creating a new one using the sample, +# it is copied to either the default location or the specified env location. +function ensure_config_file(){ + local config_file_path="${HOME}/.ibc-go-e2e-config.yaml" + if [[ ! -z "${E2E_CONFIG_PATH:-}" ]]; then + config_file_path="${E2E_CONFIG_PATH}" + fi + if [[ ! -f "${config_file_path}" ]]; then + echo "creating e2e config file from sample." + echo "copying sample.config.yaml to ${config_file_path}" + cp sample.config.yaml "${config_file_path}" + fi + echo "using config file at ${config_file_path} for e2e test" +} + +ensure_config_file 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 new file mode 100755 index 00000000000..58c5b3db683 --- /dev/null +++ b/e2e/scripts/run-e2e.sh @@ -0,0 +1,116 @@ +#!/bin/bash + +set -eo pipefail + +TEST="${1:-}" +export ENTRY_POINT="${2:-}" + +function _verify_jq() { + if ! command -v jq > /dev/null ; then + echo "jq is required to extract test entrypoint." + exit 1 + fi +} + +function _verify_fzf() { + if ! command -v fzf > /dev/null ; then + echo "fzf is required to interactively select a test." + exit 1 + fi +} + +function _verify_test_dependencies() { + if [ -z "${TEST}" ]; then + # fzf is only required if we are not explicitly specifying a test. + _verify_fzf + fi + # jq is always required to determine the entrypoint of the test. + _verify_jq +} + +function _verify_suite_dependencies() { + if [ -z "${ENTRY_POINT}" ]; then + # fzf is only required if we are not explicitly specifying an entrypoint. + _verify_fzf + fi + # jq is always required to determine the entrypoint of the test. + _verify_jq +} + +# _select_test_config lets you dynamically select a test config for the specific test. +function _select_test_config() { + ls -1 dev-configs | fzf +} + +# _get_test returns the test that should be used in the e2e test. If an argument is provided, that argument +# is returned. Otherwise, fzf is used to interactively choose from all available tests. +function _get_test(){ + # if an argument is provided, it is used directly. This enables the drop down selection with fzf. + if [ -n "${1:-}" ]; then + echo "$1" + return + # if fzf and jq are installed, we can use them to provide an interactive mechanism to select from all available tests. + else + cd .. + go run -mod=readonly cmd/build_test_matrix/main.go | jq -r '.include[] | .test' | fzf + cd - > /dev/null + fi +} + +# run_test runs a single E2E test. +function run_test() { + _verify_test_dependencies + + # if test is set, that is used directly, otherwise the test can be interactively provided if fzf is installed. + TEST="$(_get_test ${TEST})" + + # if jq is installed, we can automatically determine the test entrypoint. + if command -v jq > /dev/null; then + cd .. + ENTRY_POINT="$(go run -mod=readonly cmd/build_test_matrix/main.go | jq -r --arg TEST "${TEST}" '.include[] | select( .test == $TEST) | .entrypoint')" + cd - > /dev/null + fi + + + # find the name of the file that has this test in it. + test_file="$(grep --recursive --files-with-matches './' -e "${TEST}()")" + + # we run the test on the directory as specific files may reference types in other files but within the package. + test_dir="$(dirname $test_file)" + + # run the test file directly, this allows log output to be streamed directly in the terminal sessions + # without needed to wait for the test to finish. + # it shouldn't take 30m, but the wasm test can be quite slow, so we can be generous. + go test -v "${test_dir}" --run ${ENTRY_POINT} -testify.m ^${TEST}$ -timeout 30m +} + +# run_suite runs a full E2E test suite. +function run_suite() { + _verify_suite_dependencies + # if jq is installed, we can automatically determine the test entrypoint. + if [ -z "${ENTRY_POINT}" ]; then + cd .. + ENTRY_POINT="$(go run -mod=readonly cmd/build_test_matrix/main.go | jq -r '.include[] | .entrypoint' | uniq | fzf)" + cd - > /dev/null + fi + + # find the name of the file that has this test in it. + test_file="$(grep --recursive --files-with-matches './tests' -e "${ENTRY_POINT}(")" + test_dir="$(dirname $test_file)" + + go test -v "${test_dir}" --run ^${ENTRY_POINT}$ -timeout 30m -p 10 +} + + +# if the dev configs directory is present, enable fzf completion to select a test config file to use. +if [[ -d "dev-configs" ]]; then + export E2E_CONFIG_PATH="$(pwd)/dev-configs/$(_select_test_config)" + echo "Using configuration file at ${E2E_CONFIG_PATH}" +fi + +if [ "${RUN_SUITE:-}" = "true" ]; then + run_suite +else + run_test +fi + diff --git a/e2e/semverutil/semver.go b/e2e/semverutil/semver.go new file mode 100644 index 00000000000..8c93920b9cc --- /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):] + // replace x with 999 so the release version is always larger than the others in the release line. + versionStr = strings.ReplaceAll(versionStr, "x", "999") + } + + // 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 fr.MajorVersion != "" && GTE(versionStr, fr.MajorVersion) { + return true + } + + for _, mv := range fr.MinorVersions { + mvMajor, versionStrMajor := semver.Major(mv), semver.Major(versionStr) + + if semverEqual(mvMajor, versionStrMajor) { + return GTE(versionStr, mv) + } + } + + return false +} + +// GTE returns true if versionA is greater than or equal to versionB. +func GTE(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..b90c2832da8 --- /dev/null +++ b/e2e/semverutil/semver_test.go @@ -0,0 +1,54 @@ +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 pre-release", "v6.0.0-rc.0", false}, + {"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/tests/core/02-client/client_test.go b/e2e/tests/core/02-client/client_test.go new file mode 100644 index 00000000000..36931d8df92 --- /dev/null +++ b/e2e/tests/core/02-client/client_test.go @@ -0,0 +1,489 @@ +//go:build !test_e2e + +package client + +import ( + "context" + "fmt" + "slices" + "sort" + "strings" + "testing" + "time" + + "github.com/strangelove-ventures/interchaintest/v8/ibc" + test "github.com/strangelove-ventures/interchaintest/v8/testutil" + testifysuite "github.com/stretchr/testify/suite" + + upgradetypes "cosmossdk.io/x/upgrade/types" + + "github.com/cosmos/cosmos-sdk/client/grpc/cmtservice" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + paramsproposaltypes "github.com/cosmos/cosmos-sdk/x/params/types/proposal" + + "github.com/cometbft/cometbft/crypto/tmhash" + cmtjson "github.com/cometbft/cometbft/libs/json" + "github.com/cometbft/cometbft/privval" + cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" + cmtprotoversion "github.com/cometbft/cometbft/proto/tendermint/version" + cmttypes "github.com/cometbft/cometbft/types" + cmtversion "github.com/cometbft/cometbft/version" + + "github.com/cosmos/ibc-go/e2e/dockerutil" + "github.com/cosmos/ibc-go/e2e/testsuite" + "github.com/cosmos/ibc-go/e2e/testsuite/query" + "github.com/cosmos/ibc-go/e2e/testvalues" + wasmtypes "github.com/cosmos/ibc-go/modules/light-clients/08-wasm/types" + clienttypes "github.com/cosmos/ibc-go/v9/modules/core/02-client/types" + ibcexported "github.com/cosmos/ibc-go/v9/modules/core/exported" + ibctm "github.com/cosmos/ibc-go/v9/modules/light-clients/07-tendermint" + ibctesting "github.com/cosmos/ibc-go/v9/testing" +) + +const ( + invalidHashValue = "invalid_hash" +) + +func TestClientTestSuite(t *testing.T) { + testifysuite.Run(t, new(ClientTestSuite)) +} + +type ClientTestSuite struct { + testsuite.E2ETestSuite +} + +// QueryAllowedClients queries the on-chain AllowedClients parameter for 02-client +func (s *ClientTestSuite) QueryAllowedClients(ctx context.Context, chain ibc.Chain) []string { + res, err := query.GRPCQuery[clienttypes.QueryClientParamsResponse](ctx, chain, &clienttypes.QueryClientParamsRequest{}) + s.Require().NoError(err) + + return res.Params.AllowedClients +} + +// TestScheduleIBCUpgrade_Succeeds tests that a governance proposal to schedule an IBC software upgrade is successful. +func (s *ClientTestSuite) TestScheduleIBCUpgrade_Succeeds() { + t := s.T() + ctx := context.TODO() + + testName := t.Name() + s.CreateDefaultPaths(testName) + + chainA, chainB := s.GetChains() + chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + + const planHeight = int64(300) + var newChainID string + + t.Run("execute proposal for MsgIBCSoftwareUpgrade", func(t *testing.T) { + authority, err := query.ModuleAccountAddress(ctx, govtypes.ModuleName, chainA) + s.Require().NoError(err) + s.Require().NotNil(authority) + + clientState, err := query.ClientState(ctx, chainB, ibctesting.FirstClientID) + s.Require().NoError(err) + + originalChainID := clientState.(*ibctm.ClientState).ChainId + revisionNumber := clienttypes.ParseChainID(originalChainID) + // increment revision number even with new chain ID to prevent loss of misbehaviour detection support + newChainID, err = clienttypes.SetRevisionNumber(originalChainID, revisionNumber+1) + s.Require().NoError(err) + s.Require().NotEqual(originalChainID, newChainID) + + upgradedClientState, ok := clientState.(*ibctm.ClientState) + s.Require().True(ok) + upgradedClientState.ChainId = newChainID + + scheduleUpgradeMsg, err := clienttypes.NewMsgIBCSoftwareUpgrade( + authority.String(), + upgradetypes.Plan{ + Name: "upgrade-client", + Height: planHeight, + }, + upgradedClientState, + ) + s.Require().NoError(err) + s.ExecuteAndPassGovV1Proposal(ctx, scheduleUpgradeMsg, chainA, chainAWallet) + }) + + t.Run("check that IBC software upgrade has been scheduled successfully on chainA", func(t *testing.T) { + // checks there is an upgraded client state stored + upgradedCsResp, err := query.GRPCQuery[clienttypes.QueryUpgradedClientStateResponse](ctx, chainA, &clienttypes.QueryUpgradedClientStateRequest{}) + s.Require().NoError(err) + + clientStateAny := upgradedCsResp.UpgradedClientState + + cfg := chainA.Config().EncodingConfig + var cs ibcexported.ClientState + err = cfg.InterfaceRegistry.UnpackAny(clientStateAny, &cs) + s.Require().NoError(err) + + upgradedClientState, ok := cs.(*ibctm.ClientState) + s.Require().True(ok) + s.Require().Equal(upgradedClientState.ChainId, newChainID) + + planResponse, err := query.GRPCQuery[upgradetypes.QueryCurrentPlanResponse](ctx, chainA, &upgradetypes.QueryCurrentPlanRequest{}) + s.Require().NoError(err) + + plan := planResponse.Plan + + s.Require().Equal("upgrade-client", plan.Name) + s.Require().Equal(planHeight, plan.Height) + }) +} + +// TestRecoverClient_Succeeds tests that a governance proposal to recover a client using a MsgRecoverClient is successful. +func (s *ClientTestSuite) TestRecoverClient_Succeeds() { + t := s.T() + ctx := context.TODO() + + var ( + pathName string + subjectClientID string + substituteClientID string + // set the trusting period to a value which will still be valid upon client creation, but invalid before the first update + badTrustingPeriod = time.Second * 10 + ) + + testName := t.Name() + relayer := s.CreateDefaultPaths(testName) + + t.Run("create substitute client with correct trusting period", func(t *testing.T) { + // TODO: update when client identifier created is accessible + // currently assumes first client is 07-tendermint-0 + substituteClientID = clienttypes.FormatClientIdentifier(ibcexported.Tendermint, 0) + + pathName = s.GetPaths(testName)[0] + }) + + 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 := query.ClientStatus(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 := query.ClientStatus(ctx, chainA, subjectClientID) + s.Require().NoError(err) + s.Require().Equal(ibcexported.Expired.String(), status) + }) + }) + + t.Run("execute proposal for MsgRecoverClient", func(t *testing.T) { + authority, err := query.ModuleAccountAddress(ctx, govtypes.ModuleName, chainA) + s.Require().NoError(err) + recoverClientMsg := clienttypes.NewMsgRecoverClient(authority.String(), subjectClientID, substituteClientID) + s.Require().NotNil(recoverClientMsg) + s.ExecuteAndPassGovV1Proposal(ctx, recoverClientMsg, chainA, chainAWallet) + }) + + t.Run("check status of each client", func(t *testing.T) { + t.Run("substitute should be active", func(t *testing.T) { + status, err := query.ClientStatus(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 := query.ClientStatus(ctx, chainA, subjectClientID) + s.Require().NoError(err) + s.Require().Equal(ibcexported.Active.String(), status) + }) + }) +} + +func (s *ClientTestSuite) TestClient_Update_Misbehaviour() { + t := s.T() + ctx := context.TODO() + + var ( + trustedHeight clienttypes.Height + latestHeight clienttypes.Height + clientState ibcexported.ClientState + header *cmtservice.Header + signers []cmttypes.PrivValidator + validatorSet []*cmttypes.Validator + maliciousHeader *ibctm.Header + err error + ) + + testName := t.Name() + relayer := s.CreateDefaultPaths(testName) + + chainA, chainB := s.GetChains() + + s.Require().NoError(test.WaitForBlocks(ctx, 10, chainA, chainB)) + + t.Run("update clients", func(t *testing.T) { + err := relayer.UpdateClients(ctx, s.GetRelayerExecReporter(), s.GetPaths(testName)[0]) + s.Require().NoError(err) + + clientState, err = query.ClientState(ctx, chainA, ibctesting.FirstClientID) + s.Require().NoError(err) + }) + + t.Run("fetch trusted height", func(t *testing.T) { + tmClientState, ok := clientState.(*ibctm.ClientState) + s.Require().True(ok) + + trustedHeight = tmClientState.LatestHeight + s.Require().True(trustedHeight.GT(clienttypes.ZeroHeight())) + }) + + t.Run("update clients", func(t *testing.T) { + err := relayer.UpdateClients(ctx, s.GetRelayerExecReporter(), s.GetPaths(testName)[0]) + s.Require().NoError(err) + + clientState, err = query.ClientState(ctx, chainA, ibctesting.FirstClientID) + s.Require().NoError(err) + }) + + t.Run("fetch client state latest height", func(t *testing.T) { + tmClientState, ok := clientState.(*ibctm.ClientState) + s.Require().True(ok) + + latestHeight = tmClientState.LatestHeight + s.Require().True(latestHeight.GT(clienttypes.ZeroHeight())) + }) + + t.Run("create validator set", func(t *testing.T) { + var validators []*cmtservice.Validator + + t.Run("fetch block header at latest client state height", func(t *testing.T) { + headerResp, err := query.GRPCQuery[cmtservice.GetBlockByHeightResponse](ctx, chainB, &cmtservice.GetBlockByHeightRequest{ + Height: int64(latestHeight.GetRevisionHeight()), + }) + s.Require().NoError(err) + + header = &headerResp.SdkBlock.Header + }) + + t.Run("get validators at latest height", func(t *testing.T) { + validators, err = query.GetValidatorSetByHeight(ctx, chainB, latestHeight.GetRevisionHeight()) + s.Require().NoError(err) + }) + + t.Run("extract validator private keys", func(t *testing.T) { + privateKeys := s.extractChainPrivateKeys(ctx, chainB) + s.Require().NotEmpty(privateKeys, "private keys are empty") + + for i, pv := range privateKeys { + pubKey, err := pv.GetPubKey() + s.Require().NoError(err) + + validator := cmttypes.NewValidator(pubKey, validators[i].VotingPower) + err = validator.ValidateBasic() + s.Require().NoError(err, "invalid validator: %s", err) + + validatorSet = append(validatorSet, validator) + signers = append(signers, pv) + } + }) + }) + + s.Require().NotEmpty(validatorSet, "validator set is empty") + + t.Run("create malicious header", func(t *testing.T) { + valSet := cmttypes.NewValidatorSet(validatorSet) + err := valSet.ValidateBasic() + s.Require().NoError(err, "invalid validator set: %s", err) + maliciousHeader, err = createMaliciousTMHeader(chainB.Config().ChainID, int64(latestHeight.GetRevisionHeight()), trustedHeight, + header.GetTime(), valSet, valSet, signers, header) + s.Require().NoError(err) + }) + + t.Run("update client with duplicate misbehaviour header", func(t *testing.T) { + rlyWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + msgUpdateClient, err := clienttypes.NewMsgUpdateClient(ibctesting.FirstClientID, maliciousHeader, rlyWallet.FormattedAddress()) + s.Require().NoError(err) + + txResp := s.BroadcastMessages(ctx, chainA, rlyWallet, msgUpdateClient) + s.AssertTxSuccess(txResp) + }) + + t.Run("ensure client status is frozen", func(t *testing.T) { + status, err := query.ClientStatus(ctx, chainA, ibctesting.FirstClientID) + s.Require().NoError(err) + s.Require().Equal(ibcexported.Frozen.String(), status) + }) +} + +// TestAllowedClientsParam tests changing the AllowedClients parameter using a governance proposal +func (s *ClientTestSuite) TestAllowedClientsParam() { + t := s.T() + ctx := context.TODO() + + testName := t.Name() + s.CreateDefaultPaths(testName) + + chainA, chainB := s.GetChains() + chainAVersion := chainA.Config().Images[0].Version + chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + + s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB), "failed to wait for blocks") + + t.Run("ensure allowed clients are set to the default", func(t *testing.T) { + allowedClients := s.QueryAllowedClients(ctx, chainA) + + defaultAllowedClients := clienttypes.DefaultAllowedClients + if !testvalues.AllowAllClientsWildcardFeatureReleases.IsSupported(chainAVersion) { + defaultAllowedClients = []string{ibcexported.Solomachine, ibcexported.Tendermint, ibcexported.Localhost, wasmtypes.Wasm} + } + if !testvalues.LocalhostClientFeatureReleases.IsSupported(chainAVersion) { + defaultAllowedClients = slices.DeleteFunc(defaultAllowedClients, func(s string) bool { return s == ibcexported.Localhost }) + } + s.Require().Equal(defaultAllowedClients, allowedClients) + }) + + allowedClient := ibcexported.Solomachine + t.Run("change the allowed client to only allow solomachine clients", func(t *testing.T) { + if testvalues.SelfParamsFeatureReleases.IsSupported(chainAVersion) { + authority, err := query.ModuleAccountAddress(ctx, govtypes.ModuleName, chainA) + s.Require().NoError(err) + s.Require().NotNil(authority) + + msg := clienttypes.NewMsgUpdateParams(authority.String(), clienttypes.NewParams(allowedClient)) + s.ExecuteAndPassGovV1Proposal(ctx, msg, chainA, chainAWallet) + } else { + value, err := cmtjson.Marshal([]string{allowedClient}) + s.Require().NoError(err) + changes := []paramsproposaltypes.ParamChange{ + paramsproposaltypes.NewParamChange(ibcexported.ModuleName, string(clienttypes.KeyAllowedClients), string(value)), + } + + proposal := paramsproposaltypes.NewParameterChangeProposal(ibctesting.Title, ibctesting.Description, changes) + s.ExecuteAndPassGovV1Beta1Proposal(ctx, chainA, chainAWallet, proposal) + } + }) + + t.Run("validate the param was successfully changed", func(t *testing.T) { + allowedClients := s.QueryAllowedClients(ctx, chainA) + s.Require().Equal([]string{allowedClient}, allowedClients) + }) + + t.Run("ensure querying non-allowed client's status returns Unauthorized Status", func(t *testing.T) { + status, err := query.ClientStatus(ctx, chainA, ibctesting.FirstClientID) + s.Require().NoError(err) + s.Require().Equal(ibcexported.Unauthorized.String(), status) + + status, err = query.ClientStatus(ctx, chainA, ibcexported.Localhost) + s.Require().NoError(err) + s.Require().Equal(ibcexported.Unauthorized.String(), status) + }) +} + +// extractChainPrivateKeys returns a slice of cmttypes.PrivValidator which hold the private keys for all validator +// nodes for a given chain. +func (s *ClientTestSuite) extractChainPrivateKeys(ctx context.Context, chain ibc.Chain) []cmttypes.PrivValidator { + testContainers, err := dockerutil.GetTestContainers(ctx, s.SuiteName(), s.DockerClient) + s.Require().NoError(err) + s.Require().NotEmpty(testContainers, "no test containers found") + + var filePvs []privval.FilePVKey + var pvs []cmttypes.PrivValidator + for _, container := range testContainers { + isNodeForDifferentChain := !strings.Contains(container.Names[0], chain.Config().ChainID) + isFullNode := strings.Contains(container.Names[0], fmt.Sprintf("%s-fn", chain.Config().ChainID)) + if isNodeForDifferentChain || isFullNode { + s.T().Logf("skipping container %s for chain %s", container.Names[0], chain.Config().ChainID) + continue + } + + validatorPrivKey := fmt.Sprintf("/var/cosmos-chain/%s/config/priv_validator_key.json", chain.Config().Name) + privKeyFileContents, err := dockerutil.GetFileContentsFromContainer(ctx, s.DockerClient, container.ID, validatorPrivKey) + s.Require().NoError(err) + + var filePV privval.FilePVKey + err = cmtjson.Unmarshal(privKeyFileContents, &filePV) + s.Require().NoError(err) + filePvs = append(filePvs, filePV) + } + + // We sort by address as GetValidatorSetByHeight also sorts by address. When iterating over them, the index + // will correspond to the correct ibcmock.PV. + sort.SliceStable(filePvs, func(i, j int) bool { + return filePvs[i].Address.String() < filePvs[j].Address.String() + }) + + for _, filePV := range filePvs { + pvs = append(pvs, cmttypes.NewMockPVWithParams( + filePV.PrivKey, false, false, + )) + } + + return pvs +} + +// createMaliciousTMHeader creates a header with the provided trusted height with an invalid app hash. +func createMaliciousTMHeader(chainID string, blockHeight int64, trustedHeight clienttypes.Height, timestamp time.Time, tmValSet, tmTrustedVals *cmttypes.ValidatorSet, signers []cmttypes.PrivValidator, oldHeader *cmtservice.Header) (*ibctm.Header, error) { + tmHeader := cmttypes.Header{ + Version: cmtprotoversion.Consensus{Block: cmtversion.BlockProtocol, App: 2}, + ChainID: chainID, + Height: blockHeight, + Time: timestamp, + LastBlockID: ibctesting.MakeBlockID(make([]byte, tmhash.Size), 10_000, make([]byte, tmhash.Size)), + LastCommitHash: oldHeader.GetLastCommitHash(), + ValidatorsHash: tmValSet.Hash(), + NextValidatorsHash: tmValSet.Hash(), + DataHash: tmhash.Sum([]byte(invalidHashValue)), + ConsensusHash: tmhash.Sum([]byte(invalidHashValue)), + AppHash: tmhash.Sum([]byte(invalidHashValue)), + LastResultsHash: tmhash.Sum([]byte(invalidHashValue)), + EvidenceHash: tmhash.Sum([]byte(invalidHashValue)), + ProposerAddress: tmValSet.Proposer.Address, //nolint:staticcheck + } + + hhash := tmHeader.Hash() + blockID := ibctesting.MakeBlockID(hhash, 3, tmhash.Sum([]byte(invalidHashValue))) + voteSet := cmttypes.NewVoteSet(chainID, blockHeight, 1, cmtproto.PrecommitType, tmValSet) + + extCommit, err := cmttypes.MakeExtCommit(blockID, blockHeight, 1, voteSet, signers, timestamp, false) + if err != nil { + return nil, err + } + + signedHeader := &cmtproto.SignedHeader{ + Header: tmHeader.ToProto(), + Commit: extCommit.ToCommit().ToProto(), + } + + valSet, err := tmValSet.ToProto() + if err != nil { + return nil, err + } + + trustedVals, err := tmTrustedVals.ToProto() + if err != nil { + return nil, err + } + + return &ibctm.Header{ + SignedHeader: signedHeader, + ValidatorSet: valSet, + TrustedHeight: trustedHeight, + TrustedValidators: trustedVals, + }, 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..0c03555b6e8 --- /dev/null +++ b/e2e/tests/core/03-connection/connection_test.go @@ -0,0 +1,147 @@ +//go:build !test_e2e + +package connection + +import ( + "context" + "fmt" + "strconv" + "strings" + "testing" + "time" + + "github.com/strangelove-ventures/interchaintest/v8/ibc" + test "github.com/strangelove-ventures/interchaintest/v8/testutil" + testifysuite "github.com/stretchr/testify/suite" + + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + paramsproposaltypes "github.com/cosmos/cosmos-sdk/x/params/types/proposal" + + "github.com/cosmos/ibc-go/e2e/testsuite" + "github.com/cosmos/ibc-go/e2e/testsuite/query" + "github.com/cosmos/ibc-go/e2e/testvalues" + connectiontypes "github.com/cosmos/ibc-go/v9/modules/core/03-connection/types" + ibcexported "github.com/cosmos/ibc-go/v9/modules/core/exported" + ibctesting "github.com/cosmos/ibc-go/v9/testing" +) + +func TestConnectionTestSuite(t *testing.T) { + testifysuite.Run(t, new(ConnectionTestSuite)) +} + +type ConnectionTestSuite struct { + testsuite.E2ETestSuite +} + +func (s *ConnectionTestSuite) CreateConnectionTestPath(testName string) (ibc.Relayer, ibc.ChannelOutput) { + return s.CreatePaths(ibc.DefaultClientOpts(), s.TransferChannelOptions(), testName), s.GetChainAChannelForTest(testName) +} + +// 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 { + if testvalues.SelfParamsFeatureReleases.IsSupported(chain.Config().Images[0].Version) { + res, err := query.GRPCQuery[connectiontypes.QueryConnectionParamsResponse](ctx, chain, &connectiontypes.QueryConnectionParamsRequest{}) + s.Require().NoError(err) + + return res.Params.MaxExpectedTimePerBlock + } + res, err := query.GRPCQuery[paramsproposaltypes.QueryParamsResponse](ctx, chain, ¶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, "\"", "") + // convert to uint64 + uinttime, err := strconv.ParseUint(delay, 10, 64) + s.Require().NoError(err) + + return uinttime +} + +// TestMaxExpectedTimePerBlockParam tests changing the MaxExpectedTimePerBlock param using a governance proposal +func (s *ConnectionTestSuite) TestMaxExpectedTimePerBlockParam() { + t := s.T() + ctx := context.TODO() + testName := t.Name() + relayer, channelA := s.CreateConnectionTestPath(testName) + + chainA, chainB := s.GetChains() + chainAVersion := chainA.Config().Images[0].Version + + 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) { + delay := s.QueryMaxExpectedTimePerBlockParam(ctx, chainA) + s.Require().Equal(uint64(connectiontypes.DefaultTimePerBlock), delay) + }) + + t.Run("change the delay to 60 seconds", func(t *testing.T) { + delay := uint64(1 * time.Minute) + if testvalues.SelfParamsFeatureReleases.IsSupported(chainAVersion) { + authority, err := query.ModuleAccountAddress(ctx, govtypes.ModuleName, chainA) + s.Require().NoError(err) + s.Require().NotNil(authority) + + msg := connectiontypes.NewMsgUpdateParams(authority.String(), connectiontypes.NewParams(delay)) + s.ExecuteAndPassGovV1Proposal(ctx, msg, chainA, chainAWallet) + } else { + changes := []paramsproposaltypes.ParamChange{ + paramsproposaltypes.NewParamChange(ibcexported.ModuleName, string(connectiontypes.KeyMaxExpectedTimePerBlock), fmt.Sprintf(`"%d"`, delay)), + } + + proposal := paramsproposaltypes.NewParameterChangeProposal(ibctesting.Title, ibctesting.Description, changes) + s.ExecuteAndPassGovV1Beta1Proposal(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 := s.Transfer(ctx, chainB, chainBWallet, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, testvalues.DefaultTransferCoins(chainBDenom), chainBAddress, chainAAddress, s.GetTimeoutHeight(ctx, chainA), 0, "", nil) + s.AssertTxSuccess(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, testName) + }) + + t.Run("packets are relayed", func(t *testing.T) { + s.AssertPacketRelayed(ctx, chainA, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, 1) + + actualBalance, err := query.Balance(ctx, chainA, chainAAddress, chainAIBCToken.IBCDenom()) + + s.Require().NoError(err) + + expected := testvalues.IBCTransferAmount + s.Require().Equal(expected, actualBalance.Int64()) + }) + + t.Run("stop relayer", func(t *testing.T) { + s.StopRelayer(ctx, relayer) + }) + }) +} diff --git a/e2e/tests/data/ics10_grandpa_cw.wasm.gz b/e2e/tests/data/ics10_grandpa_cw.wasm.gz new file mode 100755 index 0000000000000000000000000000000000000000..cccae7aeeaf572a734df14dae33704e3c506c403 GIT binary patch literal 275506 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 z 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
)S;w+==XZ!JU{ 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

JUV^a3&n6VuGmcsyGCwjDh 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%0DD@)}?sr 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 literal 0 HcmV?d00001 diff --git a/e2e/tests/interchain_accounts/base_test.go b/e2e/tests/interchain_accounts/base_test.go new file mode 100644 index 00000000000..49e0e788e11 --- /dev/null +++ b/e2e/tests/interchain_accounts/base_test.go @@ -0,0 +1,539 @@ +//go:build !test_e2e + +package interchainaccounts + +import ( + "context" + "testing" + "time" + + "github.com/cosmos/gogoproto/proto" + "github.com/strangelove-ventures/interchaintest/v8" + "github.com/strangelove-ventures/interchaintest/v8/ibc" + test "github.com/strangelove-ventures/interchaintest/v8/testutil" + testifysuite "github.com/stretchr/testify/suite" + + sdkmath "cosmossdk.io/math" + + 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" + govv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" + + "github.com/cosmos/ibc-go/e2e/testsuite" + "github.com/cosmos/ibc-go/e2e/testsuite/query" + "github.com/cosmos/ibc-go/e2e/testvalues" + controllertypes "github.com/cosmos/ibc-go/v9/modules/apps/27-interchain-accounts/controller/types" + icatypes "github.com/cosmos/ibc-go/v9/modules/apps/27-interchain-accounts/types" + channeltypes "github.com/cosmos/ibc-go/v9/modules/core/04-channel/types" + ibctesting "github.com/cosmos/ibc-go/v9/testing" +) + +// orderMapping is a mapping from channel ordering to the string representation of the ordering. +// the representation can be different depending on the relayer implementation. +var orderMapping = map[channeltypes.Order][]string{ + channeltypes.ORDERED: {channeltypes.ORDERED.String(), "Ordered"}, + channeltypes.UNORDERED: {channeltypes.UNORDERED.String(), "Unordered"}, +} + +func TestInterchainAccountsTestSuite(t *testing.T) { + testifysuite.Run(t, new(InterchainAccountsTestSuite)) +} + +type InterchainAccountsTestSuite struct { + testsuite.E2ETestSuite +} + +// RegisterInterchainAccount will attempt to register an interchain account on the counterparty chain. +func (s *InterchainAccountsTestSuite) RegisterInterchainAccount(ctx context.Context, chain ibc.Chain, user ibc.Wallet, msgRegisterAccount *controllertypes.MsgRegisterInterchainAccount) { + txResp := s.BroadcastMessages(ctx, chain, user, msgRegisterAccount) + s.AssertTxSuccess(txResp) +} + +func (s *InterchainAccountsTestSuite) TestMsgSendTx_SuccessfulTransfer() { + s.testMsgSendTxSuccessfulTransfer(channeltypes.ORDERED) +} + +func (s *InterchainAccountsTestSuite) TestMsgSendTx_SuccessfulTransfer_UnorderedChannel() { + s.testMsgSendTxSuccessfulTransfer(channeltypes.UNORDERED) +} + +func (s *InterchainAccountsTestSuite) testMsgSendTxSuccessfulTransfer(order channeltypes.Order) { + t := s.T() + ctx := context.TODO() + + testName := t.Name() + relayer := s.CreateDefaultPaths(testName) + + 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) { + // explicitly set the version string because we don't want to use incentivized channels. + version := icatypes.NewDefaultMetadataString(ibctesting.FirstConnectionID, ibctesting.FirstConnectionID) + msgRegisterAccount := controllertypes.NewMsgRegisterInterchainAccount(ibctesting.FirstConnectionID, controllerAddress, version, order) + + txResp := s.BroadcastMessages(ctx, chainA, controllerAccount, msgRegisterAccount) + s.AssertTxSuccess(txResp) + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer, testName) + }) + + t.Run("verify interchain account", func(t *testing.T) { + var err error + hostAccount, err = query.InterchainAccount(ctx, chainA, controllerAddress, ibctesting.FirstConnectionID) + s.Require().NoError(err) + s.Require().NotEmpty(hostAccount) + + channels, err := relayer.GetChannels(ctx, s.GetRelayerExecReporter(), chainA.Config().ChainID) + s.Require().NoError(err) + s.Require().Equal(len(channels), 2) + icaChannel := channels[0] + + s.Require().Contains(orderMapping[order], icaChannel.Ordering) + }) + + 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: sdkmath.NewInt(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}, icatypes.EncodingProtobuf) + 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) + + resp := s.BroadcastMessages( + ctx, + chainA, + controllerAccount, + msgSendTx, + ) + + s.AssertTxSuccess(resp) + + s.Require().NoError(test.WaitForBlocks(ctx, 10, chainA, chainB)) + }) + + t.Run("verify tokens transferred", func(t *testing.T) { + balance, err := query.Balance(ctx, chainB, chainBAccount.FormattedAddress(), chainB.Config().Denom) + s.Require().NoError(err) + + _, err = query.Balance(ctx, chainB, hostAccount, chainB.Config().Denom) + s.Require().NoError(err) + + expected := testvalues.IBCTransferAmount + testvalues.StartingTokenAmount + s.Require().Equal(expected, balance.Int64()) + }) + }) +} + +func (s *InterchainAccountsTestSuite) TestMsgSendTx_FailedTransfer_InsufficientFunds() { + t := s.T() + ctx := context.TODO() + + testName := t.Name() + relayer := s.CreateDefaultPaths(testName) + + 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) { + // explicitly set the version string because we don't want to use incentivized channels. + version := icatypes.NewDefaultMetadataString(ibctesting.FirstConnectionID, ibctesting.FirstConnectionID) + msgRegisterAccount := controllertypes.NewMsgRegisterInterchainAccount(ibctesting.FirstConnectionID, controllerAddress, version, channeltypes.ORDERED) + + txResp := s.BroadcastMessages(ctx, chainA, controllerAccount, msgRegisterAccount) + s.AssertTxSuccess(txResp) + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer, testName) + }) + + t.Run("verify interchain account", func(t *testing.T) { + var err error + hostAccount, err = query.InterchainAccount(ctx, chainA, controllerAddress, ibctesting.FirstConnectionID) + s.Require().NoError(err) + s.Require().NotEmpty(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 := query.Balance(ctx, chainB, hostAccount, chainB.Config().Denom) + + s.Require().NoError(err) + s.Require().Zero(hostAccountBalance.Int64()) + }) + + 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}, icatypes.EncodingProtobuf) + 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 := s.BroadcastMessages( + ctx, + chainA, + controllerAccount, + msgSendTx, + ) + + s.AssertTxSuccess(txResp) + + s.Require().NoError(test.WaitForBlocks(ctx, 10, chainA, chainB)) + }) + + t.Run("verify balance is the same", func(t *testing.T) { + balance, err := query.Balance(ctx, chainB, chainBAccount.FormattedAddress(), chainB.Config().Denom) + s.Require().NoError(err) + + expected := testvalues.StartingTokenAmount + s.Require().Equal(expected, balance.Int64()) + }) + }) +} + +func (s *InterchainAccountsTestSuite) TestMsgSendTx_SuccessfulTransfer_AfterReopeningICA() { + t := s.T() + ctx := context.TODO() + + testName := t.Name() + relayer := s.CreateDefaultPaths(testName) + + 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 + // explicitly set the version string because we don't want to use incentivized channels. + version := icatypes.NewDefaultMetadataString(ibctesting.FirstConnectionID, ibctesting.FirstConnectionID) + msgRegisterInterchainAccount := controllertypes.NewMsgRegisterInterchainAccount(ibctesting.FirstConnectionID, controllerAddress, version, channeltypes.ORDERED) + 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, testName) + }) + + t.Run("verify interchain account", func(t *testing.T) { + var err error + hostAccount, err = query.InterchainAccount(ctx, chainA, controllerAddress, ibctesting.FirstConnectionID) + s.Require().NoError(err) + s.Require().NotEmpty(hostAccount) + + _, err = query.Channel(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 so it has some $$ to send + err := chainB.SendFunds(ctx, interchaintest.FaucetAccountKeyName, ibc.WalletAmount{ + Address: hostAccount, + Amount: sdkmath.NewInt(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}, icatypes.EncodingProtobuf) + 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 := s.BroadcastMessages( + ctx, + chainA, + controllerAccount, + msgSendTx, + ) + + s.AssertTxSuccess(resp) + + // 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, testName) + }) + + t.Run("verify channel is closed due to timeout on ordered channel", func(t *testing.T) { + channel, err := query.Channel(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 := query.Balance(ctx, chainB, chainBAccount.FormattedAddress(), chainB.Config().Denom) + s.Require().NoError(err) + + _, err = query.Balance(ctx, chainB, hostAccount, chainB.Config().Denom) + s.Require().NoError(err) + + expected := testvalues.StartingTokenAmount + s.Require().Equal(expected, balance.Int64()) + }) + + // 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) { + // explicitly set the version string because we don't want to use incentivized channels. + version := icatypes.NewDefaultMetadataString(ibctesting.FirstConnectionID, ibctesting.FirstConnectionID) + msgRegisterInterchainAccount := controllertypes.NewMsgRegisterInterchainAccount(ibctesting.FirstConnectionID, controllerAddress, version, channeltypes.ORDERED) + 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 := query.Channel(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}, icatypes.EncodingProtobuf) + 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 := s.BroadcastMessages( + ctx, + chainA, + controllerAccount, + msgSendTx, + ) + + s.AssertTxSuccess(resp) + + // 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 := query.Balance(ctx, chainB, chainBAccount.FormattedAddress(), chainB.Config().Denom) + s.Require().NoError(err) + + expected := testvalues.IBCTransferAmount + testvalues.StartingTokenAmount + s.Require().Equal(expected, balance.Int64()) + }) +} + +func (s *InterchainAccountsTestSuite) TestMsgSendTx_SuccessfulSubmitGovProposal() { + s.testMsgSendTxSuccessfulGovProposal(channeltypes.ORDERED) +} + +func (s *InterchainAccountsTestSuite) TestMsgSendTx_SuccessfulSubmitGovProposal_UnorderedChannel() { + s.testMsgSendTxSuccessfulGovProposal(channeltypes.UNORDERED) +} + +func (s *InterchainAccountsTestSuite) testMsgSendTxSuccessfulGovProposal(order channeltypes.Order) { + t := s.T() + ctx := context.TODO() + + testName := t.Name() + relayer := s.CreateDefaultPaths(testName) + + 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() + var hostAccount string + + t.Run("broadcast MsgRegisterInterchainAccount", func(t *testing.T) { + // explicitly set the version string because we don't want to use incentivized channels. + version := icatypes.NewDefaultMetadataString(ibctesting.FirstConnectionID, ibctesting.FirstConnectionID) + msgRegisterAccount := controllertypes.NewMsgRegisterInterchainAccount(ibctesting.FirstConnectionID, controllerAddress, version, order) + + txResp := s.BroadcastMessages(ctx, chainA, controllerAccount, msgRegisterAccount) + s.AssertTxSuccess(txResp) + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer, testName) + }) + + t.Run("verify interchain account", func(t *testing.T) { + var err error + hostAccount, err = query.InterchainAccount(ctx, chainA, controllerAddress, ibctesting.FirstConnectionID) + s.Require().NoError(err) + s.Require().NotEmpty(hostAccount) + + channels, err := relayer.GetChannels(ctx, s.GetRelayerExecReporter(), chainA.Config().ChainID) + s.Require().NoError(err) + s.Require().Equal(len(channels), 2) + icaChannel := channels[0] + + s.Require().Contains(orderMapping[order], icaChannel.Ordering) + }) + + t.Run("interchain account submits a governance proposal 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: sdkmath.NewInt(testvalues.StartingTokenAmount), + Denom: chainB.Config().Denom, + }) + s.Require().NoError(err) + }) + + t.Run("broadcast MsgSendTx for MsgSubmitProposal", func(t *testing.T) { + govModuleAddress, err := query.ModuleAccountAddress(ctx, govtypes.ModuleName, chainB) + s.Require().NoError(err) + s.Require().NotNil(govModuleAddress) + + testProposal := &controllertypes.MsgUpdateParams{ + Signer: govModuleAddress.String(), + Params: controllertypes.NewParams(false), + } + + msg, err := govv1.NewMsgSubmitProposal( + []sdk.Msg{testProposal}, + sdk.NewCoins(sdk.NewCoin(chainB.Config().Denom, sdkmath.NewInt(10_000_000))), + hostAccount, "e2e", "e2e", "e2e", false, + ) + s.Require().NoError(err) + + cdc := testsuite.Codec() + bz, err := icatypes.SerializeCosmosTx(cdc, []proto.Message{msg}, icatypes.EncodingProtobuf) + 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) + resp := s.BroadcastMessages( + ctx, + chainA, + controllerAccount, + msgSendTx, + ) + + s.AssertTxSuccess(resp) + + s.Require().NoError(test.WaitForBlocks(ctx, 10, chainA, chainB)) + }) + + t.Run("verify proposal included", func(t *testing.T) { + proposalResp, err := query.GRPCQuery[govv1.QueryProposalResponse](ctx, chainB, &govv1.QueryProposalRequest{ProposalId: 1}) + s.Require().NoError(err) + + s.Require().Equal("e2e", proposalResp.Proposal.Title) + }) + }) +} diff --git a/e2e/tests/interchain_accounts/gov_test.go b/e2e/tests/interchain_accounts/gov_test.go new file mode 100644 index 00000000000..9b27d42db2e --- /dev/null +++ b/e2e/tests/interchain_accounts/gov_test.go @@ -0,0 +1,125 @@ +//go:build !test_e2e + +package interchainaccounts + +import ( + "context" + "testing" + "time" + + "github.com/cosmos/gogoproto/proto" + "github.com/strangelove-ventures/interchaintest/v8" + "github.com/strangelove-ventures/interchaintest/v8/ibc" + test "github.com/strangelove-ventures/interchaintest/v8/testutil" + testifysuite "github.com/stretchr/testify/suite" + + sdkmath "cosmossdk.io/math" + + 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/ibc-go/e2e/testsuite" + "github.com/cosmos/ibc-go/e2e/testsuite/query" + "github.com/cosmos/ibc-go/e2e/testvalues" + controllertypes "github.com/cosmos/ibc-go/v9/modules/apps/27-interchain-accounts/controller/types" + icatypes "github.com/cosmos/ibc-go/v9/modules/apps/27-interchain-accounts/types" + channeltypes "github.com/cosmos/ibc-go/v9/modules/core/04-channel/types" + ibctesting "github.com/cosmos/ibc-go/v9/testing" +) + +func TestInterchainAccountsGovTestSuite(t *testing.T) { + testifysuite.Run(t, new(InterchainAccountsGovTestSuite)) +} + +type InterchainAccountsGovTestSuite struct { + testsuite.E2ETestSuite +} + +func (s *InterchainAccountsGovTestSuite) TestInterchainAccountsGovIntegration() { + t := s.T() + ctx := context.TODO() + + testName := t.Name() + relayer := s.CreateDefaultPaths(testName) + + chainA, chainB := s.GetChains() + controllerAccount := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + + chainBAccount := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount) + chainBAddress := chainBAccount.FormattedAddress() + + govModuleAddress, err := query.ModuleAccountAddress(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, channeltypes.ORDERED) + s.ExecuteAndPassGovV1Proposal(ctx, msgRegisterAccount, chainA, controllerAccount) + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer, testName) + }) + + 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 = query.InterchainAccount(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: sdkmath.NewInt(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}, icatypes.EncodingProtobuf) + 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.ExecuteAndPassGovV1Proposal(ctx, msgSendTx, chainA, controllerAccount) + }) + + s.Require().NoError(test.WaitForBlocks(ctx, 10, chainA, chainB)) // wait for the ica tx to be relayed + + t.Run("verify tokens transferred", func(t *testing.T) { + balance, err := query.Balance(ctx, chainB, chainBAccount.FormattedAddress(), chainB.Config().Denom) + s.Require().NoError(err) + + _, err = query.Balance(ctx, chainB, interchainAccAddr, chainB.Config().Denom) + s.Require().NoError(err) + + expected := testvalues.IBCTransferAmount + testvalues.StartingTokenAmount + s.Require().Equal(expected, balance.Int64()) + }) + }) +} diff --git a/e2e/tests/interchain_accounts/groups_test.go b/e2e/tests/interchain_accounts/groups_test.go new file mode 100644 index 00000000000..361d80190da --- /dev/null +++ b/e2e/tests/interchain_accounts/groups_test.go @@ -0,0 +1,215 @@ +//go:build !test_e2e + +package interchainaccounts + +import ( + "context" + "testing" + "time" + + "github.com/cosmos/gogoproto/proto" + interchaintest "github.com/strangelove-ventures/interchaintest/v8" + "github.com/strangelove-ventures/interchaintest/v8/ibc" + test "github.com/strangelove-ventures/interchaintest/v8/testutil" + testifysuite "github.com/stretchr/testify/suite" + + sdkmath "cosmossdk.io/math" + + 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/ibc-go/e2e/testsuite" + "github.com/cosmos/ibc-go/e2e/testsuite/query" + "github.com/cosmos/ibc-go/e2e/testvalues" + controllertypes "github.com/cosmos/ibc-go/v9/modules/apps/27-interchain-accounts/controller/types" + icatypes "github.com/cosmos/ibc-go/v9/modules/apps/27-interchain-accounts/types" + channeltypes "github.com/cosmos/ibc-go/v9/modules/core/04-channel/types" + ibctesting "github.com/cosmos/ibc-go/v9/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 reusable 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) { + testifysuite.Run(t, new(InterchainAccountsGroupsTestSuite)) +} + +type InterchainAccountsGroupsTestSuite struct { + testsuite.E2ETestSuite +} + +func (s *InterchainAccountsGroupsTestSuite) QueryGroupPolicyAddress(ctx context.Context, chain ibc.Chain) string { + res, err := query.GRPCQuery[grouptypes.QueryGroupPoliciesByGroupResponse](ctx, chain, &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 + ) + + testName := t.Name() + relayer := s.CreateDefaultPaths(testName) + + 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 := s.BroadcastMessages(ctx, chainA, chainAWallet, msgCreateGroupWithPolicy) + s.AssertTxSuccess(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), channeltypes.ORDERED) + + 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 := s.BroadcastMessages(ctx, chainA, chainAWallet, msgSubmitProposal) + s.AssertTxSuccess(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 := s.BroadcastMessages(ctx, chainA, chainAWallet, msgVote) + s.AssertTxSuccess(txResp) + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer, testName) + }) + + t.Run("verify interchain account registration success", func(t *testing.T) { + interchainAccAddr, err = query.InterchainAccount(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: sdkmath.NewInt(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}, icatypes.EncodingProtobuf) + 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 := s.BroadcastMessages(ctx, chainA, chainAWallet, msgSubmitProposal) + s.AssertTxSuccess(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 := s.BroadcastMessages(ctx, chainA, chainAWallet, msgVote) + s.AssertTxSuccess(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 := query.Balance(ctx, chainB, chainBAddress, chainB.Config().Denom) + + s.Require().NoError(err) + + expected := testvalues.IBCTransferAmount + testvalues.StartingTokenAmount + s.Require().Equal(expected, balance.Int64()) + + balance, err = query.Balance(ctx, chainB, interchainAccAddr, chainB.Config().Denom) + s.Require().NoError(err) + + expected = testvalues.StartingTokenAmount - testvalues.IBCTransferAmount + s.Require().Equal(expected, balance.Int64()) + }) +} diff --git a/e2e/tests/interchain_accounts/incentivized_test.go b/e2e/tests/interchain_accounts/incentivized_test.go new file mode 100644 index 00000000000..96d53ad4b86 --- /dev/null +++ b/e2e/tests/interchain_accounts/incentivized_test.go @@ -0,0 +1,374 @@ +//go:build !test_e2e + +package interchainaccounts + +import ( + "context" + "testing" + "time" + + "github.com/cosmos/gogoproto/proto" + interchaintest "github.com/strangelove-ventures/interchaintest/v8" + "github.com/strangelove-ventures/interchaintest/v8/ibc" + test "github.com/strangelove-ventures/interchaintest/v8/testutil" + testifysuite "github.com/stretchr/testify/suite" + + sdkmath "cosmossdk.io/math" + + sdk "github.com/cosmos/cosmos-sdk/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + + "github.com/cosmos/ibc-go/e2e/testsuite" + "github.com/cosmos/ibc-go/e2e/testsuite/query" + "github.com/cosmos/ibc-go/e2e/testvalues" + controllertypes "github.com/cosmos/ibc-go/v9/modules/apps/27-interchain-accounts/controller/types" + icatypes "github.com/cosmos/ibc-go/v9/modules/apps/27-interchain-accounts/types" + feetypes "github.com/cosmos/ibc-go/v9/modules/apps/29-fee/types" + channeltypes "github.com/cosmos/ibc-go/v9/modules/core/04-channel/types" + ibctesting "github.com/cosmos/ibc-go/v9/testing" +) + +func TestIncentivizedInterchainAccountsTestSuite(t *testing.T) { + testifysuite.Run(t, new(IncentivizedInterchainAccountsTestSuite)) +} + +type IncentivizedInterchainAccountsTestSuite struct { + InterchainAccountsTestSuite +} + +func (s *IncentivizedInterchainAccountsTestSuite) TestMsgSendTx_SuccessfulBankSend_Incentivized() { + t := s.T() + ctx := context.TODO() + + testName := t.Name() + relayer := s.CreateDefaultPaths(testName) + + chainA, chainB := s.GetChains() + + var ( + chainADenom = chainA.Config().Denom + interchainAcc = "" + testFee = testvalues.DefaultFee(chainADenom) + ) + + chainARelayerWallet, chainBRelayerWallet, err := s.RecoverRelayerWallets(ctx, relayer, testName) + t.Run("relayer wallets recovered", 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, testName) + 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 := "" // allow version to be specified by the controller chain since both chains should support incentivized channels + msgRegisterAccount := controllertypes.NewMsgRegisterInterchainAccount(ibctesting.FirstConnectionID, controllerAccount.FormattedAddress(), version, channeltypes.ORDERED) + + txResp := s.BroadcastMessages(ctx, chainA, controllerAccount, msgRegisterAccount) + s.AssertTxSuccess(txResp) + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer, testName) + }) + + var channelOutput ibc.ChannelOutput + t.Run("verify interchain account", func(t *testing.T) { + var err error + interchainAcc, err = query.InterchainAccount(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: sdkmath.NewInt(testvalues.StartingTokenAmount), + Denom: chainB.Config().Denom, + }) + s.Require().NoError(err) + }) + + t.Run("register counterparty payee", func(t *testing.T) { + resp := s.RegisterCounterPartyPayee(ctx, chainB, chainBRelayerUser, channelOutput.Counterparty.PortID, channelOutput.Counterparty.ChannelID, chainBRelayerWallet.FormattedAddress(), chainARelayerWallet.FormattedAddress()) + s.AssertTxSuccess(resp) + }) + + t.Run("verify counterparty payee", func(t *testing.T) { + address, err := query.CounterPartyPayee(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 := query.IncentivizedPacketsForChannel(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}, icatypes.EncodingProtobuf) + 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 := s.BroadcastMessages(ctx, chainA, controllerAccount, msgPayPacketFee, msgSendTx) + s.AssertTxSuccess(resp) + + s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB)) + }) + + t.Run("there should be incentivized packets", func(t *testing.T) { + packets, err := query.IncentivizedPacketsForChannel(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.Equal(testFee.RecvFee)) + s.Require().True(actualFee.AckFee.Equal(testFee.AckFee)) + s.Require().True(actualFee.TimeoutFee.Equal(testFee.TimeoutFee)) + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer, testName) + }) + + t.Run("packets are relayed", func(t *testing.T) { + packets, err := query.IncentivizedPacketsForChannel(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 := query.Balance(ctx, chainB, chainBAccount.FormattedAddress(), chainB.Config().Denom) + s.Require().NoError(err) + + _, err = query.Balance(ctx, chainB, interchainAcc, chainB.Config().Denom) + s.Require().NoError(err) + + expected := testvalues.IBCTransferAmount + testvalues.StartingTokenAmount + s.Require().Equal(expected, balance.Int64()) + }) + + 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() + + testName := t.Name() + relayer := s.CreateDefaultPaths(testName) + + chainA, chainB := s.GetChains() + + var ( + chainADenom = chainA.Config().Denom + interchainAcc = "" + testFee = testvalues.DefaultFee(chainADenom) + ) + + chainARelayerWallet, chainBRelayerWallet, err := s.RecoverRelayerWallets(ctx, relayer, testName) + t.Run("relayer wallets recovered", 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, testName) + 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 := "" // allow version to be specified by the controller chain since both chains should support incentivized channels + msgRegisterAccount := controllertypes.NewMsgRegisterInterchainAccount(ibctesting.FirstConnectionID, controllerAccount.FormattedAddress(), version, channeltypes.ORDERED) + + txResp := s.BroadcastMessages(ctx, chainA, controllerAccount, msgRegisterAccount) + s.AssertTxSuccess(txResp) + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer, testName) + }) + + var channelOutput ibc.ChannelOutput + t.Run("verify interchain account", func(t *testing.T) { + var err error + interchainAcc, err = query.InterchainAccount(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 := s.RegisterCounterPartyPayee(ctx, chainB, chainBRelayerUser, channelOutput.Counterparty.PortID, channelOutput.Counterparty.ChannelID, chainBRelayerWallet.FormattedAddress(), chainARelayerWallet.FormattedAddress()) + s.AssertTxSuccess(resp) + }) + + t.Run("verify counterparty payee", func(t *testing.T) { + address, err := query.CounterPartyPayee(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 := query.IncentivizedPacketsForChannel(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}, icatypes.EncodingProtobuf) + 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 := s.BroadcastMessages(ctx, chainA, controllerAccount, msgPayPacketFee, msgSendTx) + s.AssertTxSuccess(resp) + + s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB)) + }) + + t.Run("there should be incentivized packets", func(t *testing.T) { + packets, err := query.IncentivizedPacketsForChannel(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.Equal(testFee.RecvFee)) + s.Require().True(actualFee.AckFee.Equal(testFee.AckFee)) + s.Require().True(actualFee.TimeoutFee.Equal(testFee.TimeoutFee)) + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer, testName) + }) + + t.Run("packets are relayed", func(t *testing.T) { + packets, err := query.IncentivizedPacketsForChannel(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 := query.Balance(ctx, chainB, chainBAccount.FormattedAddress(), chainB.Config().Denom) + s.Require().NoError(err) + + _, err = query.Balance(ctx, chainB, interchainAcc, chainB.Config().Denom) + s.Require().NoError(err) + + expected := testvalues.StartingTokenAmount + s.Require().Equal(expected, balance.Int64(), "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/localhost_test.go b/e2e/tests/interchain_accounts/localhost_test.go new file mode 100644 index 00000000000..edadfcc7ec9 --- /dev/null +++ b/e2e/tests/interchain_accounts/localhost_test.go @@ -0,0 +1,481 @@ +//go:build !test_e2e + +package interchainaccounts + +import ( + "context" + "testing" + "time" + + "github.com/cosmos/gogoproto/proto" + "github.com/strangelove-ventures/interchaintest/v8" + "github.com/strangelove-ventures/interchaintest/v8/ibc" + test "github.com/strangelove-ventures/interchaintest/v8/testutil" + testifysuite "github.com/stretchr/testify/suite" + + sdkmath "cosmossdk.io/math" + + sdk "github.com/cosmos/cosmos-sdk/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + + "github.com/cosmos/ibc-go/e2e/testsuite" + "github.com/cosmos/ibc-go/e2e/testsuite/query" + "github.com/cosmos/ibc-go/e2e/testvalues" + controllertypes "github.com/cosmos/ibc-go/v9/modules/apps/27-interchain-accounts/controller/types" + icatypes "github.com/cosmos/ibc-go/v9/modules/apps/27-interchain-accounts/types" + clienttypes "github.com/cosmos/ibc-go/v9/modules/core/02-client/types" + channeltypes "github.com/cosmos/ibc-go/v9/modules/core/04-channel/types" + "github.com/cosmos/ibc-go/v9/modules/core/exported" + localhost "github.com/cosmos/ibc-go/v9/modules/light-clients/09-localhost" + ibctesting "github.com/cosmos/ibc-go/v9/testing" +) + +func TestInterchainAccountsLocalhostTestSuite(t *testing.T) { + testifysuite.Run(t, new(LocalhostInterchainAccountsTestSuite)) +} + +type LocalhostInterchainAccountsTestSuite struct { + testsuite.E2ETestSuite +} + +func (s *LocalhostInterchainAccountsTestSuite) TestInterchainAccounts_Localhost() { + t := s.T() + ctx := context.TODO() + + testName := t.Name() + s.CreateDefaultPaths(testName) + + chainA, _ := s.GetChains() + + chainADenom := chainA.Config().Denom + + rlyWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + userAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + userBWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + + var ( + msgChanOpenInitRes channeltypes.MsgChannelOpenInitResponse + msgChanOpenTryRes channeltypes.MsgChannelOpenTryResponse + ack []byte + packet channeltypes.Packet + ) + + s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA), "failed to wait for blocks") + + version := icatypes.NewDefaultMetadataString(exported.LocalhostConnectionID, exported.LocalhostConnectionID) + controllerPortID, err := icatypes.NewControllerPortID(userAWallet.FormattedAddress()) + s.Require().NoError(err) + + t.Run("channel open init localhost - broadcast MsgRegisterInterchainAccount", func(t *testing.T) { + msgRegisterAccount := controllertypes.NewMsgRegisterInterchainAccount(exported.LocalhostConnectionID, userAWallet.FormattedAddress(), version, channeltypes.ORDERED) + + txResp := s.BroadcastMessages(ctx, chainA, userAWallet, msgRegisterAccount) + s.AssertTxSuccess(txResp) + + s.Require().NoError(testsuite.UnmarshalMsgResponses(txResp, &msgChanOpenInitRes)) + }) + + t.Run("channel open try localhost", func(t *testing.T) { + msgChanOpenTry := channeltypes.NewMsgChannelOpenTry( + icatypes.HostPortID, icatypes.Version, + channeltypes.ORDERED, []string{exported.LocalhostConnectionID}, + controllerPortID, msgChanOpenInitRes.ChannelId, + version, localhost.SentinelProof, clienttypes.ZeroHeight(), rlyWallet.FormattedAddress(), + ) + + txResp := s.BroadcastMessages(ctx, chainA, rlyWallet, msgChanOpenTry) + s.AssertTxSuccess(txResp) + + s.Require().NoError(testsuite.UnmarshalMsgResponses(txResp, &msgChanOpenTryRes)) + }) + + t.Run("channel open ack localhost", func(t *testing.T) { + msgChanOpenAck := channeltypes.NewMsgChannelOpenAck( + controllerPortID, msgChanOpenInitRes.ChannelId, + msgChanOpenTryRes.ChannelId, msgChanOpenTryRes.Version, + localhost.SentinelProof, clienttypes.ZeroHeight(), rlyWallet.FormattedAddress(), + ) + + txResp := s.BroadcastMessages(ctx, chainA, rlyWallet, msgChanOpenAck) + s.AssertTxSuccess(txResp) + }) + + t.Run("channel open confirm localhost", func(t *testing.T) { + msgChanOpenConfirm := channeltypes.NewMsgChannelOpenConfirm( + icatypes.HostPortID, msgChanOpenTryRes.ChannelId, + localhost.SentinelProof, clienttypes.ZeroHeight(), rlyWallet.FormattedAddress(), + ) + + txResp := s.BroadcastMessages(ctx, chainA, rlyWallet, msgChanOpenConfirm) + s.AssertTxSuccess(txResp) + }) + + t.Run("query localhost interchain accounts channel ends", func(t *testing.T) { + channelEndA, err := query.Channel(ctx, chainA, controllerPortID, msgChanOpenInitRes.ChannelId) + s.Require().NoError(err) + s.Require().NotNil(channelEndA) + + channelEndB, err := query.Channel(ctx, chainA, icatypes.HostPortID, msgChanOpenTryRes.ChannelId) + s.Require().NoError(err) + s.Require().NotNil(channelEndB) + + s.Require().Equal(channelEndA.ConnectionHops, channelEndB.ConnectionHops) + }) + + t.Run("verify interchain account registration and deposit funds", func(t *testing.T) { + interchainAccAddress, err := query.InterchainAccount(ctx, chainA, userAWallet.FormattedAddress(), exported.LocalhostConnectionID) + s.Require().NoError(err) + s.Require().NotEmpty(interchainAccAddress) + + walletAmount := ibc.WalletAmount{ + Address: interchainAccAddress, + Amount: sdkmath.NewInt(testvalues.StartingTokenAmount), + Denom: chainADenom, + } + + s.Require().NoError(chainA.SendFunds(ctx, interchaintest.FaucetAccountKeyName, walletAmount)) + }) + + t.Run("send packet localhost interchain accounts", func(t *testing.T) { + interchainAccAddress, err := query.InterchainAccount(ctx, chainA, userAWallet.FormattedAddress(), exported.LocalhostConnectionID) + s.Require().NoError(err) + s.Require().NotEmpty(interchainAccAddress) + + msgSend := &banktypes.MsgSend{ + FromAddress: interchainAccAddress, + ToAddress: userBWallet.FormattedAddress(), + Amount: sdk.NewCoins(testvalues.DefaultTransferAmount(chainADenom)), + } + + cdc := testsuite.Codec() + bz, err := icatypes.SerializeCosmosTx(cdc, []proto.Message{msgSend}, icatypes.EncodingProtobuf) + s.Require().NoError(err) + + packetData := icatypes.InterchainAccountPacketData{ + Type: icatypes.EXECUTE_TX, + Data: bz, + Memo: "e2e", + } + + msgSendTx := controllertypes.NewMsgSendTx(userAWallet.FormattedAddress(), exported.LocalhostConnectionID, uint64(time.Hour.Nanoseconds()), packetData) + + txResp := s.BroadcastMessages(ctx, chainA, userAWallet, msgSendTx) + s.AssertTxSuccess(txResp) + + packet, err = ibctesting.ParsePacketFromEvents(txResp.Events) + s.Require().NoError(err) + s.Require().NotNil(packet) + }) + + t.Run("recv packet localhost interchain accounts", func(t *testing.T) { + msgRecvPacket := channeltypes.NewMsgRecvPacket(packet, localhost.SentinelProof, clienttypes.ZeroHeight(), rlyWallet.FormattedAddress()) + + txResp := s.BroadcastMessages(ctx, chainA, rlyWallet, msgRecvPacket) + s.AssertTxSuccess(txResp) + + ack, err = ibctesting.ParseAckFromEvents(txResp.Events) + s.Require().NoError(err) + s.Require().NotNil(ack) + }) + + t.Run("acknowledge packet localhost interchain accounts", func(t *testing.T) { + msgAcknowledgement := channeltypes.NewMsgAcknowledgement(packet, ack, localhost.SentinelProof, clienttypes.ZeroHeight(), rlyWallet.FormattedAddress()) + + txResp := s.BroadcastMessages(ctx, chainA, rlyWallet, msgAcknowledgement) + s.AssertTxSuccess(txResp) + }) + + t.Run("verify tokens transferred", func(t *testing.T) { + balance, err := query.Balance(ctx, chainA, userBWallet.FormattedAddress(), chainADenom) + s.Require().NoError(err) + + expected := testvalues.IBCTransferAmount + testvalues.StartingTokenAmount + s.Require().Equal(expected, balance.Int64()) + }) +} + +func (s *LocalhostInterchainAccountsTestSuite) TestInterchainAccounts_ReopenChannel_Localhost() { + t := s.T() + ctx := context.TODO() + + testName := t.Name() + s.CreateDefaultPaths(testName) + + chainA, _ := s.GetChains() + + chainADenom := chainA.Config().Denom + + rlyWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + userAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + userBWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + + var ( + msgChanOpenInitRes channeltypes.MsgChannelOpenInitResponse + msgChanOpenTryRes channeltypes.MsgChannelOpenTryResponse + ack []byte + packet channeltypes.Packet + ) + + s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA), "failed to wait for blocks") + + version := icatypes.NewDefaultMetadataString(exported.LocalhostConnectionID, exported.LocalhostConnectionID) + controllerPortID, err := icatypes.NewControllerPortID(userAWallet.FormattedAddress()) + s.Require().NoError(err) + + t.Run("channel open init localhost - broadcast MsgRegisterInterchainAccount", func(t *testing.T) { + msgRegisterAccount := controllertypes.NewMsgRegisterInterchainAccount(exported.LocalhostConnectionID, userAWallet.FormattedAddress(), version, channeltypes.ORDERED) + + txResp := s.BroadcastMessages(ctx, chainA, userAWallet, msgRegisterAccount) + s.AssertTxSuccess(txResp) + + s.Require().NoError(testsuite.UnmarshalMsgResponses(txResp, &msgChanOpenInitRes)) + }) + + t.Run("channel open try localhost", func(t *testing.T) { + msgChanOpenTry := channeltypes.NewMsgChannelOpenTry( + icatypes.HostPortID, icatypes.Version, + channeltypes.ORDERED, []string{exported.LocalhostConnectionID}, + controllerPortID, msgChanOpenInitRes.ChannelId, + version, localhost.SentinelProof, clienttypes.ZeroHeight(), rlyWallet.FormattedAddress(), + ) + + txResp := s.BroadcastMessages(ctx, chainA, rlyWallet, msgChanOpenTry) + s.AssertTxSuccess(txResp) + + s.Require().NoError(testsuite.UnmarshalMsgResponses(txResp, &msgChanOpenTryRes)) + }) + + t.Run("channel open ack localhost", func(t *testing.T) { + msgChanOpenAck := channeltypes.NewMsgChannelOpenAck( + controllerPortID, msgChanOpenInitRes.ChannelId, + msgChanOpenTryRes.ChannelId, msgChanOpenTryRes.Version, + localhost.SentinelProof, clienttypes.ZeroHeight(), rlyWallet.FormattedAddress(), + ) + + txResp := s.BroadcastMessages(ctx, chainA, rlyWallet, msgChanOpenAck) + s.AssertTxSuccess(txResp) + }) + + t.Run("channel open confirm localhost", func(t *testing.T) { + msgChanOpenConfirm := channeltypes.NewMsgChannelOpenConfirm( + icatypes.HostPortID, msgChanOpenTryRes.ChannelId, + localhost.SentinelProof, clienttypes.ZeroHeight(), rlyWallet.FormattedAddress(), + ) + + txResp := s.BroadcastMessages(ctx, chainA, rlyWallet, msgChanOpenConfirm) + s.AssertTxSuccess(txResp) + }) + + t.Run("query localhost interchain accounts channel ends", func(t *testing.T) { + channelEndA, err := query.Channel(ctx, chainA, controllerPortID, msgChanOpenInitRes.ChannelId) + s.Require().NoError(err) + s.Require().NotNil(channelEndA) + + channelEndB, err := query.Channel(ctx, chainA, icatypes.HostPortID, msgChanOpenTryRes.ChannelId) + s.Require().NoError(err) + s.Require().NotNil(channelEndB) + + s.Require().Equal(channelEndA.ConnectionHops, channelEndB.ConnectionHops) + }) + + t.Run("verify interchain account registration and deposit funds", func(t *testing.T) { + interchainAccAddress, err := query.InterchainAccount(ctx, chainA, userAWallet.FormattedAddress(), exported.LocalhostConnectionID) + s.Require().NoError(err) + s.Require().NotEmpty(interchainAccAddress) + + walletAmount := ibc.WalletAmount{ + Address: interchainAccAddress, + Amount: sdkmath.NewInt(testvalues.StartingTokenAmount), + Denom: chainADenom, + } + + s.Require().NoError(chainA.SendFunds(ctx, interchaintest.FaucetAccountKeyName, walletAmount)) + }) + + t.Run("send localhost interchain accounts packet with timeout", func(t *testing.T) { + interchainAccAddress, err := query.InterchainAccount(ctx, chainA, userAWallet.FormattedAddress(), exported.LocalhostConnectionID) + s.Require().NoError(err) + s.Require().NotEmpty(interchainAccAddress) + + msgSend := &banktypes.MsgSend{ + FromAddress: interchainAccAddress, + ToAddress: userBWallet.FormattedAddress(), + Amount: sdk.NewCoins(testvalues.DefaultTransferAmount(chainADenom)), + } + + cdc := testsuite.Codec() + bz, err := icatypes.SerializeCosmosTx(cdc, []proto.Message{msgSend}, icatypes.EncodingProtobuf) + s.Require().NoError(err) + + packetData := icatypes.InterchainAccountPacketData{ + Type: icatypes.EXECUTE_TX, + Data: bz, + Memo: "e2e", + } + + msgSendTx := controllertypes.NewMsgSendTx(userAWallet.FormattedAddress(), exported.LocalhostConnectionID, uint64(1), packetData) + + txResp := s.BroadcastMessages(ctx, chainA, userAWallet, msgSendTx) + s.AssertTxSuccess(txResp) + + packet, err = ibctesting.ParsePacketFromEvents(txResp.Events) + s.Require().NoError(err) + s.Require().NotNil(packet) + }) + + t.Run("timeout localhost interchain accounts packet", func(t *testing.T) { + msgTimeout := channeltypes.NewMsgTimeout(packet, 1, localhost.SentinelProof, clienttypes.ZeroHeight(), rlyWallet.FormattedAddress()) + + txResp := s.BroadcastMessages(ctx, chainA, rlyWallet, msgTimeout) + s.AssertTxSuccess(txResp) + }) + + t.Run("close interchain accounts host channel end", func(t *testing.T) { + // Pass in zero for counterpartyUpgradeSequence given that channel has not undergone any upgrades. + msgCloseConfirm := channeltypes.NewMsgChannelCloseConfirm(icatypes.HostPortID, msgChanOpenTryRes.ChannelId, localhost.SentinelProof, clienttypes.ZeroHeight(), rlyWallet.FormattedAddress(), 0) + + txResp := s.BroadcastMessages(ctx, chainA, rlyWallet, msgCloseConfirm) + s.AssertTxSuccess(txResp) + }) + + t.Run("verify localhost interchain accounts channel is closed", func(t *testing.T) { + channelEndA, err := query.Channel(ctx, chainA, controllerPortID, msgChanOpenInitRes.ChannelId) + s.Require().NoError(err) + + s.Require().Equal(channeltypes.CLOSED, channelEndA.State, "the channel was not in an expected state") + + channelEndB, err := query.Channel(ctx, chainA, icatypes.HostPortID, msgChanOpenTryRes.ChannelId) + s.Require().NoError(err) + + s.Require().Equal(channeltypes.CLOSED, channelEndB.State, "the channel was not in an expected state") + }) + + t.Run("channel open init localhost: create new channel for existing account", func(t *testing.T) { + msgRegisterAccount := controllertypes.NewMsgRegisterInterchainAccount(exported.LocalhostConnectionID, userAWallet.FormattedAddress(), version, channeltypes.ORDERED) + + txResp := s.BroadcastMessages(ctx, chainA, userAWallet, msgRegisterAccount) + s.AssertTxSuccess(txResp) + + // note: response values are updated here in msgChanOpenInitRes + s.Require().NoError(testsuite.UnmarshalMsgResponses(txResp, &msgChanOpenInitRes)) + }) + + t.Run("channel open try localhost", func(t *testing.T) { + msgChanOpenTry := channeltypes.NewMsgChannelOpenTry( + icatypes.HostPortID, icatypes.Version, + channeltypes.ORDERED, []string{exported.LocalhostConnectionID}, + controllerPortID, msgChanOpenInitRes.ChannelId, + version, localhost.SentinelProof, clienttypes.ZeroHeight(), rlyWallet.FormattedAddress(), + ) + + txResp := s.BroadcastMessages(ctx, chainA, rlyWallet, msgChanOpenTry) + s.AssertTxSuccess(txResp) + + // note: response values are updated here in msgChanOpenTryRes + s.Require().NoError(testsuite.UnmarshalMsgResponses(txResp, &msgChanOpenTryRes)) + }) + + t.Run("channel open ack localhost", func(t *testing.T) { + msgChanOpenAck := channeltypes.NewMsgChannelOpenAck( + controllerPortID, msgChanOpenInitRes.ChannelId, + msgChanOpenTryRes.ChannelId, msgChanOpenTryRes.Version, + localhost.SentinelProof, clienttypes.ZeroHeight(), rlyWallet.FormattedAddress(), + ) + + txResp := s.BroadcastMessages(ctx, chainA, rlyWallet, msgChanOpenAck) + s.AssertTxSuccess(txResp) + }) + + t.Run("channel open confirm localhost", func(t *testing.T) { + msgChanOpenConfirm := channeltypes.NewMsgChannelOpenConfirm( + icatypes.HostPortID, msgChanOpenTryRes.ChannelId, + localhost.SentinelProof, clienttypes.ZeroHeight(), rlyWallet.FormattedAddress(), + ) + + txResp := s.BroadcastMessages(ctx, chainA, rlyWallet, msgChanOpenConfirm) + s.AssertTxSuccess(txResp) + }) + + t.Run("query localhost interchain accounts channel ends", func(t *testing.T) { + channelEndA, err := query.Channel(ctx, chainA, controllerPortID, msgChanOpenInitRes.ChannelId) + s.Require().NoError(err) + s.Require().NotNil(channelEndA) + + channelEndB, err := query.Channel(ctx, chainA, icatypes.HostPortID, msgChanOpenTryRes.ChannelId) + s.Require().NoError(err) + s.Require().NotNil(channelEndB) + + s.Require().Equal(channelEndA.ConnectionHops, channelEndB.ConnectionHops) + }) + + t.Run("verify interchain account and existing balance", func(t *testing.T) { + interchainAccAddress, err := query.InterchainAccount(ctx, chainA, userAWallet.FormattedAddress(), exported.LocalhostConnectionID) + s.Require().NoError(err) + s.Require().NotEmpty(interchainAccAddress) + + balance, err := query.Balance(ctx, chainA, interchainAccAddress, chainADenom) + s.Require().NoError(err) + + expected := testvalues.StartingTokenAmount + s.Require().Equal(expected, balance.Int64()) + }) + + t.Run("send packet localhost interchain accounts", func(t *testing.T) { + interchainAccAddress, err := query.InterchainAccount(ctx, chainA, userAWallet.FormattedAddress(), exported.LocalhostConnectionID) + s.Require().NoError(err) + s.Require().NotEmpty(interchainAccAddress) + + msgSend := &banktypes.MsgSend{ + FromAddress: interchainAccAddress, + ToAddress: userBWallet.FormattedAddress(), + Amount: sdk.NewCoins(testvalues.DefaultTransferAmount(chainADenom)), + } + + cdc := testsuite.Codec() + bz, err := icatypes.SerializeCosmosTx(cdc, []proto.Message{msgSend}, icatypes.EncodingProtobuf) + s.Require().NoError(err) + + packetData := icatypes.InterchainAccountPacketData{ + Type: icatypes.EXECUTE_TX, + Data: bz, + Memo: "e2e", + } + + msgSendTx := controllertypes.NewMsgSendTx(userAWallet.FormattedAddress(), exported.LocalhostConnectionID, uint64(time.Hour.Nanoseconds()), packetData) + + txResp := s.BroadcastMessages(ctx, chainA, userAWallet, msgSendTx) + s.AssertTxSuccess(txResp) + + packet, err = ibctesting.ParsePacketFromEvents(txResp.Events) + s.Require().NoError(err) + s.Require().NotNil(packet) + }) + + t.Run("recv packet localhost interchain accounts", func(t *testing.T) { + msgRecvPacket := channeltypes.NewMsgRecvPacket(packet, localhost.SentinelProof, clienttypes.ZeroHeight(), rlyWallet.FormattedAddress()) + + txResp := s.BroadcastMessages(ctx, chainA, rlyWallet, msgRecvPacket) + s.AssertTxSuccess(txResp) + + ack, err = ibctesting.ParseAckFromEvents(txResp.Events) + s.Require().NoError(err) + s.Require().NotNil(ack) + }) + + t.Run("acknowledge packet localhost interchain accounts", func(t *testing.T) { + msgAcknowledgement := channeltypes.NewMsgAcknowledgement(packet, ack, localhost.SentinelProof, clienttypes.ZeroHeight(), rlyWallet.FormattedAddress()) + + txResp := s.BroadcastMessages(ctx, chainA, rlyWallet, msgAcknowledgement) + s.AssertTxSuccess(txResp) + }) + + t.Run("verify tokens transferred", func(t *testing.T) { + s.AssertPacketRelayed(ctx, chainA, controllerPortID, msgChanOpenInitRes.ChannelId, 1) + + balance, err := query.Balance(ctx, chainA, userBWallet.FormattedAddress(), chainADenom) + s.Require().NoError(err) + + expected := testvalues.IBCTransferAmount + testvalues.StartingTokenAmount + s.Require().Equal(expected, balance.Int64()) + }) +} diff --git a/e2e/tests/interchain_accounts/params_test.go b/e2e/tests/interchain_accounts/params_test.go new file mode 100644 index 00000000000..f2e3ef05cb6 --- /dev/null +++ b/e2e/tests/interchain_accounts/params_test.go @@ -0,0 +1,271 @@ +//go:build !test_e2e + +package interchainaccounts + +import ( + "context" + "testing" + "time" + + "github.com/cosmos/gogoproto/proto" + "github.com/strangelove-ventures/interchaintest/v8" + "github.com/strangelove-ventures/interchaintest/v8/ibc" + test "github.com/strangelove-ventures/interchaintest/v8/testutil" + testifysuite "github.com/stretchr/testify/suite" + + sdkmath "cosmossdk.io/math" + + 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" + paramsproposaltypes "github.com/cosmos/cosmos-sdk/x/params/types/proposal" + + "github.com/cosmos/ibc-go/e2e/testsuite" + "github.com/cosmos/ibc-go/e2e/testsuite/query" + "github.com/cosmos/ibc-go/e2e/testvalues" + controllertypes "github.com/cosmos/ibc-go/v9/modules/apps/27-interchain-accounts/controller/types" + hosttypes "github.com/cosmos/ibc-go/v9/modules/apps/27-interchain-accounts/host/types" + icatypes "github.com/cosmos/ibc-go/v9/modules/apps/27-interchain-accounts/types" + channeltypes "github.com/cosmos/ibc-go/v9/modules/core/04-channel/types" + coretypes "github.com/cosmos/ibc-go/v9/modules/core/types" + ibctesting "github.com/cosmos/ibc-go/v9/testing" +) + +func TestInterchainAccountsParamsTestSuite(t *testing.T) { + testifysuite.Run(t, new(InterchainAccountsParamsTestSuite)) +} + +type InterchainAccountsParamsTestSuite struct { + testsuite.E2ETestSuite +} + +// QueryControllerParams queries the params for the controller +func (s *InterchainAccountsParamsTestSuite) QueryControllerParams(ctx context.Context, chain ibc.Chain) controllertypes.Params { + res, err := query.GRPCQuery[controllertypes.QueryParamsResponse](ctx, chain, &controllertypes.QueryParamsRequest{}) + s.Require().NoError(err) + + return *res.Params +} + +// QueryHostParams queries the host chain for the params +func (s *InterchainAccountsParamsTestSuite) QueryHostParams(ctx context.Context, chain ibc.Chain) hosttypes.Params { + res, err := query.GRPCQuery[hosttypes.QueryParamsResponse](ctx, chain, &hosttypes.QueryParamsRequest{}) + s.Require().NoError(err) + + return *res.Params +} + +// TestControllerEnabledParam tests that changing the ControllerEnabled param works as expected +func (s *InterchainAccountsParamsTestSuite) TestControllerEnabledParam() { + t := s.T() + ctx := context.TODO() + + testName := t.Name() + s.CreateDefaultPaths(testName) + + chainA, _ := s.GetChains() + chainAVersion := chainA.Config().Images[0].Version + + // setup controller account on chainA + controllerAccount := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + controllerAddress := controllerAccount.FormattedAddress() + + t.Run("ensure the controller is enabled", func(t *testing.T) { + params := s.QueryControllerParams(ctx, chainA) + s.Require().True(params.ControllerEnabled) + }) + + t.Run("disable the controller", func(t *testing.T) { + if testvalues.SelfParamsFeatureReleases.IsSupported(chainAVersion) { + authority, err := query.ModuleAccountAddress(ctx, govtypes.ModuleName, chainA) + s.Require().NoError(err) + s.Require().NotNil(authority) + + msg := controllertypes.MsgUpdateParams{ + Signer: authority.String(), + Params: controllertypes.NewParams(false), + } + s.ExecuteAndPassGovV1Proposal(ctx, &msg, chainA, controllerAccount) + } else { + changes := []paramsproposaltypes.ParamChange{ + paramsproposaltypes.NewParamChange(controllertypes.StoreKey, string(controllertypes.KeyControllerEnabled), "false"), + } + + proposal := paramsproposaltypes.NewParameterChangeProposal(ibctesting.Title, ibctesting.Description, changes) + s.ExecuteAndPassGovV1Beta1Proposal(ctx, chainA, controllerAccount, proposal) + } + }) + + t.Run("ensure controller is disabled", func(t *testing.T) { + params := s.QueryControllerParams(ctx, chainA) + s.Require().False(params.ControllerEnabled) + }) + + t.Run("ensure that broadcasting a MsgRegisterInterchainAccount fails", func(t *testing.T) { + // explicitly set the version string because we don't want to use incentivized channels. + version := icatypes.NewDefaultMetadataString(ibctesting.FirstConnectionID, ibctesting.FirstConnectionID) + msgRegisterAccount := controllertypes.NewMsgRegisterInterchainAccount(ibctesting.FirstConnectionID, controllerAddress, version, channeltypes.ORDERED) + + txResp := s.BroadcastMessages(ctx, chainA, controllerAccount, msgRegisterAccount) + s.AssertTxFailure(txResp, controllertypes.ErrControllerSubModuleDisabled) + }) +} + +func (s *InterchainAccountsParamsTestSuite) TestHostEnabledParam() { + t := s.T() + ctx := context.TODO() + + testName := t.Name() + relayer := s.CreateDefaultPaths(testName) + + chainA, chainB := s.GetChains() + chainBVersion := chainB.Config().Images[0].Version + + // setup 2 accounts: controller account on chain A, a second chain B account (to do the disable host gov proposal) + // 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) + chainBAddress := chainBAccount.FormattedAddress() + var hostAccount string + + // Assert that default value for enabled is true. + t.Run("ensure the host is enabled", func(t *testing.T) { + params := s.QueryHostParams(ctx, chainB) + s.Require().True(params.HostEnabled) + s.Require().Equal([]string{hosttypes.AllowAllHostMsgs}, params.AllowMessages) + }) + + t.Run("ensure ica packets are flowing before disabling the host", func(t *testing.T) { + t.Run("broadcast MsgRegisterInterchainAccount", func(t *testing.T) { + // explicitly set the version string because we don't want to use incentivized channels. + version := icatypes.NewDefaultMetadataString(ibctesting.FirstConnectionID, ibctesting.FirstConnectionID) + msgRegisterAccount := controllertypes.NewMsgRegisterInterchainAccount(ibctesting.FirstConnectionID, controllerAddress, version, channeltypes.ORDERED) + + txResp := s.BroadcastMessages(ctx, chainA, controllerAccount, msgRegisterAccount) + s.AssertTxSuccess(txResp) + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer, testName) + }) + + t.Run("verify interchain account", func(t *testing.T) { + var err error + hostAccount, err = query.InterchainAccount(ctx, chainA, controllerAddress, ibctesting.FirstConnectionID) + s.Require().NoError(err) + s.Require().NotEmpty(hostAccount) + + channels, err := relayer.GetChannels(ctx, s.GetRelayerExecReporter(), chainA.Config().ChainID) + s.Require().NoError(err) + s.Require().Equal(len(channels), 2) + }) + + t.Run("stop relayer", func(t *testing.T) { + s.StopRelayer(ctx, relayer) + }) + }) + + t.Run("disable the host", func(t *testing.T) { + if testvalues.SelfParamsFeatureReleases.IsSupported(chainBVersion) { + authority, err := query.ModuleAccountAddress(ctx, govtypes.ModuleName, chainB) + s.Require().NoError(err) + s.Require().NotNil(authority) + + msg := hosttypes.MsgUpdateParams{ + Signer: authority.String(), + Params: hosttypes.NewParams(false, []string{hosttypes.AllowAllHostMsgs}), + } + s.ExecuteAndPassGovV1Proposal(ctx, &msg, chainB, chainBAccount) + } else { + changes := []paramsproposaltypes.ParamChange{ + paramsproposaltypes.NewParamChange(hosttypes.StoreKey, string(hosttypes.KeyHostEnabled), "false"), + } + + proposal := paramsproposaltypes.NewParameterChangeProposal(ibctesting.Title, ibctesting.Description, changes) + s.ExecuteAndPassGovV1Beta1Proposal(ctx, chainB, chainBAccount, proposal) + } + }) + + t.Run("ensure the host is disabled", func(t *testing.T) { + params := s.QueryHostParams(ctx, chainB) + s.Require().False(params.HostEnabled) + }) + + t.Run("ensure that ica packets are not flowing", 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: sdkmath.NewInt(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: chainBAddress, + Amount: sdk.NewCoins(testvalues.DefaultTransferAmount(chainB.Config().Denom)), + } + + cdc := testsuite.Codec() + bz, err := icatypes.SerializeCosmosTx(cdc, []proto.Message{msgSend}, icatypes.EncodingProtobuf) + 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) + + resp := s.BroadcastMessages( + ctx, + chainA, + controllerAccount, + msgSendTx, + ) + + s.AssertTxSuccess(resp) + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer, testName) + }) + + s.Require().NoError(test.WaitForBlocks(ctx, 10, chainA, chainB)) + + t.Run("verify no tokens were transferred", func(t *testing.T) { + chainBAccountBalance, err := query.Balance(ctx, chainB, chainBAddress, chainB.Config().Denom) + s.Require().NoError(err) + s.Require().Equal(testvalues.StartingTokenAmount, chainBAccountBalance.Int64()) + + hostAccountBalance, err := query.Balance(ctx, chainB, hostAccount, chainB.Config().Denom) + s.Require().NoError(err) + s.Require().Equal(testvalues.StartingTokenAmount, hostAccountBalance.Int64()) + }) + + t.Run("verify acknowledgement error in ack transaction", func(t *testing.T) { + cmd := "message.action=/ibc.core.channel.v1.MsgRecvPacket" + if testvalues.TransactionEventQueryFeatureReleases.IsSupported(chainBVersion) { + cmd = "message.action='/ibc.core.channel.v1.MsgRecvPacket'" + } + txSearchRes, err := s.QueryTxsByEvents(ctx, chainB, 1, 1, cmd, "") + s.Require().NoError(err) + s.Require().Len(txSearchRes.Txs, 1) + + errorMessage, isFound := s.ExtractValueFromEvents( + txSearchRes.Txs[0].Events, + coretypes.ErrorAttributeKeyPrefix+icatypes.EventTypePacket, + coretypes.ErrorAttributeKeyPrefix+icatypes.AttributeKeyAckError, + ) + + s.Require().True(isFound) + s.Require().Equal(errorMessage, hosttypes.ErrHostSubModuleDisabled.Error()) + }) + }) +} diff --git a/e2e/tests/interchain_accounts/query_test.go b/e2e/tests/interchain_accounts/query_test.go new file mode 100644 index 00000000000..0c9c6417d9d --- /dev/null +++ b/e2e/tests/interchain_accounts/query_test.go @@ -0,0 +1,164 @@ +//go:build !test_e2e + +package interchainaccounts + +import ( + "context" + "encoding/hex" + "encoding/json" + "testing" + "time" + + "github.com/cosmos/gogoproto/proto" + "github.com/strangelove-ventures/interchaintest/v8/testutil" + testifysuite "github.com/stretchr/testify/suite" + + sdk "github.com/cosmos/cosmos-sdk/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + + "github.com/cosmos/ibc-go/e2e/testsuite" + "github.com/cosmos/ibc-go/e2e/testsuite/query" + "github.com/cosmos/ibc-go/e2e/testvalues" + controllertypes "github.com/cosmos/ibc-go/v9/modules/apps/27-interchain-accounts/controller/types" + icahosttypes "github.com/cosmos/ibc-go/v9/modules/apps/27-interchain-accounts/host/types" + icatypes "github.com/cosmos/ibc-go/v9/modules/apps/27-interchain-accounts/types" + channeltypes "github.com/cosmos/ibc-go/v9/modules/core/04-channel/types" + ibctesting "github.com/cosmos/ibc-go/v9/testing" +) + +func TestInterchainAccountsQueryTestSuite(t *testing.T) { + testifysuite.Run(t, new(InterchainAccountsQueryTestSuite)) +} + +type InterchainAccountsQueryTestSuite struct { + testsuite.E2ETestSuite +} + +func (s *InterchainAccountsQueryTestSuite) TestInterchainAccountsQuery() { + t := s.T() + ctx := context.TODO() + + testName := t.Name() + relayer := s.CreateDefaultPaths(testName) + + chainA, chainB := s.GetChains() + chainBVersion := chainB.Config().Images[0].Version + + // 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) { + // explicitly set the version string because we don't want to use incentivized channels. + version := icatypes.NewDefaultMetadataString(ibctesting.FirstConnectionID, ibctesting.FirstConnectionID) + msgRegisterAccount := controllertypes.NewMsgRegisterInterchainAccount(ibctesting.FirstConnectionID, controllerAddress, version, channeltypes.UNORDERED) + + txResp := s.BroadcastMessages(ctx, chainA, controllerAccount, msgRegisterAccount) + s.AssertTxSuccess(txResp) + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer, testName) + }) + + t.Run("verify interchain account", func(t *testing.T) { + var err error + hostAccount, err = query.InterchainAccount(ctx, chainA, controllerAddress, ibctesting.FirstConnectionID) + s.Require().NoError(err) + s.Require().NotEmpty(hostAccount) + + channels, err := relayer.GetChannels(ctx, s.GetRelayerExecReporter(), chainA.Config().ChainID) + s.Require().NoError(err) + s.Require().Len(channels, 2) + }) + + t.Run("query via interchain account", func(t *testing.T) { + // the host account need not be funded + t.Run("broadcast query packet", func(t *testing.T) { + balanceQuery := banktypes.NewQueryBalanceRequest(chainBAccount.Address(), chainB.Config().Denom) + queryBz, err := balanceQuery.Marshal() + s.Require().NoError(err) + + queryMsg := icahosttypes.NewMsgModuleQuerySafe(hostAccount, []icahosttypes.QueryRequest{ + { + Path: "/cosmos.bank.v1beta1.Query/Balance", + Data: queryBz, + }, + }) + + cdc := testsuite.Codec() + bz, err := icatypes.SerializeCosmosTx(cdc, []proto.Message{queryMsg}, icatypes.EncodingProtobuf) + s.Require().NoError(err) + + packetData := icatypes.InterchainAccountPacketData{ + Type: icatypes.EXECUTE_TX, + Data: bz, + Memo: "e2e", + } + + icaQueryMsg := controllertypes.NewMsgSendTx(controllerAddress, ibctesting.FirstConnectionID, uint64(time.Hour.Nanoseconds()), packetData) + + txResp := s.BroadcastMessages(ctx, chainA, controllerAccount, icaQueryMsg) + s.AssertTxSuccess(txResp) + + s.Require().NoError(testutil.WaitForBlocks(ctx, 10, chainA, chainB)) + }) + + t.Run("verify query response", func(t *testing.T) { + var expQueryHeight uint64 + + ack := &channeltypes.Acknowledgement_Result{} + t.Run("retrieve acknowledgement", func(t *testing.T) { + cmd := "message.action=/ibc.core.channel.v1.MsgRecvPacket" + if testvalues.TransactionEventQueryFeatureReleases.IsSupported(chainBVersion) { + cmd = "message.action='/ibc.core.channel.v1.MsgRecvPacket'" + } + txSearchRes, err := s.QueryTxsByEvents(ctx, chainB, 1, 1, cmd, "") + s.Require().NoError(err) + s.Require().Len(txSearchRes.Txs, 1) + + expQueryHeight = uint64(txSearchRes.Txs[0].Height) + + ackHexValue, isFound := s.ExtractValueFromEvents( + txSearchRes.Txs[0].Events, + channeltypes.EventTypeWriteAck, + channeltypes.AttributeKeyAckHex, + ) + s.Require().True(isFound) + s.Require().NotEmpty(ackHexValue) + + ackBz, err := hex.DecodeString(ackHexValue) + s.Require().NoError(err) + + err = json.Unmarshal(ackBz, ack) + s.Require().NoError(err) + }) + + icaAck := &sdk.TxMsgData{} + t.Run("unmarshal ica response", func(t *testing.T) { + err := proto.Unmarshal(ack.Result, icaAck) + s.Require().NoError(err) + s.Require().Len(icaAck.GetMsgResponses(), 1) + }) + + queryTxResp := &icahosttypes.MsgModuleQuerySafeResponse{} + t.Run("unmarshal MsgModuleQuerySafeResponse", func(t *testing.T) { + err := proto.Unmarshal(icaAck.MsgResponses[0].Value, queryTxResp) + s.Require().NoError(err) + s.Require().Len(queryTxResp.Responses, 1) + s.Require().Equal(expQueryHeight, queryTxResp.Height) + }) + + balanceResp := &banktypes.QueryBalanceResponse{} + t.Run("unmarshal and verify bank query response", func(t *testing.T) { + err := proto.Unmarshal(queryTxResp.Responses[0], balanceResp) + s.Require().NoError(err) + s.Require().Equal(chainB.Config().Denom, balanceResp.Balance.Denom) + s.Require().Equal(testvalues.StartingTokenAmount, balanceResp.Balance.Amount.Int64()) + }) + }) + }) +} diff --git a/e2e/tests/interchain_accounts/upgrades_test.go b/e2e/tests/interchain_accounts/upgrades_test.go new file mode 100644 index 00000000000..96cdd27f6fa --- /dev/null +++ b/e2e/tests/interchain_accounts/upgrades_test.go @@ -0,0 +1,382 @@ +//go:build !test_e2e + +package interchainaccounts + +import ( + "context" + "testing" + "time" + + "github.com/cosmos/gogoproto/proto" + "github.com/strangelove-ventures/interchaintest/v8" + "github.com/strangelove-ventures/interchaintest/v8/ibc" + test "github.com/strangelove-ventures/interchaintest/v8/testutil" + testifysuite "github.com/stretchr/testify/suite" + + sdkmath "cosmossdk.io/math" + + 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/ibc-go/e2e/testsuite" + "github.com/cosmos/ibc-go/e2e/testsuite/query" + "github.com/cosmos/ibc-go/e2e/testvalues" + controllertypes "github.com/cosmos/ibc-go/v9/modules/apps/27-interchain-accounts/controller/types" + icatypes "github.com/cosmos/ibc-go/v9/modules/apps/27-interchain-accounts/types" + feetypes "github.com/cosmos/ibc-go/v9/modules/apps/29-fee/types" + channeltypes "github.com/cosmos/ibc-go/v9/modules/core/04-channel/types" + ibctesting "github.com/cosmos/ibc-go/v9/testing" +) + +func TestInterchainAccountsChannelUpgradesTestSuite(t *testing.T) { + testifysuite.Run(t, new(InterchainAccountsChannelUpgradesTestSuite)) +} + +type InterchainAccountsChannelUpgradesTestSuite struct { + testsuite.E2ETestSuite +} + +// TestMsgSendTx_SuccessfulTransfer_AfterUpgradingOrdertoUnordered tests upgrading an ICA channel to +// unordered and sends a message to the host afterwards. +func (s *InterchainAccountsChannelUpgradesTestSuite) TestMsgSendTx_SuccessfulTransfer_AfterUpgradingOrdertoUnordered() { + t := s.T() + ctx := context.TODO() + + testName := t.Name() + relayer := s.CreateDefaultPaths(testName) + + 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" + ) + + t.Run("register interchain account", func(t *testing.T) { + var err error + // explicitly set the version string because we don't want to use incentivized channels. + version := icatypes.NewDefaultMetadataString(ibctesting.FirstConnectionID, ibctesting.FirstConnectionID) + msgRegisterInterchainAccount := controllertypes.NewMsgRegisterInterchainAccount(ibctesting.FirstConnectionID, controllerAddress, version, channeltypes.ORDERED) + + txResp := s.BroadcastMessages(ctx, chainA, controllerAccount, msgRegisterInterchainAccount) + s.AssertTxSuccess(txResp) + portID, err = icatypes.NewControllerPortID(controllerAddress) + s.Require().NoError(err) + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer, testName) + }) + + t.Run("verify interchain account", func(t *testing.T) { + var err error + hostAccount, err = query.InterchainAccount(ctx, chainA, controllerAddress, ibctesting.FirstConnectionID) + s.Require().NoError(err) + s.Require().NotEmpty(hostAccount) + + _, err = query.Channel(ctx, chainA, portID, initialChannelID) + s.Require().NoError(err) + }) + + 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: sdkmath.NewInt(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}, icatypes.EncodingProtobuf) + 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 := s.BroadcastMessages( + ctx, + chainA, + controllerAccount, + msgSendTx, + ) + + s.AssertTxSuccess(resp) + s.AssertPacketRelayed(ctx, chainA, portID, initialChannelID, 1) + }) + + t.Run("verify tokens transferred", func(t *testing.T) { + balance, err := query.Balance(ctx, chainB, chainBAccount.FormattedAddress(), chainB.Config().Denom) + s.Require().NoError(err) + + expected := testvalues.IBCTransferAmount + testvalues.StartingTokenAmount + s.Require().Equal(expected, balance.Int64()) + }) + + channel, err := query.Channel(ctx, chainA, portID, initialChannelID) + s.Require().NoError(err) + + // upgrade the channel ordering to UNORDERED + upgradeFields := channeltypes.NewUpgradeFields(channeltypes.UNORDERED, channel.ConnectionHops, channel.Version) + + t.Run("execute gov proposal to initiate channel upgrade", func(t *testing.T) { + govModuleAddress, err := query.ModuleAccountAddress(ctx, govtypes.ModuleName, chainA) + s.Require().NoError(err) + s.Require().NotNil(govModuleAddress) + + msg := channeltypes.NewMsgChannelUpgradeInit(portID, initialChannelID, upgradeFields, govModuleAddress.String()) + s.ExecuteAndPassGovV1Proposal(ctx, msg, chainA, controllerAccount) + }) + + t.Run("verify channel A upgraded and is now unordered", func(t *testing.T) { + var channel channeltypes.Channel + waitErr := test.WaitForCondition(time.Minute*2, time.Second*5, func() (bool, error) { + channel, err = query.Channel(ctx, chainA, portID, initialChannelID) + if err != nil { + return false, err + } + return channel.Ordering == channeltypes.UNORDERED, nil + }) + s.Require().NoErrorf(waitErr, "channel was not upgraded: expected %s got %s", channeltypes.UNORDERED, channel.Ordering) + }) + + t.Run("verify channel B upgraded and is now unordered", func(t *testing.T) { + var channel channeltypes.Channel + waitErr := test.WaitForCondition(time.Minute*2, time.Second*5, func() (bool, error) { + channel, err = query.Channel(ctx, chainB, icatypes.HostPortID, initialChannelID) + if err != nil { + return false, err + } + return channel.Ordering == channeltypes.UNORDERED, nil + }) + s.Require().NoErrorf(waitErr, "channel was not upgraded: expected %s got %s", channeltypes.UNORDERED, channel.Ordering) + }) + + 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}, icatypes.EncodingProtobuf) + 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 := s.BroadcastMessages( + ctx, + chainA, + controllerAccount, + msgSendTx, + ) + + s.AssertTxSuccess(resp) + s.AssertPacketRelayed(ctx, chainA, portID, initialChannelID, 2) + }) + + t.Run("verify tokens transferred", func(t *testing.T) { + balance, err := query.Balance(ctx, chainB, chainBAccount.FormattedAddress(), chainB.Config().Denom) + s.Require().NoError(err) + + expected := 2*testvalues.IBCTransferAmount + testvalues.StartingTokenAmount + s.Require().Equal(expected, balance.Int64()) + }) +} + +// TestChannelUpgrade_ICAChannelClosesAfterTimeout_Succeeds tests upgrading an ICA channel to +// wire up fee middleware and then forces it to close it by timing out a packet. +func (s *InterchainAccountsChannelUpgradesTestSuite) TestChannelUpgrade_ICAChannelClosesAfterTimeout_Succeeds() { + t := s.T() + ctx := context.TODO() + + testName := t.Name() + + relayer := s.CreateDefaultPaths(testName) + + 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) + + chainBDenom := chainB.Config().Denom + chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + + s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB), "failed to wait for blocks") + + var ( + channelID = "channel-1" + controllerPortID string + hostPortID = icatypes.HostPortID + interchainAccount string + channelA channeltypes.Channel + ) + + t.Run("register interchain account", func(t *testing.T) { + var err error + // explicitly set the version string because we don't want to use incentivized channels. + version := icatypes.NewDefaultMetadataString(ibctesting.FirstConnectionID, ibctesting.FirstConnectionID) + msgRegisterInterchainAccount := controllertypes.NewMsgRegisterInterchainAccount(ibctesting.FirstConnectionID, controllerAddress, version, channeltypes.ORDERED) + txResp := s.BroadcastMessages(ctx, chainA, controllerAccount, msgRegisterInterchainAccount) + s.AssertTxSuccess(txResp) + + controllerPortID, err = icatypes.NewControllerPortID(controllerAddress) + s.Require().NoError(err) + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer, testName) + }) + + t.Run("verify interchain account", func(t *testing.T) { + var err error + interchainAccount, err = query.InterchainAccount(ctx, chainA, controllerAddress, ibctesting.FirstConnectionID) + s.Require().NoError(err) + s.Require().NotZero(len(interchainAccount)) + + channelA, err = query.Channel(ctx, chainA, controllerPortID, channelID) + s.Require().NoError(err) + }) + + t.Run("execute gov proposal to initiate channel upgrade", func(t *testing.T) { + s.InitiateChannelUpgrade(ctx, chainA, chainAWallet, controllerPortID, channelID, s.CreateUpgradeFields(channelA)) + }) + + s.Require().NoError(test.WaitForBlocks(ctx, 10, chainA, chainB), "failed to wait for blocks") + + t.Run("verify channel A upgraded and is fee enabled", func(t *testing.T) { + channel, err := query.Channel(ctx, chainA, controllerPortID, channelID) + s.Require().NoError(err) + + // check the channel version include the fee version + version, err := feetypes.MetadataFromVersion(channel.Version) + s.Require().NoError(err) + s.Require().Equal(feetypes.Version, version.FeeVersion, "the channel version did not include ics29") + + // extra check + feeEnabled, err := query.FeeEnabledChannel(ctx, chainA, controllerPortID, channelID) + s.Require().NoError(err) + s.Require().Equal(true, feeEnabled) + }) + + t.Run("verify channel B upgraded and is fee enabled", func(t *testing.T) { + channel, err := query.Channel(ctx, chainB, hostPortID, channelID) + s.Require().NoError(err) + + // check the channel version include the fee version + version, err := feetypes.MetadataFromVersion(channel.Version) + s.Require().NoError(err) + s.Require().Equal(feetypes.Version, version.FeeVersion, "the channel version did not include ics29") + + // extra check + feeEnabled, err := query.FeeEnabledChannel(ctx, chainB, hostPortID, channelID) + s.Require().NoError(err) + s.Require().Equal(true, feeEnabled) + }) + + // 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 that times out", 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: interchainAccount, + Amount: sdkmath.NewInt(testvalues.StartingTokenAmount), + Denom: chainBDenom, + }) + 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: interchainAccount, + ToAddress: chainBAccount.FormattedAddress(), + Amount: sdk.NewCoins(testvalues.DefaultTransferAmount(chainBDenom)), + } + + cdc := testsuite.Codec() + + bz, err := icatypes.SerializeCosmosTx(cdc, []proto.Message{msgSend}, icatypes.EncodingProtobuf) + s.Require().NoError(err) + + packetData := icatypes.InterchainAccountPacketData{ + Type: icatypes.EXECUTE_TX, + Data: bz, + Memo: "e2e", + } + + timeout := uint64(1) + msgSendTx := controllertypes.NewMsgSendTx(controllerAddress, ibctesting.FirstConnectionID, timeout, packetData) + + resp := s.BroadcastMessages( + ctx, + chainA, + controllerAccount, + msgSendTx, + ) + + s.AssertTxSuccess(resp) + + // 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, testName) + }) + + t.Run("verify channel A is closed due to timeout on ordered channel", func(t *testing.T) { + channel, err := query.Channel(ctx, chainA, controllerPortID, channelID) + s.Require().NoError(err) + + s.Require().Equal(channeltypes.CLOSED, channel.State, "the channel was not in an expected state") + }) + + t.Run("verify channel B is closed due to timeout on ordered channel", func(t *testing.T) { + channel, err := query.Channel(ctx, chainB, hostPortID, channelID) + s.Require().NoError(err) + + s.Require().Equal(channeltypes.CLOSED, channel.State, "the channel was not in an expected state") + }) +} diff --git a/e2e/tests/transfer/authz_test.go b/e2e/tests/transfer/authz_test.go new file mode 100644 index 00000000000..5014bc891ab --- /dev/null +++ b/e2e/tests/transfer/authz_test.go @@ -0,0 +1,377 @@ +//go:build !test_e2e + +package transfer + +import ( + "context" + "testing" + + "github.com/strangelove-ventures/interchaintest/v8/ibc" + test "github.com/strangelove-ventures/interchaintest/v8/testutil" + testifysuite "github.com/stretchr/testify/suite" + + sdkmath "cosmossdk.io/math" + + 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" + + "github.com/cosmos/ibc-go/e2e/testsuite" + "github.com/cosmos/ibc-go/e2e/testsuite/query" + "github.com/cosmos/ibc-go/e2e/testvalues" + transfertypes "github.com/cosmos/ibc-go/v9/modules/apps/transfer/types" + ibcerrors "github.com/cosmos/ibc-go/v9/modules/core/errors" +) + +func TestAuthzTransferTestSuite(t *testing.T) { + testifysuite.Run(t, new(AuthzTransferTestSuite)) +} + +type AuthzTransferTestSuite struct { + testsuite.E2ETestSuite +} + +func (suite *AuthzTransferTestSuite) CreateAuthzTestPath(testName string) (ibc.Relayer, ibc.ChannelOutput) { + return suite.CreatePaths(ibc.DefaultClientOpts(), suite.TransferChannelOptions(), testName), suite.GetChainAChannelForTest(testName) +} + +// QueryGranterGrants returns all GrantAuthorizations for the given granterAddress. +func (*AuthzTransferTestSuite) QueryGranterGrants(ctx context.Context, chain ibc.Chain, granterAddress string) ([]*authz.GrantAuthorization, error) { + res, err := query.GRPCQuery[authz.QueryGranterGrantsResponse](ctx, chain, &authz.QueryGranterGrantsRequest{ + Granter: granterAddress, + }) + if err != nil { + return nil, err + } + + return res.Grants, nil +} + +func (suite *AuthzTransferTestSuite) TestAuthz_MsgTransfer_Succeeds() { + t := suite.T() + ctx := context.TODO() + + testName := t.Name() + t.Parallel() + relayer, channelA := suite.CreateAuthzTestPath(testName) + + 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, testName) + }) + + // createMsgGrantFn initializes a TransferAuthorization and broadcasts a MsgGrant message. + createMsgGrantFn := func(t *testing.T) { + t.Helper() + transferAuth := transfertypes.TransferAuthorization{ + Allocations: []transfertypes.Allocation{ + { + SourcePort: channelA.PortID, + SourceChannel: channelA.ChannelID, + SpendLimit: sdk.NewCoins(sdk.NewCoin(chainADenom, sdkmath.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 := suite.BroadcastMessages(context.TODO(), chainA, granterWallet, msgGrant) + suite.AssertTxSuccess(resp) + } + + // 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) { + t.Helper() + return func(t *testing.T) { + t.Helper() + 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, sdkmath.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 := testsuite.GetMsgTransfer( + channelA.PortID, + channelA.ChannelID, + channelA.Version, + testvalues.DefaultTransferCoins(chainADenom), + granterAddress, + receiverWalletAddress, + suite.GetTimeoutHeight(ctx, chainB), + 0, + "", + nil, + ) + + protoAny, err := codectypes.NewAnyWithValue(transferMsg) + suite.Require().NoError(err) + + msgExec := &authz.MsgExec{ + Grantee: granteeAddress, + Msgs: []*codectypes.Any{protoAny}, + } + + resp := suite.BroadcastMessages(context.TODO(), chainA, granteeWallet, msgExec) + suite.AssertTxSuccess(resp) + }) + + 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 := query.Balance(ctx, chainB, receiverWalletAddress, chainBIBCToken.IBCDenom()) + + suite.Require().NoError(err) + suite.Require().Equal(testvalues.IBCTransferAmount, actualBalance.Int64()) + }) + + 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 := suite.BroadcastMessages(context.TODO(), chainA, granterWallet, &msgRevoke) + suite.AssertTxSuccess(resp) + }) + + t.Run("exec unauthorized MsgTransfer", func(t *testing.T) { + transferMsg := testsuite.GetMsgTransfer( + channelA.PortID, + channelA.ChannelID, + channelA.Version, + testvalues.DefaultTransferCoins(chainADenom), + granterAddress, + receiverWalletAddress, + suite.GetTimeoutHeight(ctx, chainB), + 0, + "", + nil, + ) + + protoAny, err := codectypes.NewAnyWithValue(transferMsg) + suite.Require().NoError(err) + + msgExec := &authz.MsgExec{ + Grantee: granteeAddress, + Msgs: []*codectypes.Any{protoAny}, + } + + resp := suite.BroadcastMessages(context.TODO(), chainA, granteeWallet, msgExec) + suite.AssertTxFailure(resp, authz.ErrNoAuthorizationFound) + }) +} + +func (suite *AuthzTransferTestSuite) TestAuthz_InvalidTransferAuthorizations() { + t := suite.T() + ctx := context.TODO() + + testName := t.Name() + t.Parallel() + relayer, channelA := suite.CreateAuthzTestPath(testName) + + chainA, chainB := suite.GetChains() + chainADenom := chainA.Config().Denom + chainAVersion := chainA.Config().Images[0].Version + + 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, testName) + }) + + 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, sdkmath.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 := suite.BroadcastMessages(context.TODO(), chainA, granterWallet, msgGrant) + suite.AssertTxSuccess(resp) + }) + + 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 := testsuite.GetMsgTransfer( + channelA.PortID, + channelA.ChannelID, + channelA.Version, + sdk.NewCoins(sdk.Coin{Denom: chainADenom, Amount: sdkmath.NewInt(invalidSpendAmount)}), + granterAddress, + receiverWalletAddress, + suite.GetTimeoutHeight(ctx, chainB), + 0, + "", + nil, + ) + + protoAny, err := codectypes.NewAnyWithValue(transferMsg) + suite.Require().NoError(err) + + msgExec := &authz.MsgExec{ + Grantee: granteeAddress, + Msgs: []*codectypes.Any{protoAny}, + } + + resp := suite.BroadcastMessages(context.TODO(), chainA, granteeWallet, msgExec) + if testvalues.IbcErrorsFeatureReleases.IsSupported(chainAVersion) { + suite.AssertTxFailure(resp, ibcerrors.ErrInsufficientFunds) + } else { + suite.AssertTxFailure(resp, sdkerrors.ErrInsufficientFunds) + } + }) + + 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 := query.Balance(ctx, chainB, receiverWalletAddress, chainBIBCToken.IBCDenom()) + + suite.Require().NoError(err) + suite.Require().Equal(int64(0), actualBalance.Int64()) + }) + + 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, sdkmath.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 := testsuite.GetMsgTransfer( + channelA.PortID, + channelA.ChannelID, + channelA.Version, + sdk.NewCoins(sdk.Coin{Denom: chainADenom, Amount: sdkmath.NewInt(spendLimit)}), + granterAddress, + invalidWalletAddress, + suite.GetTimeoutHeight(ctx, chainB), + 0, + "", + nil, + ) + + protoAny, err := codectypes.NewAnyWithValue(transferMsg) + suite.Require().NoError(err) + + msgExec := &authz.MsgExec{ + Grantee: granteeAddress, + Msgs: []*codectypes.Any{protoAny}, + } + + resp := suite.BroadcastMessages(context.TODO(), chainA, granteeWallet, msgExec) + if testvalues.IbcErrorsFeatureReleases.IsSupported(chainAVersion) { + suite.AssertTxFailure(resp, ibcerrors.ErrInvalidAddress) + } else { + suite.AssertTxFailure(resp, sdkerrors.ErrInvalidAddress) + } + }) + }) +} + +// extractTransferAuthorizationFromGrantAuthorization extracts a TransferAuthorization from the given +// GrantAuthorization. +func (suite *AuthzTransferTestSuite) extractTransferAuthorizationFromGrantAuthorization(grantAuth *authz.GrantAuthorization) *transfertypes.TransferAuthorization { + cfg := testsuite.SDKEncodingConfig() + 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..745a0d57f07 --- /dev/null +++ b/e2e/tests/transfer/base_test.go @@ -0,0 +1,644 @@ +//go:build !test_e2e + +package transfer + +import ( + "context" + "testing" + "time" + + "github.com/strangelove-ventures/interchaintest/v8/ibc" + test "github.com/strangelove-ventures/interchaintest/v8/testutil" + testifysuite "github.com/stretchr/testify/suite" + + sdkmath "cosmossdk.io/math" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/cosmos/ibc-go/e2e/testsuite" + "github.com/cosmos/ibc-go/e2e/testsuite/query" + "github.com/cosmos/ibc-go/e2e/testvalues" + transfertypes "github.com/cosmos/ibc-go/v9/modules/apps/transfer/types" +) + +func TestTransferTestSuite(t *testing.T) { + testifysuite.Run(t, new(TransferTestSuite)) +} + +type TransferTestSuite struct { + transferTester +} + +// transferTester defines some helper functions that can be used in various test suites +// that test transfer functionality. +type transferTester struct { + testsuite.E2ETestSuite +} + +// QueryTransferParams queries the on-chain send enabled param for the transfer module +func (s *transferTester) QueryTransferParams(ctx context.Context, chain ibc.Chain) transfertypes.Params { + res, err := query.GRPCQuery[transfertypes.QueryParamsResponse](ctx, chain, &transfertypes.QueryParamsRequest{}) + s.Require().NoError(err) + return *res.Params +} + +// CreateTransferPath sets up a path between chainA and chainB with a transfer channel and returns the relayer wired +// up to watch the channel and port IDs created. +func (s *transferTester) CreateTransferPath(testName string) (ibc.Relayer, ibc.ChannelOutput) { + relayer, channel := s.CreatePaths(ibc.DefaultClientOpts(), s.TransferChannelOptions(), testName), s.GetChainAChannelForTest(testName) + s.T().Logf("test %s running on portID %s channelID %s", testName, channel.PortID, channel.ChannelID) + return relayer, channel +} + +// 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() + + testName := t.Name() + + // NOTE: t.Parallel() should be called before SetupPath in all tests. + // t.Name() must be stored in a variable before t.Parallel() otherwise t.Name() is not + // deterministic. + t.Parallel() + + relayer, channelA := s.CreateTransferPath(testName) + + chainA, chainB := s.GetChains() + + chainBVersion := chainB.Config().Images[0].Version + 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") + // TODO: https://github.com/cosmos/ibc-go/issues/6743 + // t.Run("ensure capability module BeginBlock is executed", func(t *testing.T) { + // // by restarting the chain we ensure that the capability module's BeginBlocker is executed. + // s.Require().NoError(chainA.(*cosmos.CosmosChain).StopAllNodes(ctx)) + // s.Require().NoError(chainA.(*cosmos.CosmosChain).StartAllNodes(ctx)) + // s.Require().NoError(test.WaitForBlocks(ctx, 5, chainA), "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 := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, testvalues.DefaultTransferCoins(chainADenom), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "", nil) + s.AssertTxSuccess(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) + + // TODO: cannot query total escrow if tests in parallel are using the same denom. + // if testvalues.TotalEscrowFeatureReleases.IsSupported(chainAVersion) { + // actualTotalEscrow, err := query.TotalEscrowForDenom(ctx, chainA, chainADenom) + // s.Require().NoError(err) + // + // expectedTotalEscrow := sdk.NewCoin(chainADenom, sdkmath.NewInt(testvalues.IBCTransferAmount)) + // s.Require().Equal(expectedTotalEscrow, actualTotalEscrow) + // } + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer, testName) + }) + + 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 := query.Balance(ctx, chainB, chainBAddress, chainBIBCToken.IBCDenom()) + s.Require().NoError(err) + + expected := testvalues.IBCTransferAmount + s.Require().Equal(expected, actualBalance.Int64()) + }) + + if testvalues.TokenMetadataFeatureReleases.IsSupported(chainBVersion) { + t.Run("metadata for IBC denomination exists on chainB", func(t *testing.T) { + s.AssertHumanReadableDenom(ctx, chainB, chainADenom, channelA) + }) + } + + t.Run("non-native IBC token transfer from chainB to chainA, receiver is source of tokens", func(t *testing.T) { + transferTxResp := s.Transfer(ctx, chainB, chainBWallet, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, testvalues.DefaultTransferCoins(chainBIBCToken.IBCDenom()), chainBAddress, chainAAddress, s.GetTimeoutHeight(ctx, chainA), 0, "", nil) + s.AssertTxSuccess(transferTxResp) + }) + + t.Run("tokens are escrowed", func(t *testing.T) { + actualBalance, err := query.Balance(ctx, chainB, chainBAddress, chainBIBCToken.IBCDenom()) + s.Require().NoError(err) + + s.Require().Equal(sdkmath.ZeroInt(), actualBalance) + + // https://github.com/cosmos/ibc-go/issues/6742 + // if testvalues.TotalEscrowFeatureReleases.IsSupported(chainBVersion) { + // actualTotalEscrow, err := query.TotalEscrowForDenom(ctx, chainB, chainBIBCToken.IBCDenom()) + // s.Require().NoError(err) + // s.Require().Equal(sdk.NewCoin(chainBIBCToken.IBCDenom(), sdkmath.NewInt(0)), actualTotalEscrow) // total escrow is zero because sending chain is not source for tokens + // } + }) + + 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) + }) + + // https://github.com/cosmos/ibc-go/issues/6742 + // if testvalues.TotalEscrowFeatureReleases.IsSupported(chainAVersion) { + // t.Run("tokens are un-escrowed", func(t *testing.T) { + // actualTotalEscrow, err := query.TotalEscrowForDenom(ctx, chainA, chainADenom) + // s.Require().NoError(err) + // s.Require().Equal(sdk.NewCoin(chainADenom, sdkmath.NewInt(0)), actualTotalEscrow) // total escrow is zero because tokens have come back + // }) + // } +} + +// TestMsgTransfer_Succeeds_MultiDenom will test sending successful IBC transfers from chainA to chainB. +// A multidenom transfer with native chainB tokens and IBC tokens from chainA is executed from chainB to chainA. +func (s *TransferTestSuite) TestMsgTransfer_Succeeds_Nonincentivized_MultiDenom() { + t := s.T() + ctx := context.TODO() + + testName := t.Name() + t.Parallel() + relayer, channelA := s.CreateTransferPath(testName) + + chainA, chainB := s.GetChains() + + chainADenom := chainA.Config().Denom + chainBDenom := chainB.Config().Denom + + chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + chainAAddress := chainAWallet.FormattedAddress() + + chainBWallet := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount) + chainBAddress := chainBWallet.FormattedAddress() + + chainBIBCToken := testsuite.GetIBCToken(chainADenom, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID) + chainAIBCToken := testsuite.GetIBCToken(chainBDenom, channelA.PortID, channelA.ChannelID) + + t.Run("native IBC token transfer from chainA to chainB, sender is source of tokens", func(t *testing.T) { + transferTxResp := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, sdk.NewCoins(testvalues.DefaultTransferAmount(chainADenom)), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "", nil) + s.AssertTxSuccess(transferTxResp) + }) + + s.Require().NoError(test.WaitForBlocks(ctx, 5, chainA, chainB), "failed to wait for blocks") + + t.Run("native chainA 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) + + // https://github.com/cosmos/ibc-go/issues/6742 + // actualTotalEscrow, err := query.TotalEscrowForDenom(ctx, chainA, chainADenom) + // s.Require().NoError(err) + // + // expectedTotalEscrow := sdk.NewCoin(chainADenom, sdkmath.NewInt(testvalues.IBCTransferAmount)) + // s.Require().Equal(expectedTotalEscrow, actualTotalEscrow) + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer, testName) + }) + + t.Run("packets are relayed", func(t *testing.T) { + s.AssertPacketRelayed(ctx, chainA, channelA.PortID, channelA.ChannelID, 1) + + actualBalance, err := query.Balance(ctx, chainB, chainBAddress, chainBIBCToken.IBCDenom()) + s.Require().NoError(err) + + expected := testvalues.IBCTransferAmount + s.Require().Equal(expected, actualBalance.Int64()) + }) + + t.Run("metadata for IBC denomination exists on chainB", func(t *testing.T) { + s.AssertHumanReadableDenom(ctx, chainB, chainADenom, channelA) + }) + + // send the native chainB denom and also the ibc token from chainA + transferCoins := []sdk.Coin{ + testvalues.DefaultTransferAmount(chainBIBCToken.IBCDenom()), + testvalues.DefaultTransferAmount(chainBDenom), + } + + t.Run("native token from chain B and non-native IBC token from chainA, both to chainA", func(t *testing.T) { + transferTxResp := s.Transfer(ctx, chainB, chainBWallet, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, transferCoins, chainBAddress, chainAAddress, s.GetTimeoutHeight(ctx, chainA), 0, "", nil) + s.AssertTxSuccess(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, chainB, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, 1) + + t.Run("chain A native denom", func(t *testing.T) { + actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet) + s.Require().NoError(err) + + expected := testvalues.StartingTokenAmount + s.Require().Equal(expected, actualBalance) + }) + + t.Run("chain B IBC denom", func(t *testing.T) { + actualBalance, err := query.Balance(ctx, chainA, chainAAddress, chainAIBCToken.IBCDenom()) + s.Require().NoError(err) + + expected := testvalues.IBCTransferAmount + s.Require().Equal(expected, actualBalance.Int64()) + }) + }) + + // https://github.com/cosmos/ibc-go/issues/6742 + // t.Run("native chainA tokens are un-escrowed", func(t *testing.T) { + // actualTotalEscrow, err := query.TotalEscrowForDenom(ctx, chainA, chainADenom) + // s.Require().NoError(err) + // s.Require().Equal(sdk.NewCoin(chainADenom, sdkmath.NewInt(0)), actualTotalEscrow) // total escrow is zero because tokens have come back + // }) +} + +// TestMsgTransfer_Fails_InvalidAddress_MultiDenom attempts to send a multidenom IBC transfer +// to an invalid address and ensures that the tokens on the sending chain are returned to the sender. +func (s *TransferTestSuite) TestMsgTransfer_Fails_InvalidAddress_MultiDenom() { + t := s.T() + ctx := context.TODO() + + testName := t.Name() + t.Parallel() + relayer, channelA := s.CreateTransferPath(testName) + + chainA, chainB := s.GetChains() + + chainADenom := chainA.Config().Denom + chainBDenom := chainB.Config().Denom + + chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + chainAAddress := chainAWallet.FormattedAddress() + + chainBWallet := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount) + chainBAddress := chainBWallet.FormattedAddress() + + chainBIBCToken := testsuite.GetIBCToken(chainADenom, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID) + + t.Run("native IBC token transfer from chainA to chainB, sender is source of tokens", func(t *testing.T) { + transferTxResp := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, sdk.NewCoins(testvalues.DefaultTransferAmount(chainADenom)), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "", nil) + s.AssertTxSuccess(transferTxResp) + }) + + s.Require().NoError(test.WaitForBlocks(ctx, 5, chainA, chainB), "failed to wait for blocks") + + t.Run("native chainA 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) + + // https://github.com/cosmos/ibc-go/issues/6742 + // actualTotalEscrow, err := query.TotalEscrowForDenom(ctx, chainA, chainADenom) + // s.Require().NoError(err) + // + // expectedTotalEscrow := sdk.NewCoin(chainADenom, sdkmath.NewInt(testvalues.IBCTransferAmount)) + // s.Require().Equal(expectedTotalEscrow, actualTotalEscrow) + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer, testName) + }) + + t.Run("packets are relayed", func(t *testing.T) { + s.AssertPacketRelayed(ctx, chainA, channelA.PortID, channelA.ChannelID, 1) + + actualBalance, err := query.Balance(ctx, chainB, chainBAddress, chainBIBCToken.IBCDenom()) + s.Require().NoError(err) + + expected := testvalues.IBCTransferAmount + s.Require().Equal(expected, actualBalance.Int64()) + }) + + t.Run("metadata for IBC denomination exists on chainB", func(t *testing.T) { + s.AssertHumanReadableDenom(ctx, chainB, chainADenom, channelA) + }) + + // send the native chainB denom and also the ibc token from chainA + transferCoins := []sdk.Coin{ + testvalues.DefaultTransferAmount(chainBIBCToken.IBCDenom()), + testvalues.DefaultTransferAmount(chainBDenom), + } + + t.Run("stop relayer", func(t *testing.T) { + s.StopRelayer(ctx, relayer) + }) + + t.Run("native token from chain B and non-native IBC token from chainA, both to chainA", func(t *testing.T) { + transferTxResp := s.Transfer(ctx, chainB, chainBWallet, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, transferCoins, chainBAddress, testvalues.InvalidAddress, s.GetTimeoutHeight(ctx, chainA), 0, "", nil) + s.AssertTxSuccess(transferTxResp) + }) + + s.Require().NoError(test.WaitForBlocks(ctx, 5, chainA, chainB), "failed to wait for blocks") + + t.Run("tokens are sent from chain B", func(t *testing.T) { + t.Run("native chainB 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("non-native chainA IBC denom are burned", func(t *testing.T) { + actualBalance, err := query.Balance(ctx, chainB, chainBAddress, chainBIBCToken.IBCDenom()) + s.Require().NoError(err) + s.Require().Equal(int64(0), actualBalance.Int64()) + }) + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer, testName) + }) + + t.Run("packets are relayed", func(t *testing.T) { + s.AssertPacketRelayed(ctx, chainB, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, 1) + }) + + t.Run("tokens are returned to sender on chainB", func(t *testing.T) { + t.Run("native chainB denom", func(t *testing.T) { + actualBalance, err := s.GetChainBNativeBalance(ctx, chainBWallet) + s.Require().NoError(err) + + expected := testvalues.StartingTokenAmount + s.Require().Equal(expected, actualBalance) + }) + + t.Run("non-native chainA IBC denom", func(t *testing.T) { + actualBalance, err := query.Balance(ctx, chainB, chainBAddress, chainBIBCToken.IBCDenom()) + s.Require().NoError(err) + + expected := testvalues.IBCTransferAmount + s.Require().Equal(expected, actualBalance.Int64()) + }) + }) +} + +// 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() + + testName := t.Name() + t.Parallel() + relayer, channelA := s.CreateTransferPath(testName) + + 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 := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, testvalues.DefaultTransferCoins(chainADenom), chainAAddress, testvalues.InvalidAddress, s.GetTimeoutHeight(ctx, chainB), 0, "", nil) + s.AssertTxSuccess(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, testName) + }) + + 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() + + testName := t.Name() + t.Parallel() + relayer, channelA := s.CreateTransferPath(testName) + + 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: sdkmath.NewInt(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, testName) + }) + + 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) + }) +} + +// 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() + + testName := t.Name() + t.Parallel() + relayer, channelA := s.CreateTransferPath(testName) + + 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("IBC token transfer with memo from chainA to chainB", func(t *testing.T) { + transferTxResp := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, testvalues.DefaultTransferCoins(chainADenom), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "memo", nil) + s.AssertTxSuccess(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, testName) + }) + + 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 := query.Balance(ctx, chainB, chainBAddress, chainBIBCToken.IBCDenom()) + + s.Require().NoError(err) + s.Require().Equal(testvalues.IBCTransferAmount, actualBalance.Int64()) + }) +} + +// TestMsgTransfer_EntireBalance tests that it is possible to transfer the entire balance +// of a given denom by using types.UnboundedSpendLimit as the amount. +func (s *TransferTestSuite) TestMsgTransfer_EntireBalance() { + t := s.T() + ctx := context.TODO() + + testName := t.Name() + t.Parallel() + relayer, channelA := s.CreateTransferPath(testName) + + 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() + + coinFromA := testvalues.DefaultTransferAmount(chainADenom) + + s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB), "failed to wait for blocks") + + t.Run("IBC token transfer from chainA to chainB", func(t *testing.T) { + transferTxResp := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, sdk.NewCoins(coinFromA), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainA), 0, "", nil) + s.AssertTxSuccess(transferTxResp) + }) + + t.Run("tokens are escrowed", func(t *testing.T) { + actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet) + s.Require().NoError(err) + + s.Require().Equal(testvalues.StartingTokenAmount-coinFromA.Amount.Int64(), actualBalance) + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer, testName) + }) + + 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 := query.Balance(ctx, chainB, chainBAddress, chainBIBCToken.IBCDenom()) + + s.Require().NoError(err) + s.Require().Equal(coinFromA.Amount.Int64(), actualBalance.Int64()) + + actualBalance, err = query.Balance(ctx, chainA, chainAAddress, chainADenom) + + s.Require().NoError(err) + s.Require().Equal(testvalues.StartingTokenAmount-coinFromA.Amount.Int64(), actualBalance.Int64()) + }) + + t.Run("send entire balance from B to A", func(t *testing.T) { + transferCoins := sdk.NewCoins((sdk.NewCoin(chainBIBCToken.IBCDenom(), transfertypes.UnboundedSpendLimit())), sdk.NewCoin(chainB.Config().Denom, transfertypes.UnboundedSpendLimit())) + transferTxResp := s.Transfer(ctx, chainB, chainBWallet, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, transferCoins, chainBAddress, chainAAddress, s.GetTimeoutHeight(ctx, chainB), 0, "", nil) + s.AssertTxSuccess(transferTxResp) + }) + + chainAIBCToken := testsuite.GetIBCToken(chainB.Config().Denom, channelA.PortID, channelA.ChannelID) + t.Run("packets relayed", func(t *testing.T) { + // test that chainA has the entire balance back of its native token. + s.AssertPacketRelayed(ctx, chainA, channelA.PortID, channelA.ChannelID, 1) + actualBalance, err := query.Balance(ctx, chainA, chainAAddress, chainADenom) + + s.Require().NoError(err) + s.Require().Equal(testvalues.StartingTokenAmount, actualBalance.Int64()) + + // test that chainA has the entirety of chainB's token IBC denom. + actualBalance, err = query.Balance(ctx, chainA, chainAAddress, chainAIBCToken.IBCDenom()) + + s.Require().NoError(err) + s.Require().Equal(testvalues.StartingTokenAmount, actualBalance.Int64()) + + // Tests that chainB has a zero balance for both. + actualBalance, err = query.Balance(ctx, chainB, chainBAddress, chainBIBCToken.IBCDenom()) + + s.Require().NoError(err) + s.Require().Zero(actualBalance.Int64()) + + actualBalance, err = query.Balance(ctx, chainB, chainBAddress, chainB.Config().Denom) + + s.Require().NoError(err) + s.Require().Zero(actualBalance.Int64()) + }) +} diff --git a/e2e/tests/transfer/forwarding_test.go b/e2e/tests/transfer/forwarding_test.go new file mode 100644 index 00000000000..f7a2c33c15f --- /dev/null +++ b/e2e/tests/transfer/forwarding_test.go @@ -0,0 +1,291 @@ +//go:build !test_e2e + +package transfer + +import ( + "context" + "testing" + "time" + + "github.com/strangelove-ventures/interchaintest/v8/ibc" + test "github.com/strangelove-ventures/interchaintest/v8/testutil" + testifysuite "github.com/stretchr/testify/suite" + + "github.com/cosmos/ibc-go/e2e/testsuite" + "github.com/cosmos/ibc-go/e2e/testsuite/query" + "github.com/cosmos/ibc-go/e2e/testvalues" + transfertypes "github.com/cosmos/ibc-go/v9/modules/apps/transfer/types" + clienttypes "github.com/cosmos/ibc-go/v9/modules/core/02-client/types" + channeltypes "github.com/cosmos/ibc-go/v9/modules/core/04-channel/types" +) + +func TestTransferForwardingTestSuite(t *testing.T) { + testifysuite.Run(t, new(TransferForwardingTestSuite)) +} + +type TransferForwardingTestSuite struct { + testsuite.E2ETestSuite +} + +// SetupSuite explicitly sets up three chains for this test suite. +func (s *TransferForwardingTestSuite) SetupSuite() { + s.SetupChains(context.TODO(), nil, testsuite.ThreeChainSetup()) +} + +// TestForwarding_WithLastChainBeingICS20v1_Succeeds tests the case where a token is forwarded and successfully +// received on a destination chain that is on ics20-v1 version. +func (s *TransferForwardingTestSuite) TestForwarding_WithLastChainBeingICS20v1_Succeeds() { + s.testForwardingThreeChains(transfertypes.V1) +} + +// TestForwarding_Succeeds tests the case where a token is forwarded and successfully +// received on a destination chain. +func (s *TransferForwardingTestSuite) TestForwarding_Succeeds() { + s.testForwardingThreeChains(transfertypes.V2) +} + +func (s *TransferForwardingTestSuite) testForwardingThreeChains(lastChainVersion string) { + ctx := context.TODO() + t := s.T() + + testName := t.Name() + t.Parallel() + relayer := s.CreateDefaultPaths(testName) + + chains := s.GetAllChains() + + chainA, chainB, chainC := chains[0], chains[1], chains[2] + + channelAtoB := s.GetChainAChannelForTest(testName) + + s.Require().Len(s.GetChannelsForTest(chainA, testName), 1, "expected one channel on chain A") + s.Require().Len(s.GetChannelsForTest(chainB, testName), 2, "expected two channels on chain B") + s.Require().Len(s.GetChannelsForTest(chainC, testName), 1, "expected one channel on chain C") + + var channelBtoC ibc.ChannelOutput + if lastChainVersion == transfertypes.V2 { + channelBtoC = s.GetChannelsForTest(chainB, testName)[1] + s.Require().Equal(transfertypes.V2, channelBtoC.Version, "the channel version is not ics20-2") + } else { + opts := s.TransferChannelOptions() + opts.Version = transfertypes.V1 + channelBtoC, _ = s.CreatePath(ctx, relayer, chainB, chainC, ibc.DefaultClientOpts(), opts, testName) + s.Require().Equal(transfertypes.V1, channelBtoC.Version, "the channel version is not ics20-1") + } + + chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + chainAAddress := chainAWallet.FormattedAddress() + chainADenom := chainA.Config().Denom + + chainCWallet := s.CreateUserOnChainC(ctx, testvalues.StartingTokenAmount) + chainCAddress := chainCWallet.FormattedAddress() + + t.Run("IBC transfer from A to C with forwarding through B", func(t *testing.T) { + inFiveMinutes := time.Now().Add(5 * time.Minute).UnixNano() + forwarding := transfertypes.NewForwarding(false, transfertypes.NewHop(channelBtoC.PortID, channelBtoC.ChannelID)) + resp := s.Transfer(ctx, chainA, chainAWallet, channelAtoB.PortID, channelAtoB.ChannelID, testvalues.DefaultTransferCoins(chainADenom), chainAAddress, chainCAddress, clienttypes.ZeroHeight(), uint64(inFiveMinutes), "", forwarding) + s.AssertTxSuccess(resp) + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer, testName) + }) + + t.Run("packets are relayed from A to B to C", func(t *testing.T) { + chainCDenom := transfertypes.NewDenom(chainADenom, + transfertypes.NewHop(channelBtoC.Counterparty.PortID, channelBtoC.Counterparty.ChannelID), + transfertypes.NewHop(channelAtoB.Counterparty.PortID, channelAtoB.Counterparty.ChannelID), + ) + + s.AssertPacketRelayed(ctx, chainA, channelAtoB.PortID, channelAtoB.ChannelID, 1) + s.AssertPacketRelayed(ctx, chainB, channelBtoC.PortID, channelBtoC.ChannelID, 1) + + actualBalance, err := query.Balance(ctx, chainC, chainCAddress, chainCDenom.IBCDenom()) + s.Require().NoError(err) + + expected := testvalues.IBCTransferAmount + s.Require().Equal(expected, actualBalance.Int64()) + + // packet from B to C is acknowledged on chain C + s.AssertPacketAcknowledged(ctx, chainC, channelBtoC.Counterparty.PortID, channelBtoC.Counterparty.ChannelID, 1) + // packet from A to B is acknowledged on chain B + s.AssertPacketAcknowledged(ctx, chainB, channelAtoB.Counterparty.PortID, channelAtoB.Counterparty.ChannelID, 1) + }) +} + +// TestForwardingWithUnwindSucceeds tests the forwarding scenario in which +// a packet is sent from A to B, then unwound back to A and forwarded to C +// The overall flow of the packet is: +// A ---> B +// B --(unwind)-->A --(forward)-->B --(forward)--> C +func (s *TransferForwardingTestSuite) TestForwardingWithUnwindSucceeds() { + t := s.T() + ctx := context.TODO() + t.Parallel() + testName := t.Name() + relayer := s.CreateDefaultPaths(testName) + + chains := s.GetAllChains() + + chainA, chainB, chainC := chains[0], chains[1], chains[2] + + channelAtoB := s.GetChainAChannelForTest(testName) + channelBtoC := s.GetChannelsForTest(chainB, testName)[1] + + chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + chainAAddress := chainAWallet.FormattedAddress() + chainADenom := chainA.Config().Denom + + chainBWallet := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount) + chainBAddress := chainBWallet.FormattedAddress() + + chainCWallet := s.CreateUserOnChainC(ctx, testvalues.StartingTokenAmount) + chainCAddress := chainCWallet.FormattedAddress() + + t.Run("IBC transfer from A to B", func(t *testing.T) { + inFiveMinutes := time.Now().Add(5 * time.Minute).UnixNano() + resp := s.Transfer(ctx, chainA, chainAWallet, channelAtoB.PortID, channelAtoB.ChannelID, testvalues.DefaultTransferCoins(chainADenom), chainAAddress, chainBAddress, clienttypes.ZeroHeight(), uint64(inFiveMinutes), "", nil) + s.AssertTxSuccess(resp) + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer, testName) + }) + + chainBDenom := transfertypes.NewDenom(chainADenom, transfertypes.NewHop(channelAtoB.Counterparty.PortID, channelAtoB.Counterparty.ChannelID)) + t.Run("packet has reached B", func(t *testing.T) { + s.AssertPacketRelayed(ctx, chainA, channelAtoB.PortID, channelAtoB.ChannelID, 1) + s.AssertPacketAcknowledged(ctx, chainB, channelAtoB.Counterparty.PortID, channelAtoB.Counterparty.ChannelID, 1) + + balance, err := query.Balance(ctx, chainB, chainBAddress, chainBDenom.IBCDenom()) + s.Require().NoError(err) + + s.Require().Equal(testvalues.IBCTransferAmount, balance.Int64()) + }) + + t.Run("IBC transfer from B (unwind) to A and forwarded to C through B", func(t *testing.T) { + inFiveMinutes := time.Now().Add(5 * time.Minute).UnixNano() + + forwarding := transfertypes.NewForwarding( + true, + transfertypes.NewHop(channelAtoB.PortID, channelAtoB.ChannelID), + transfertypes.NewHop(channelBtoC.PortID, channelBtoC.ChannelID), + ) + msgTransfer := testsuite.GetMsgTransfer( + "", + "", + transfertypes.V2, + testvalues.DefaultTransferCoins(chainBDenom.IBCDenom()), + chainBAddress, + chainCAddress, + clienttypes.ZeroHeight(), + uint64(inFiveMinutes), + "", + forwarding) + resp := s.BroadcastMessages(ctx, chainB, chainBWallet, msgTransfer) + s.AssertTxSuccess(resp) + }) + + t.Run("packet has reached C", func(t *testing.T) { + chainCDenom := transfertypes.NewDenom(chainADenom, + transfertypes.NewHop(channelAtoB.Counterparty.PortID, channelAtoB.Counterparty.ChannelID), + transfertypes.NewHop(channelBtoC.Counterparty.PortID, channelBtoC.Counterparty.ChannelID), + ) + + err := test.WaitForCondition(time.Minute*10, time.Second*30, func() (bool, error) { + balance, err := query.Balance(ctx, chainC, chainCAddress, chainCDenom.IBCDenom()) + if err != nil { + return false, err + } + return balance.Int64() == testvalues.IBCTransferAmount, nil + }) + s.Require().NoError(err) + // packet from B to C is relayed + s.AssertPacketRelayed(ctx, chainB, channelBtoC.PortID, channelBtoC.ChannelID, 1) + // packet from B to C is acknowledged on chain C + s.AssertPacketAcknowledged(ctx, chainC, channelBtoC.Counterparty.PortID, channelBtoC.Counterparty.ChannelID, 1) + // packet from A to B is acknowledged on chain B + s.AssertPacketAcknowledged(ctx, chainB, channelAtoB.Counterparty.PortID, channelAtoB.Counterparty.ChannelID, 2) + }) +} + +func (s *TransferForwardingTestSuite) TestChannelUpgradeForwarding_Succeeds() { + ctx := context.TODO() + t := s.T() + testName := t.Name() + t.Parallel() + + relayer := s.CreateDefaultPaths(testName) + chains := s.GetAllChains() + + chainA, chainB, chainC := chains[0], chains[1], chains[2] + + opts := s.TransferChannelOptions() + opts.Version = transfertypes.V1 + + channelAtoB, _ := s.CreatePath(ctx, relayer, chains[0], chains[1], ibc.DefaultClientOpts(), opts, testName) + s.Require().Equal(transfertypes.V1, channelAtoB.Version, "the channel version is not ics20-1") + + channelBtoC, _ := s.CreatePath(ctx, relayer, chains[1], chains[2], ibc.DefaultClientOpts(), opts, testName) + s.Require().Equal(transfertypes.V1, channelBtoC.Version, "the channel version is not ics20-1") + + chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + chainAAddress := chainAWallet.FormattedAddress() + chainADenom := chainA.Config().Denom + + chainBWallet := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount) + + chainCWallet := s.CreateUserOnChainC(ctx, testvalues.StartingTokenAmount) + chainCAddress := chainCWallet.FormattedAddress() + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer, testName) + }) + t.Run("execute gov proposal to initiate channel upgrade", func(t *testing.T) { + chA, err := query.Channel(ctx, chainA, channelAtoB.PortID, channelAtoB.ChannelID) + s.Require().NoError(err) + + upgradeFields := channeltypes.NewUpgradeFields(chA.Ordering, chA.ConnectionHops, transfertypes.V2) + s.InitiateChannelUpgrade(ctx, chainA, chainAWallet, channelAtoB.PortID, channelAtoB.ChannelID, upgradeFields) + + chB, err := query.Channel(ctx, chainB, channelBtoC.PortID, channelBtoC.ChannelID) + s.Require().NoError(err) + + upgradeFields = channeltypes.NewUpgradeFields(chB.Ordering, chB.ConnectionHops, transfertypes.V2) + s.InitiateChannelUpgrade(ctx, chainB, chainBWallet, channelBtoC.PortID, channelBtoC.ChannelID, upgradeFields) + }) + + s.Require().NoError(test.WaitForBlocks(ctx, 10, chainA, chainB), "failed to wait for blocks") + + t.Run("verify channel A upgraded and transfer version is ics20-2", func(t *testing.T) { + channel, err := query.Channel(ctx, chainA, channelAtoB.PortID, channelAtoB.ChannelID) + s.Require().NoError(err) + s.Require().Equal(transfertypes.V2, channel.Version, "the channel version is not ics20-2") + }) + + t.Run("verify channel B upgraded and transfer version is ics20-2", func(t *testing.T) { + channel, err := query.Channel(ctx, chainB, channelBtoC.PortID, channelBtoC.ChannelID) + s.Require().NoError(err) + s.Require().Equal(transfertypes.V2, channel.Version, "the channel version is not ics20-2") + }) + + t.Run("IBC transfer from A to C with forwarding through B", func(t *testing.T) { + inFiveMinutes := time.Now().Add(5 * time.Minute).UnixNano() + forwarding := transfertypes.NewForwarding(false, transfertypes.NewHop(channelBtoC.PortID, channelBtoC.ChannelID)) + resp := s.Transfer(ctx, chainA, chainAWallet, channelAtoB.PortID, channelAtoB.ChannelID, testvalues.DefaultTransferCoins(chainADenom), chainAAddress, chainCAddress, clienttypes.ZeroHeight(), uint64(inFiveMinutes), "", forwarding) + s.AssertTxSuccess(resp) + }) + + t.Run("packets are relayed from A to B to C", func(t *testing.T) { + chainCDenom := transfertypes.NewDenom(chainADenom, + transfertypes.NewHop(channelBtoC.Counterparty.PortID, channelBtoC.Counterparty.ChannelID), + transfertypes.NewHop(channelAtoB.Counterparty.PortID, channelAtoB.Counterparty.ChannelID), + ) + + actualBalance, err := query.Balance(ctx, chainC, chainCAddress, chainCDenom.IBCDenom()) + s.Require().NoError(err) + + expected := testvalues.IBCTransferAmount + s.Require().Equal(expected, actualBalance.Int64()) + }) +} diff --git a/e2e/tests/transfer/incentivized_test.go b/e2e/tests/transfer/incentivized_test.go new file mode 100644 index 00000000000..eab38d18de1 --- /dev/null +++ b/e2e/tests/transfer/incentivized_test.go @@ -0,0 +1,770 @@ +//go:build !test_e2e + +package transfer + +import ( + "context" + "fmt" + "testing" + "time" + + "github.com/strangelove-ventures/interchaintest/v8/ibc" + test "github.com/strangelove-ventures/interchaintest/v8/testutil" + testifysuite "github.com/stretchr/testify/suite" + + sdkmath "cosmossdk.io/math" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/cosmos/ibc-go/e2e/testsuite" + "github.com/cosmos/ibc-go/e2e/testsuite/query" + "github.com/cosmos/ibc-go/e2e/testvalues" + feetypes "github.com/cosmos/ibc-go/v9/modules/apps/29-fee/types" + channeltypes "github.com/cosmos/ibc-go/v9/modules/core/04-channel/types" +) + +const ( + legacyMessage = "balance should be lowered by sum of recv_fee, ack_fee and timeout_fee" + capitalEfficientMessage = "balance should be lowered by max(recv_fee + ack_fee, timeout_fee)" +) + +type IncentivizedTransferTestSuite struct { + transferTester +} + +func TestIncentivizedTransferTestSuite(t *testing.T) { + testifysuite.Run(t, new(IncentivizedTransferTestSuite)) +} + +// CreateTransferFeePath explicitly enables fee middleware in the channel options. +func (s *IncentivizedTransferTestSuite) CreateTransferFeePath(testName string) (ibc.Relayer, ibc.ChannelOutput) { + return s.CreatePaths(ibc.DefaultClientOpts(), s.FeeTransferChannelOptions(), testName), s.GetChainAChannelForTest(testName) +} + +func (s *IncentivizedTransferTestSuite) TestMsgPayPacketFee_AsyncSingleSender_Succeeds() { + t := s.T() + ctx := context.TODO() + + testName := t.Name() + t.Parallel() + relayer, channelA := s.CreateTransferFeePath(testName) + + chainA, chainB := s.GetChains() + chainAVersion := chainA.Config().Images[0].Version + + var ( + chainADenom = chainA.Config().Denom + testFee = testvalues.DefaultFee(chainADenom) + chainATx ibc.Tx + payPacketFeeTxResp sdk.TxResponse + ) + + chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + + chainARelayerWallet, chainBRelayerWallet, err := s.RecoverRelayerWallets(ctx, relayer, testName) + t.Run("relayer wallets recovered", 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, testName) + + t.Run("register counterparty payee", func(t *testing.T) { + resp := s.RegisterCounterPartyPayee(ctx, chainB, chainBRelayerUser, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, chainBRelayerWallet.FormattedAddress(), chainARelayerWallet.FormattedAddress()) + s.AssertTxSuccess(resp) + }) + + t.Run("verify counterparty payee", func(t *testing.T) { + address, err := query.CounterPartyPayee(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: sdkmath.NewInt(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.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 := query.IncentivizedPacketsForChannel(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 = s.PayPacketFeeAsync(ctx, chainA, chainAWallet, packetID, packetFee) + s.AssertTxSuccess(payPacketFeeTxResp) + }) + + t.Run("there should be incentivized packets", func(t *testing.T) { + packets, err := query.IncentivizedPacketsForChannel(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.Equal(testFee.RecvFee)) + s.Require().True(actualFee.AckFee.Equal(testFee.AckFee)) + s.Require().True(actualFee.TimeoutFee.Equal(testFee.TimeoutFee)) + }) + + msg, escrowTotalFee := getMessageAndFee(testFee, chainAVersion) + t.Run(msg, func(t *testing.T) { + actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet) + s.Require().NoError(err) + + expected := testvalues.StartingTokenAmount - walletAmount.Amount.Int64() - escrowTotalFee.AmountOf(chainADenom).Int64() + s.Require().Equal(expected, actualBalance) + }) + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer, testName) + }) + + t.Run("packets are relayed", func(t *testing.T) { + packets, err := query.IncentivizedPacketsForChannel(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.Int64() - 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() + + testName := t.Name() + t.Parallel() + relayer, channelA := s.CreateTransferFeePath(testName) + + chainA, chainB := s.GetChains() + chainAVersion := chainA.Config().Images[0].Version + + var ( + chainADenom = chainA.Config().Denom + testFee = testvalues.DefaultFee(chainADenom) + payPacketFeeTxResp sdk.TxResponse + ) + + chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + + chainARelayerWallet, chainBRelayerWallet, err := s.RecoverRelayerWallets(ctx, relayer, testName) + t.Run("relayer wallets recovered", 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, testName) + + t.Run("register counterparty payee", func(t *testing.T) { + resp := s.RegisterCounterPartyPayee(ctx, chainB, chainBRelayerUser, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, chainBRelayerWallet.FormattedAddress(), chainARelayerWallet.FormattedAddress()) + s.AssertTxSuccess(resp) + }) + + t.Run("verify counterparty payee", func(t *testing.T) { + address, err := query.CounterPartyPayee(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) { + resp := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, sdk.NewCoins(transferAmount), chainAWallet.FormattedAddress(), testvalues.InvalidAddress, s.GetTimeoutHeight(ctx, chainB), 0, "", nil) + // this message should be successful, as receiver account is not validated on the sending chain. + s.AssertTxSuccess(resp) + }) + + 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 := query.IncentivizedPacketsForChannel(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 = s.PayPacketFeeAsync(ctx, chainA, chainAWallet, packetID, packetFee) + s.AssertTxSuccess(payPacketFeeTxResp) + }) + + t.Run("there should be incentivized packets", func(t *testing.T) { + packets, err := query.IncentivizedPacketsForChannel(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.Equal(testFee.RecvFee)) + s.Require().True(actualFee.AckFee.Equal(testFee.AckFee)) + s.Require().True(actualFee.TimeoutFee.Equal(testFee.TimeoutFee)) + }) + + msg, escrowTotalFee := getMessageAndFee(testFee, chainAVersion) + t.Run(msg, func(t *testing.T) { + actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet) + s.Require().NoError(err) + + expected := testvalues.StartingTokenAmount - transferAmount.Amount.Int64() - escrowTotalFee.AmountOf(chainADenom).Int64() + s.Require().Equal(expected, actualBalance) + }) + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer, testName) + }) + + t.Run("packets are relayed", func(t *testing.T) { + packets, err := query.IncentivizedPacketsForChannel(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() + + testName := t.Name() + t.Parallel() + relayer, channelA := s.CreateTransferFeePath(testName) + + chainA, chainB := s.GetChains() + chainAVersion := chainA.Config().Images[0].Version + + 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) + + chainARelayerWallet, chainBRelayerWallet, err := s.RecoverRelayerWallets(ctx, relayer, testName) + t.Run("relayer wallets recovered", 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, testName) + + 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 = s.RegisterCounterPartyPayee(ctx, chainB, chainBRelayerUser, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, chainBRelayerWallet.FormattedAddress(), chainARelayerWallet.FormattedAddress()) + s.AssertTxSuccess(multiMsgTxResponse) + }) + + t.Run("verify counterparty payee", func(t *testing.T) { + address, err := query.CounterPartyPayee(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 := query.IncentivizedPacketsForChannel(ctx, chainA, channelA.PortID, channelA.ChannelID) + s.Require().NoError(err) + s.Require().Empty(packets) + }) + + version, err := feetypes.MetadataFromVersion(channelA.Version) + s.Require().NoError(err) + + msgPayPacketFee := feetypes.NewMsgPayPacketFee(testFee, channelA.PortID, channelA.ChannelID, chainAWallet.FormattedAddress(), nil) + msgTransfer := testsuite.GetMsgTransfer( + channelA.PortID, + channelA.ChannelID, + version.AppVersion, + sdk.NewCoins(transferAmount), + chainAWallet.FormattedAddress(), + chainBWallet.FormattedAddress(), + s.GetTimeoutHeight(ctx, chainB), + 0, + "", + nil, + ) + resp := s.BroadcastMessages(ctx, chainA, chainAWallet, msgPayPacketFee, msgTransfer) + s.AssertTxSuccess(resp) + + t.Run("there should be incentivized packets", func(t *testing.T) { + packets, err := query.IncentivizedPacketsForChannel(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.Equal(testFee.RecvFee)) + s.Require().True(actualFee.AckFee.Equal(testFee.AckFee)) + s.Require().True(actualFee.TimeoutFee.Equal(testFee.TimeoutFee)) + }) + + msg, escrowTotalFee := getMessageAndFee(testFee, chainAVersion) + t.Run(msg, func(t *testing.T) { + actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet) + s.Require().NoError(err) + + expected := testvalues.StartingTokenAmount - testvalues.IBCTransferAmount - escrowTotalFee.AmountOf(chainADenom).Int64() + s.Require().Equal(expected, actualBalance) + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer, testName) + }) + + t.Run("packets are relayed", func(t *testing.T) { + packets, err := query.IncentivizedPacketsForChannel(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() + + testName := t.Name() + t.Parallel() + relayer, channelA := s.CreateTransferFeePath(testName) + + chainA, chainB := s.GetChains() + chainAVersion := chainA.Config().Images[0].Version + + 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) + + chainARelayerWallet, chainBRelayerWallet, err := s.RecoverRelayerWallets(ctx, relayer, testName) + t.Run("relayer wallets recovered", func(t *testing.T) { + s.Require().NoError(err) + }) + + _, chainBRelayerUser := s.GetRelayerUsers(ctx, testName) + + t.Run("register counterparty payee", func(t *testing.T) { + resp := s.RegisterCounterPartyPayee(ctx, chainB, chainBRelayerUser, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, chainBRelayerWallet.FormattedAddress(), chainARelayerWallet.FormattedAddress()) + s.AssertTxSuccess(resp) + }) + + t.Run("verify counterparty payee", func(t *testing.T) { + address, err := query.CounterPartyPayee(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: sdkmath.NewInt(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.Int64() + 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 := query.IncentivizedPacketsForChannel(ctx, chainA, channelA.PortID, channelA.ChannelID) + s.Require().NoError(err) + s.Require().Empty(packets) + }) + + t.Run("should succeed", func(t *testing.T) { + payPacketFeeTxResp = s.PayPacketFeeAsync(ctx, chainA, chainAWallet, packetID, packetFee) + s.AssertTxSuccess(payPacketFeeTxResp) + }) + + t.Run("there should be incentivized packets", func(t *testing.T) { + packets, err := query.IncentivizedPacketsForChannel(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.Equal(testFee.RecvFee)) + s.Require().True(actualFee.AckFee.Equal(testFee.AckFee)) + s.Require().True(actualFee.TimeoutFee.Equal(testFee.TimeoutFee)) + }) + + msg, escrowTotalFee := getMessageAndFee(testFee, chainAVersion) + t.Run(msg, func(t *testing.T) { + actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet) + s.Require().NoError(err) + + expected := testvalues.StartingTokenAmount - chainBWalletAmount.Amount.Int64() - escrowTotalFee.AmountOf(chainADenom).Int64() + s.Require().Equal(expected, actualBalance) + }) + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer, testName) + }) + + t.Run("packets are relayed", func(t *testing.T) { + packets, err := query.IncentivizedPacketsForChannel(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() + + testName := t.Name() + t.Parallel() + relayer, channelA := s.CreateTransferFeePath(testName) + + chainA, _ := s.GetChains() + chainAVersion := chainA.Config().Images[0].Version + + var ( + chainADenom = chainA.Config().Denom + testFee = testvalues.DefaultFee(chainADenom) + chainATx ibc.Tx + payPacketFeeTxResp sdk.TxResponse + ) + + chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + + _, _, err := s.RecoverRelayerWallets(ctx, relayer, testName) + t.Run("relayer wallets recovered", func(t *testing.T) { + s.Require().NoError(err) + }) + + chainBWalletAmount := ibc.WalletAmount{ + Address: chainAWallet.FormattedAddress(), // destination address + Denom: chainADenom, + Amount: sdkmath.NewInt(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.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 := query.IncentivizedPacketsForChannel(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 = s.PayPacketFeeAsync(ctx, chainA, chainAWallet, packetID, packetFee) + s.AssertTxSuccess(payPacketFeeTxResp) + }) + + t.Run("should be incentivized packets", func(t *testing.T) { + packets, err := query.IncentivizedPacketsForChannel(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.Equal(testFee.RecvFee)) + s.Require().True(actualFee.AckFee.Equal(testFee.AckFee)) + s.Require().True(actualFee.TimeoutFee.Equal(testFee.TimeoutFee)) + }) + }) + + msg, escrowTotalFee := getMessageAndFee(testFee, chainAVersion) + t.Run(msg, func(t *testing.T) { + actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet) + s.Require().NoError(err) + + expected := testvalues.StartingTokenAmount - chainBWalletAmount.Amount.Int64() - escrowTotalFee.AmountOf(chainADenom).Int64() + s.Require().Equal(expected, actualBalance) + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer, testName) + }) + + t.Run("with no counterparty address", func(t *testing.T) { + t.Run("packets are relayed", func(t *testing.T) { + packets, err := query.IncentivizedPacketsForChannel(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.Int64() - testFee.AckFee.AmountOf(chainADenom).Int64() + s.Require().Equal(expected, actualBalance) + }) + }) +} + +func (s *IncentivizedTransferTestSuite) TestMsgPayPacketFee_AsyncMultipleSenders_Succeeds() { + t := s.T() + ctx := context.TODO() + + testName := t.Name() + t.Parallel() + relayer, channelA := s.CreateTransferFeePath(testName) + + chainA, chainB := s.GetChains() + chainAVersion := chainA.Config().Images[0].Version + + 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) + + chainARelayerWallet, chainBRelayerWallet, err := s.RecoverRelayerWallets(ctx, relayer, testName) + t.Run("relayer wallets recovered", 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, testName) + + t.Run("register counterparty payee", func(t *testing.T) { + resp := s.RegisterCounterPartyPayee(ctx, chainB, chainBRelayerUser, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, chainBRelayerWallet.FormattedAddress(), chainARelayerWallet.FormattedAddress()) + s.AssertTxSuccess(resp) + }) + + t.Run("verify counterparty payee", func(t *testing.T) { + address, err := query.CounterPartyPayee(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: sdkmath.NewInt(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.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 := query.IncentivizedPacketsForChannel(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 = s.PayPacketFeeAsync(ctx, chainA, chainAWallet1, packetID, packetFee1) + s.AssertTxSuccess(payPacketFeeTxResp) + }) + t.Run("paying packetFee2 should succeed", func(t *testing.T) { + payPacketFeeTxResp = s.PayPacketFeeAsync(ctx, chainA, chainAWallet2, packetID, packetFee2) + s.AssertTxSuccess(payPacketFeeTxResp) + }) + + t.Run("there should be incentivized packets", func(t *testing.T) { + packets, err := query.IncentivizedPacketsForChannel(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.Equal(testFee.RecvFee)) + s.Require().True(actualFee1.AckFee.Equal(testFee.AckFee)) + s.Require().True(actualFee1.TimeoutFee.Equal(testFee.TimeoutFee)) + + s.Require().True(actualFee2.RecvFee.Equal(testFee.RecvFee)) + s.Require().True(actualFee2.AckFee.Equal(testFee.AckFee)) + s.Require().True(actualFee2.TimeoutFee.Equal(testFee.TimeoutFee)) + }) + + msgFn := func(wallet string) string { + return fmt.Sprintf("balance of %s should be lowered by max(recv_fee + ack_fee, timeout_fee)", wallet) + } + escrowTotalFee := testFee.Total() + if !testvalues.CapitalEfficientFeeEscrowFeatureReleases.IsSupported(chainAVersion) { + msgFn = func(wallet string) string { + return fmt.Sprintf("balance of %s should be lowered by sum of recv_fee, ack_fee and timeout_fee", wallet) + } + escrowTotalFee = testFee.RecvFee.Add(testFee.AckFee...).Add(testFee.TimeoutFee...) + } + t.Run(msgFn("chainAWallet1"), func(t *testing.T) { + actualBalance1, err := s.GetChainANativeBalance(ctx, chainAWallet1) + s.Require().NoError(err) + + expected1 := testvalues.StartingTokenAmount - walletAmount1.Amount.Int64() - escrowTotalFee.AmountOf(chainADenom).Int64() + s.Require().Equal(expected1, actualBalance1) + }) + + t.Run(msgFn("chainAWallet2"), func(t *testing.T) { + actualBalance2, err := s.GetChainANativeBalance(ctx, chainAWallet2) + s.Require().NoError(err) + + expected2 := testvalues.StartingTokenAmount - escrowTotalFee.AmountOf(chainADenom).Int64() + s.Require().Equal(expected2, actualBalance2) + }) + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer, testName) + }) + + t.Run("packets are relayed", func(t *testing.T) { + packets, err := query.IncentivizedPacketsForChannel(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.Int64() - 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) + }) +} + +// getMessageAndFee returns the message that should be used for t.Run as well as the expected fee based on +// whether or not capital efficient fee escrow is supported. +func getMessageAndFee(fee feetypes.Fee, chainVersion string) (string, sdk.Coins) { + msg := capitalEfficientMessage + escrowTotalFee := fee.Total() + if !testvalues.CapitalEfficientFeeEscrowFeatureReleases.IsSupported(chainVersion) { + msg = legacyMessage + escrowTotalFee = fee.RecvFee.Add(fee.AckFee...).Add(fee.TimeoutFee...) + } + return msg, escrowTotalFee +} diff --git a/e2e/tests/transfer/localhost_test.go b/e2e/tests/transfer/localhost_test.go new file mode 100644 index 00000000000..dac73910e4b --- /dev/null +++ b/e2e/tests/transfer/localhost_test.go @@ -0,0 +1,167 @@ +//go:build !test_e2e + +package transfer + +import ( + "context" + "testing" + + test "github.com/strangelove-ventures/interchaintest/v8/testutil" + testifysuite "github.com/stretchr/testify/suite" + + "github.com/cosmos/ibc-go/e2e/testsuite" + "github.com/cosmos/ibc-go/e2e/testsuite/query" + "github.com/cosmos/ibc-go/e2e/testvalues" + transfertypes "github.com/cosmos/ibc-go/v9/modules/apps/transfer/types" + clienttypes "github.com/cosmos/ibc-go/v9/modules/core/02-client/types" + channeltypes "github.com/cosmos/ibc-go/v9/modules/core/04-channel/types" + "github.com/cosmos/ibc-go/v9/modules/core/exported" + localhost "github.com/cosmos/ibc-go/v9/modules/light-clients/09-localhost" + ibctesting "github.com/cosmos/ibc-go/v9/testing" +) + +func TestTransferLocalhostTestSuite(t *testing.T) { + testifysuite.Run(t, new(LocalhostTransferTestSuite)) +} + +type LocalhostTransferTestSuite struct { + testsuite.E2ETestSuite +} + +// TestMsgTransfer_Localhost creates two wallets on a single chain and performs MsgTransfers back and forth +// to ensure ibc functions as expected on localhost. This test is largely the same as TestMsgTransfer_Succeeds_Nonincentivized +// except that chain B is replaced with an additional wallet on chainA. +func (s *LocalhostTransferTestSuite) TestMsgTransfer_Localhost() { + t := s.T() + ctx := context.TODO() + + chainA, _ := s.GetChains() + + channelVersion := transfertypes.V2 + + chainADenom := chainA.Config().Denom + + rlyWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + userAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + userBWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + + var ( + msgChanOpenInitRes channeltypes.MsgChannelOpenInitResponse + msgChanOpenTryRes channeltypes.MsgChannelOpenTryResponse + ack []byte + packet channeltypes.Packet + ) + + s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA), "failed to wait for blocks") + + t.Run("channel open init localhost", func(t *testing.T) { + msgChanOpenInit := channeltypes.NewMsgChannelOpenInit( + transfertypes.PortID, channelVersion, + channeltypes.UNORDERED, []string{exported.LocalhostConnectionID}, + transfertypes.PortID, rlyWallet.FormattedAddress(), + ) + + s.Require().NoError(msgChanOpenInit.ValidateBasic()) + + txResp := s.BroadcastMessages(ctx, chainA, rlyWallet, msgChanOpenInit) + s.AssertTxSuccess(txResp) + + s.Require().NoError(testsuite.UnmarshalMsgResponses(txResp, &msgChanOpenInitRes)) + }) + + t.Run("channel open try localhost", func(t *testing.T) { + msgChanOpenTry := channeltypes.NewMsgChannelOpenTry( + transfertypes.PortID, channelVersion, + channeltypes.UNORDERED, []string{exported.LocalhostConnectionID}, + transfertypes.PortID, msgChanOpenInitRes.ChannelId, + channelVersion, localhost.SentinelProof, clienttypes.ZeroHeight(), rlyWallet.FormattedAddress(), + ) + + txResp := s.BroadcastMessages(ctx, chainA, rlyWallet, msgChanOpenTry) + s.AssertTxSuccess(txResp) + + s.Require().NoError(testsuite.UnmarshalMsgResponses(txResp, &msgChanOpenTryRes)) + }) + + t.Run("channel open ack localhost", func(t *testing.T) { + msgChanOpenAck := channeltypes.NewMsgChannelOpenAck( + transfertypes.PortID, msgChanOpenInitRes.ChannelId, + msgChanOpenTryRes.ChannelId, channelVersion, + localhost.SentinelProof, clienttypes.ZeroHeight(), rlyWallet.FormattedAddress(), + ) + + txResp := s.BroadcastMessages(ctx, chainA, rlyWallet, msgChanOpenAck) + s.AssertTxSuccess(txResp) + }) + + t.Run("channel open confirm localhost", func(t *testing.T) { + msgChanOpenConfirm := channeltypes.NewMsgChannelOpenConfirm( + transfertypes.PortID, msgChanOpenTryRes.ChannelId, + localhost.SentinelProof, clienttypes.ZeroHeight(), rlyWallet.FormattedAddress(), + ) + + txResp := s.BroadcastMessages(ctx, chainA, rlyWallet, msgChanOpenConfirm) + s.AssertTxSuccess(txResp) + }) + + t.Run("query localhost transfer channel ends", func(t *testing.T) { + channelEndA, err := query.Channel(ctx, chainA, transfertypes.PortID, msgChanOpenInitRes.ChannelId) + s.Require().NoError(err) + s.Require().NotNil(channelEndA) + + channelEndB, err := query.Channel(ctx, chainA, transfertypes.PortID, msgChanOpenTryRes.ChannelId) + s.Require().NoError(err) + s.Require().NotNil(channelEndB) + + s.Require().Equal(channelEndA.ConnectionHops, channelEndB.ConnectionHops) + }) + + t.Run("send packet localhost ibc transfer", func(t *testing.T) { + var err error + txResp := s.Transfer(ctx, chainA, userAWallet, transfertypes.PortID, msgChanOpenInitRes.ChannelId, testvalues.DefaultTransferCoins(chainADenom), userAWallet.FormattedAddress(), userBWallet.FormattedAddress(), clienttypes.NewHeight(1, 500), 0, "", nil) + s.AssertTxSuccess(txResp) + + packet, err = ibctesting.ParsePacketFromEvents(txResp.Events) + s.Require().NoError(err) + s.Require().NotNil(packet) + }) + + t.Run("tokens are escrowed", func(t *testing.T) { + actualBalance, err := s.GetChainANativeBalance(ctx, userAWallet) + s.Require().NoError(err) + + expected := testvalues.StartingTokenAmount - testvalues.IBCTransferAmount + s.Require().Equal(expected, actualBalance) + }) + + t.Run("recv packet localhost ibc transfer", func(t *testing.T) { + var err error + msgRecvPacket := channeltypes.NewMsgRecvPacket(packet, localhost.SentinelProof, clienttypes.ZeroHeight(), rlyWallet.FormattedAddress()) + + txResp := s.BroadcastMessages(ctx, chainA, rlyWallet, msgRecvPacket) + s.AssertTxSuccess(txResp) + + ack, err = ibctesting.ParseAckFromEvents(txResp.Events) + s.Require().NoError(err) + s.Require().NotNil(ack) + }) + + t.Run("acknowledge packet localhost ibc transfer", func(t *testing.T) { + msgAcknowledgement := channeltypes.NewMsgAcknowledgement(packet, ack, localhost.SentinelProof, clienttypes.ZeroHeight(), rlyWallet.FormattedAddress()) + + txResp := s.BroadcastMessages(ctx, chainA, rlyWallet, msgAcknowledgement) + s.AssertTxSuccess(txResp) + }) + + t.Run("verify tokens transferred", func(t *testing.T) { + s.AssertPacketRelayed(ctx, chainA, transfertypes.PortID, msgChanOpenInitRes.ChannelId, 1) + + ibcToken := testsuite.GetIBCToken(chainADenom, transfertypes.PortID, msgChanOpenTryRes.ChannelId) + actualBalance, err := query.Balance(ctx, chainA, userBWallet.FormattedAddress(), ibcToken.IBCDenom()) + + s.Require().NoError(err) + + expected := testvalues.IBCTransferAmount + s.Require().Equal(expected, actualBalance.Int64()) + }) +} diff --git a/e2e/tests/transfer/send_enabled_test.go b/e2e/tests/transfer/send_enabled_test.go new file mode 100644 index 00000000000..f3bee515a77 --- /dev/null +++ b/e2e/tests/transfer/send_enabled_test.go @@ -0,0 +1,98 @@ +//go:build !test_e2e + +package transfer + +import ( + "context" + "testing" + + test "github.com/strangelove-ventures/interchaintest/v8/testutil" + testifysuite "github.com/stretchr/testify/suite" + + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + paramsproposaltypes "github.com/cosmos/cosmos-sdk/x/params/types/proposal" + + "github.com/cosmos/ibc-go/e2e/testsuite" + "github.com/cosmos/ibc-go/e2e/testsuite/query" + "github.com/cosmos/ibc-go/e2e/testvalues" + transfertypes "github.com/cosmos/ibc-go/v9/modules/apps/transfer/types" + ibctesting "github.com/cosmos/ibc-go/v9/testing" +) + +func TestTransferTestSuiteSendEnabled(t *testing.T) { + testifysuite.Run(t, new(TransferTestSuiteSendEnabled)) +} + +type TransferTestSuiteSendEnabled struct { + transferTester +} + +func (s *TransferTestSuiteSendEnabled) SetupSuite() { + s.SetupChains(context.TODO(), nil, func(options *testsuite.ChainOptions) { + options.RelayerCount = 1 + }) +} + +// TestSendEnabledParam tests changing ics20 SendEnabled parameter +func (s *TransferTestSuiteSendEnabled) TestSendEnabledParam() { + t := s.T() + ctx := context.TODO() + + testName := t.Name() + // Note: explicitly not using t.Parallel() in this test as it makes chain wide changes + s.CreateTransferPath(testName) + + chainA, chainB := s.GetChains() + + channelA := s.GetChainAChannelForTest(testName) + chainAVersion := chainA.Config().Images[0].Version + chainADenom := chainA.Config().Denom + + chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + chainAAddress := chainAWallet.FormattedAddress() + + chainBWallet := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount) + chainBAddress := chainBWallet.FormattedAddress() + + isSelfManagingParams := testvalues.SelfParamsFeatureReleases.IsSupported(chainAVersion) + + govModuleAddress, err := query.ModuleAccountAddress(ctx, govtypes.ModuleName, chainA) + s.Require().NoError(err) + s.Require().NotNil(govModuleAddress) + + 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.QueryTransferParams(ctx, chainA).SendEnabled + s.Require().True(enabled) + }) + + t.Run("ensure packets can be sent", func(t *testing.T) { + transferTxResp := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, testvalues.DefaultTransferCoins(chainADenom), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "", nil) + s.AssertTxSuccess(transferTxResp) + }) + + t.Run("change send enabled parameter to disabled", func(t *testing.T) { + if isSelfManagingParams { + msg := transfertypes.NewMsgUpdateParams(govModuleAddress.String(), transfertypes.NewParams(false, true)) + s.ExecuteAndPassGovV1Proposal(ctx, msg, chainA, chainAWallet) + } else { + changes := []paramsproposaltypes.ParamChange{ + paramsproposaltypes.NewParamChange(transfertypes.StoreKey, string(transfertypes.KeySendEnabled), "false"), + } + + proposal := paramsproposaltypes.NewParameterChangeProposal(ibctesting.Title, ibctesting.Description, changes) + s.ExecuteAndPassGovV1Beta1Proposal(ctx, chainA, chainAWallet, proposal) + } + }) + + t.Run("ensure transfer params are disabled", func(t *testing.T) { + enabled := s.QueryTransferParams(ctx, chainA).SendEnabled + s.Require().False(enabled) + }) + + t.Run("ensure ics20 transfer fails", func(t *testing.T) { + transferTxResp := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, testvalues.DefaultTransferCoins(chainADenom), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "", nil) + s.AssertTxFailure(transferTxResp, transfertypes.ErrSendDisabled) + }) +} diff --git a/e2e/tests/transfer/send_receive_test.go b/e2e/tests/transfer/send_receive_test.go new file mode 100644 index 00000000000..d9f7b51ce2c --- /dev/null +++ b/e2e/tests/transfer/send_receive_test.go @@ -0,0 +1,154 @@ +//go:build !test_e2e + +package transfer + +import ( + "context" + "testing" + + test "github.com/strangelove-ventures/interchaintest/v8/testutil" + testifysuite "github.com/stretchr/testify/suite" + + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + paramsproposaltypes "github.com/cosmos/cosmos-sdk/x/params/types/proposal" + + "github.com/cosmos/ibc-go/e2e/testsuite" + "github.com/cosmos/ibc-go/e2e/testsuite/query" + "github.com/cosmos/ibc-go/e2e/testvalues" + transfertypes "github.com/cosmos/ibc-go/v9/modules/apps/transfer/types" + ibctesting "github.com/cosmos/ibc-go/v9/testing" +) + +func TestTransferTestSuiteSendReceive(t *testing.T) { + testifysuite.Run(t, new(TransferTestSuiteSendReceive)) +} + +type TransferTestSuiteSendReceive struct { + transferTester +} + +func (s *TransferTestSuiteSendReceive) SetupSuite() { + s.SetupChains(context.TODO(), nil, func(options *testsuite.ChainOptions) { + options.RelayerCount = 1 + }) +} + +// TestReceiveEnabledParam tests changing ics20 ReceiveEnabled parameter +func (s *TransferTestSuiteSendReceive) TestReceiveEnabledParam() { + t := s.T() + ctx := context.TODO() + + testName := t.Name() + // Note: explicitly not using t.Parallel() in this test as it makes chain wide changes + s.CreateTransferPath(testName) + + chainA, chainB := s.GetChains() + + relayer := s.GetRelayerForTest(testName) + channelA := s.GetChainAChannelForTest(testName) + + chainAVersion := chainA.Config().Images[0].Version + + 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() + ) + + isSelfManagingParams := testvalues.SelfParamsFeatureReleases.IsSupported(chainAVersion) + + govModuleAddress, err := query.ModuleAccountAddress(ctx, govtypes.ModuleName, chainA) + s.Require().NoError(err) + s.Require().NotNil(govModuleAddress) + + 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.QueryTransferParams(ctx, chainA).ReceiveEnabled + 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 := s.Transfer(ctx, chainB, chainBWallet, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, testvalues.DefaultTransferCoins(chainBDenom), chainBAddress, chainAAddress, s.GetTimeoutHeight(ctx, chainA), 0, "", nil) + s.AssertTxSuccess(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, testName) + }) + + t.Run("packets are relayed", func(t *testing.T) { + s.AssertPacketRelayed(ctx, chainA, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, 1) + actualBalance, err := query.Balance(ctx, chainA, chainAAddress, chainAIBCToken.IBCDenom()) + + s.Require().NoError(err) + + expected := testvalues.IBCTransferAmount + s.Require().Equal(expected, actualBalance.Int64()) + }) + + t.Run("stop relayer", func(t *testing.T) { + s.StopRelayer(ctx, relayer) + }) + }) + + t.Run("change receive enabled parameter to disabled ", func(t *testing.T) { + if isSelfManagingParams { + msg := transfertypes.NewMsgUpdateParams(govModuleAddress.String(), transfertypes.NewParams(false, false)) + s.ExecuteAndPassGovV1Proposal(ctx, msg, chainA, chainAWallet) + } else { + changes := []paramsproposaltypes.ParamChange{ + paramsproposaltypes.NewParamChange(transfertypes.StoreKey, string(transfertypes.KeyReceiveEnabled), "false"), + } + + proposal := paramsproposaltypes.NewParameterChangeProposal(ibctesting.Title, ibctesting.Description, changes) + s.ExecuteAndPassGovV1Beta1Proposal(ctx, chainA, chainAWallet, proposal) + } + }) + + t.Run("ensure transfer params are disabled", func(t *testing.T) { + enabled := s.QueryTransferParams(ctx, chainA).ReceiveEnabled + 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 := s.Transfer(ctx, chainB, chainBWallet, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, testvalues.DefaultTransferCoins(chainBDenom), chainBAddress, chainAAddress, s.GetTimeoutHeight(ctx, chainA), 0, "", nil) + s.AssertTxSuccess(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, testName) + }) + + 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) + }) + }) +} diff --git a/e2e/tests/transfer/upgradesv1_test.go b/e2e/tests/transfer/upgradesv1_test.go new file mode 100644 index 00000000000..2850e14b4ec --- /dev/null +++ b/e2e/tests/transfer/upgradesv1_test.go @@ -0,0 +1,363 @@ +//go:build !test_e2e + +package transfer + +import ( + "context" + "testing" + + "github.com/strangelove-ventures/interchaintest/v8/ibc" + test "github.com/strangelove-ventures/interchaintest/v8/testutil" + testifysuite "github.com/stretchr/testify/suite" + + sdkmath "cosmossdk.io/math" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/cosmos/ibc-go/e2e/testsuite" + "github.com/cosmos/ibc-go/e2e/testsuite/query" + "github.com/cosmos/ibc-go/e2e/testvalues" + feetypes "github.com/cosmos/ibc-go/v9/modules/apps/29-fee/types" + transfertypes "github.com/cosmos/ibc-go/v9/modules/apps/transfer/types" + channeltypes "github.com/cosmos/ibc-go/v9/modules/core/04-channel/types" +) + +func TestTransferChannelUpgradesV1TestSuite(t *testing.T) { + testifysuite.Run(t, new(TransferChannelUpgradesV1TestSuite)) +} + +type TransferChannelUpgradesV1TestSuite struct { + testsuite.E2ETestSuite +} + +func (s *TransferChannelUpgradesV1TestSuite) SetupChannelUpgradesV1Test(testName string) { + opts := s.TransferChannelOptions() + opts.Version = transfertypes.V1 + s.CreatePaths(ibc.DefaultClientOpts(), opts, testName) +} + +// TestChannelUpgrade_WithICS20v2_Succeeds tests upgrading a transfer channel to ICS20 v2. +func (s *TransferChannelUpgradesV1TestSuite) TestChannelUpgrade_WithICS20v2_Succeeds() { + t := s.T() + ctx := context.TODO() + + testName := t.Name() + s.SetupChannelUpgradesV1Test(testName) + + relayer, channelA := s.GetRelayerForTest(testName), s.GetChainAChannelForTest(testName) + + channelB := channelA.Counterparty + chainA, chainB := s.GetChains() + + chainADenom := chainA.Config().Denom + chainBDenom := chainB.Config().Denom + chainAIBCToken := testsuite.GetIBCToken(chainBDenom, channelA.PortID, channelA.ChannelID) + chainBIBCToken := testsuite.GetIBCToken(chainADenom, channelB.PortID, channelB.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("verify transfer version of channel A is ics20-1", func(t *testing.T) { + channel, err := query.Channel(ctx, chainA, channelA.PortID, channelA.ChannelID) + s.Require().NoError(err) + s.Require().Equal(transfertypes.V1, channel.Version, "the channel version is not ics20-1") + }) + + t.Run("native token transfer from chainA to chainB, sender is source of tokens", func(t *testing.T) { + chainBWalletAmount := ibc.WalletAmount{ + Address: chainBWallet.FormattedAddress(), // destination address + Denom: chainA.Config().Denom, + Amount: sdkmath.NewInt(testvalues.IBCTransferAmount), + } + + transferTxResp, err := chainA.SendIBCTransfer(ctx, channelA.ChannelID, chainAWallet.KeyName(), chainBWalletAmount, ibc.TransferOptions{}) + s.Require().NoError(err) + s.Require().NoError(transferTxResp.Validate(), "chain-a ibc transfer tx is invalid") + }) + + s.Require().NoError(test.WaitForBlocks(ctx, 5, chainA, chainB), "failed to wait for blocks") + + 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) + + actualTotalEscrow, err := query.TotalEscrowForDenom(ctx, chainA, chainADenom) + s.Require().NoError(err) + + expectedTotalEscrow := sdk.NewCoin(chainADenom, sdkmath.NewInt(testvalues.IBCTransferAmount)) + s.Require().Equal(expectedTotalEscrow, actualTotalEscrow) + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer, testName) + }) + + t.Run("packets are relayed", func(t *testing.T) { + s.AssertPacketRelayed(ctx, chainA, channelA.PortID, channelA.ChannelID, 1) + + actualBalance, err := query.Balance(ctx, chainB, chainBAddress, chainBIBCToken.IBCDenom()) + s.Require().NoError(err) + + expected := testvalues.IBCTransferAmount + s.Require().Equal(expected, actualBalance.Int64()) + }) + + t.Run("execute gov proposal to initiate channel upgrade", func(t *testing.T) { + chA, err := query.Channel(ctx, chainA, channelA.PortID, channelA.ChannelID) + s.Require().NoError(err) + + upgradeFields := channeltypes.NewUpgradeFields(chA.Ordering, chA.ConnectionHops, transfertypes.V2) + s.InitiateChannelUpgrade(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, upgradeFields) + }) + + s.Require().NoError(test.WaitForBlocks(ctx, 10, chainA, chainB), "failed to wait for blocks") + + t.Run("verify channel A upgraded and transfer version is ics20-2", func(t *testing.T) { + channel, err := query.Channel(ctx, chainA, channelA.PortID, channelA.ChannelID) + s.Require().NoError(err) + s.Require().Equal(transfertypes.V2, channel.Version, "the channel version is not ics20-2") + }) + + t.Run("verify channel B upgraded and transfer version is ics20-2", func(t *testing.T) { + channel, err := query.Channel(ctx, chainB, channelB.PortID, channelB.ChannelID) + s.Require().NoError(err) + s.Require().Equal(transfertypes.V2, channel.Version, "the channel version is not ics20-2") + }) + + // send the native chainB denom and also the ibc token from chainA + transferCoins := []sdk.Coin{ + testvalues.DefaultTransferAmount(chainBIBCToken.IBCDenom()), + testvalues.DefaultTransferAmount(chainBDenom), + } + + t.Run("native token from chain B and non-native IBC token from chainA, both to chainA", func(t *testing.T) { + transferTxResp := s.Transfer(ctx, chainB, chainBWallet, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, transferCoins, chainBAddress, chainAAddress, s.GetTimeoutHeight(ctx, chainA), 0, "", nil) + s.AssertTxSuccess(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, chainB, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, 1) + + t.Run("chain A native denom", func(t *testing.T) { + actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet) + s.Require().NoError(err) + + expected := testvalues.StartingTokenAmount + s.Require().Equal(expected, actualBalance) + }) + + t.Run("chain B IBC denom", func(t *testing.T) { + actualBalance, err := query.Balance(ctx, chainA, chainAAddress, chainAIBCToken.IBCDenom()) + s.Require().NoError(err) + + expected := testvalues.IBCTransferAmount + s.Require().Equal(expected, actualBalance.Int64()) + }) + }) + + t.Run("tokens are un-escrowed", func(t *testing.T) { + t.Run("chain A escrow", func(t *testing.T) { + actualTotalEscrow, err := query.TotalEscrowForDenom(ctx, chainA, chainADenom) + s.Require().NoError(err) + s.Require().Equal(sdk.NewCoin(chainADenom, sdkmath.NewInt(0)), actualTotalEscrow) // total escrow is zero because tokens have come back + }) + + t.Run("chain B escrow", func(t *testing.T) { + actualTotalEscrow, err := query.TotalEscrowForDenom(ctx, chainB, chainBDenom) + s.Require().NoError(err) + s.Require().Equal(sdk.NewCoin(chainBDenom, sdkmath.NewInt(testvalues.IBCTransferAmount)), actualTotalEscrow) + }) + }) +} + +// TestChannelUpgrade_WithFeeMiddlewareAndICS20v2_Succeeds tests upgrading a transfer channel to wire up fee middleware and upgrade to ICS20 v2. +func (s *TransferChannelUpgradesV1TestSuite) TestChannelUpgrade_WithFeeMiddlewareAndICS20v2_Succeeds() { + t := s.T() + ctx := context.TODO() + + testName := t.Name() + s.SetupChannelUpgradesV1Test(testName) + + relayer, channelA := s.GetRelayerForTest(testName), s.GetChainAChannelForTest(testName) + + channelB := channelA.Counterparty + chainA, chainB := s.GetChains() + + chainADenom := chainA.Config().Denom + chainBDenom := chainB.Config().Denom + chainAIBCToken := testsuite.GetIBCToken(chainBDenom, channelA.PortID, channelA.ChannelID) + chainBIBCToken := testsuite.GetIBCToken(chainADenom, channelB.PortID, channelB.ChannelID) + + chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + chainBWallet := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount) + chainBAddress := chainBWallet.FormattedAddress() + + s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB), "failed to wait for blocks") + + var ( + err error + channel channeltypes.Channel + ) + + t.Run("verify transfer version of channel A is ics20-1", func(t *testing.T) { + channel, err = query.Channel(ctx, chainA, channelA.PortID, channelA.ChannelID) + s.Require().NoError(err) + s.Require().Equal(transfertypes.V1, channel.Version, "the channel version is not ics20-1") + }) + + t.Run("native token transfer from chainB to chainA, sender is source of tokens", func(t *testing.T) { + chainAwalletAmount := ibc.WalletAmount{ + Address: chainAWallet.FormattedAddress(), // destination address + Denom: chainBDenom, + Amount: sdkmath.NewInt(testvalues.IBCTransferAmount), + } + + transferTxResp, err := chainB.SendIBCTransfer(ctx, channelB.ChannelID, chainBWallet.KeyName(), chainAwalletAmount, ibc.TransferOptions{}) + s.Require().NoError(err) + s.Require().NoError(transferTxResp.Validate(), "chain-b ibc transfer tx is invalid") + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer, testName) + }) + + t.Run("execute gov proposal to initiate channel upgrade", func(t *testing.T) { + channel.Version = transfertypes.V2 // change version to ics20-2 + s.InitiateChannelUpgrade(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, s.CreateUpgradeFields(channel)) + }) + + s.Require().NoError(test.WaitForBlocks(ctx, 10, chainA, chainB), "failed to wait for blocks") + + t.Run("verify channel A upgraded and channel version is {ics29-1,ics20-2}", func(t *testing.T) { + channel, err = query.Channel(ctx, chainA, channelA.PortID, channelA.ChannelID) + s.Require().NoError(err) + + // check the channel version include the fee version + version, err := feetypes.MetadataFromVersion(channel.Version) + s.Require().NoError(err) + s.Require().Equal(feetypes.Version, version.FeeVersion, "the channel version did not include ics29") + s.Require().Equal(transfertypes.V2, version.AppVersion, "the channel version is not ics20-2") + }) + + t.Run("verify channel B upgraded and channel version is {ics29-1,ics20-2}", func(t *testing.T) { + channel, err = query.Channel(ctx, chainB, channelB.PortID, channelB.ChannelID) + s.Require().NoError(err) + + // check the channel version include the fee version + version, err := feetypes.MetadataFromVersion(channel.Version) + s.Require().NoError(err) + s.Require().Equal(feetypes.Version, version.FeeVersion, "the channel version did not include ics29") + s.Require().Equal(transfertypes.V2, version.AppVersion, "the channel version is not ics20-2") + }) + + var ( + chainARelayerWallet, chainBRelayerWallet ibc.Wallet + relayerAStartingBalance int64 + testFee = testvalues.DefaultFee(chainADenom) + ) + + t.Run("recover relayer wallets", func(t *testing.T) { + _, _, err := s.RecoverRelayerWallets(ctx, relayer, testName) + s.Require().NoError(err) + + chainARelayerWallet, chainBRelayerWallet, err = s.GetRelayerWallets(relayer) + s.Require().NoError(err) + + relayerAStartingBalance, err = s.GetChainANativeBalance(ctx, chainARelayerWallet) + s.Require().NoError(err) + t.Logf("relayer A user starting with balance: %d", relayerAStartingBalance) + }) + + t.Run("register and verify counterparty payee", func(t *testing.T) { + _, chainBRelayerUser := s.GetRelayerUsers(ctx, testName) + resp := s.RegisterCounterPartyPayee(ctx, chainB, chainBRelayerUser, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, chainBRelayerWallet.FormattedAddress(), chainARelayerWallet.FormattedAddress()) + s.AssertTxSuccess(resp) + + address, err := query.CounterPartyPayee(ctx, chainB, chainBRelayerWallet.FormattedAddress(), channelA.Counterparty.ChannelID) + s.Require().NoError(err) + s.Require().Equal(chainARelayerWallet.FormattedAddress(), address) + }) + + // send the native chainA denom and also the ibc token from chainB + denoms := []string{chainAIBCToken.IBCDenom(), chainADenom} + var transferCoins []sdk.Coin + for _, denom := range denoms { + transferCoins = append(transferCoins, testvalues.DefaultTransferAmount(denom)) + } + + t.Run("send incentivized transfer packet to chain B with native token from chain A and non-native IBC token from chainB", func(t *testing.T) { + // before adding fees for the packet, there should not be incentivized packets + packets, err := query.IncentivizedPacketsForChannel(ctx, chainA, channelA.PortID, channelA.ChannelID) + s.Require().NoError(err) + s.Require().Empty(packets) + + msgPayPacketFee := feetypes.NewMsgPayPacketFee(testFee, channelA.PortID, channelA.ChannelID, chainAWallet.FormattedAddress(), nil) + msgTransfer := testsuite.GetMsgTransfer( + channelA.PortID, + channelA.ChannelID, + transfertypes.V2, + transferCoins, + chainAWallet.FormattedAddress(), + chainBWallet.FormattedAddress(), + s.GetTimeoutHeight(ctx, chainB), + 0, + "", + nil, + ) + resp := s.BroadcastMessages(ctx, chainA, chainAWallet, msgPayPacketFee, msgTransfer) + s.AssertTxSuccess(resp) + }) + + t.Run("packets are relayed", func(t *testing.T) { + s.AssertPacketRelayed(ctx, chainA, channelA.PortID, channelA.ChannelID, 1) + + t.Run("chain B native denom", func(t *testing.T) { + actualBalance, err := s.GetChainBNativeBalance(ctx, chainBWallet) + s.Require().NoError(err) + + expected := testvalues.StartingTokenAmount + s.Require().Equal(expected, actualBalance) + }) + + t.Run("chain A IBC denom", func(t *testing.T) { + actualBalance, err := query.Balance(ctx, chainB, chainBAddress, chainBIBCToken.IBCDenom()) + s.Require().NoError(err) + + expected := testvalues.IBCTransferAmount + s.Require().Equal(expected, actualBalance.Int64()) + }) + }) + + 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, chainARelayerWallet) + s.Require().NoError(err) + + expected := relayerAStartingBalance + testFee.AckFee.AmountOf(chainADenom).Int64() + testFee.RecvFee.AmountOf(chainADenom).Int64() + s.Require().Equal(expected, actualBalance) + }) + + t.Run("tokens are un-escrowed", func(t *testing.T) { + actualTotalEscrow, err := query.TotalEscrowForDenom(ctx, chainB, chainADenom) + s.Require().NoError(err) + s.Require().Equal(sdk.NewCoin(chainADenom, sdkmath.NewInt(0)), actualTotalEscrow) // total escrow is zero because tokens have come back + }) +} diff --git a/e2e/tests/transfer/upgradesv2_test.go b/e2e/tests/transfer/upgradesv2_test.go new file mode 100644 index 00000000000..7b2b53559d8 --- /dev/null +++ b/e2e/tests/transfer/upgradesv2_test.go @@ -0,0 +1,508 @@ +//go:build !test_e2e + +package transfer + +import ( + "context" + "sync" + "testing" + + "github.com/strangelove-ventures/interchaintest/v8/ibc" + test "github.com/strangelove-ventures/interchaintest/v8/testutil" + testifysuite "github.com/stretchr/testify/suite" + + sdkmath "cosmossdk.io/math" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/cosmos/ibc-go/e2e/testsuite" + "github.com/cosmos/ibc-go/e2e/testsuite/query" + "github.com/cosmos/ibc-go/e2e/testvalues" + feetypes "github.com/cosmos/ibc-go/v9/modules/apps/29-fee/types" + transfertypes "github.com/cosmos/ibc-go/v9/modules/apps/transfer/types" + channeltypes "github.com/cosmos/ibc-go/v9/modules/core/04-channel/types" +) + +func TestTransferChannelUpgradesTestSuite(t *testing.T) { + testifysuite.Run(t, new(TransferChannelUpgradesTestSuite)) +} + +type TransferChannelUpgradesTestSuite struct { + testsuite.E2ETestSuite +} + +func (s *TransferChannelUpgradesTestSuite) SetupChannelUpgradesPath(testName string) { + s.CreatePaths(ibc.DefaultClientOpts(), s.TransferChannelOptions(), testName) +} + +// TestChannelUpgrade_WithFeeMiddleware_Succeeds tests upgrading a transfer channel to wire up fee middleware +func (s *TransferChannelUpgradesTestSuite) TestChannelUpgrade_WithFeeMiddleware_Succeeds() { + t := s.T() + ctx := context.TODO() + + testName := t.Name() + s.SetupChannelUpgradesPath(testName) + + relayer, channelA := s.GetRelayerForTest(testName), s.GetChainAChannelForTest(testName) + + channelB := channelA.Counterparty + + chainA, chainB := s.GetChains() + chainADenom := chainA.Config().Denom + chainBDenom := chainB.Config().Denom + chainAIBCToken := testsuite.GetIBCToken(chainBDenom, channelA.PortID, channelA.ChannelID) + chainBIBCToken := testsuite.GetIBCToken(chainADenom, channelB.PortID, channelB.ChannelID) + + chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + chainAAddress := chainAWallet.FormattedAddress() + + chainBWallet := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount) + chainBAddress := chainBWallet.FormattedAddress() + + var ( + chainARelayerWallet, chainBRelayerWallet ibc.Wallet + relayerAStartingBalance int64 + testFee = testvalues.DefaultFee(chainADenom) + ) + + s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB), "failed to wait for blocks") + + // trying to create some inflight packets, although they might get relayed before the upgrade starts + t.Run("create inflight transfer packets between chain A and chain B", func(t *testing.T) { + chainBWalletAmount := ibc.WalletAmount{ + Address: chainBWallet.FormattedAddress(), // destination address + Denom: chainADenom, + Amount: sdkmath.NewInt(testvalues.IBCTransferAmount), + } + + transferTxResp, err := chainA.SendIBCTransfer(ctx, channelA.ChannelID, chainAWallet.KeyName(), chainBWalletAmount, ibc.TransferOptions{}) + s.Require().NoError(err) + s.Require().NoError(transferTxResp.Validate(), "chain-a ibc transfer tx is invalid") + + chainAwalletAmount := ibc.WalletAmount{ + Address: chainAWallet.FormattedAddress(), // destination address + Denom: chainBDenom, + Amount: sdkmath.NewInt(testvalues.IBCTransferAmount), + } + + transferTxResp, err = chainB.SendIBCTransfer(ctx, channelB.ChannelID, chainBWallet.KeyName(), chainAwalletAmount, ibc.TransferOptions{}) + s.Require().NoError(err) + s.Require().NoError(transferTxResp.Validate(), "chain-b ibc transfer tx is invalid") + }) + + t.Run("execute gov proposal to initiate channel upgrade", func(t *testing.T) { + chA, err := query.Channel(ctx, chainA, channelA.PortID, channelA.ChannelID) + s.Require().NoError(err) + + s.InitiateChannelUpgrade(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, s.CreateUpgradeFields(chA)) + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer, testName) + }) + + s.Require().NoError(test.WaitForBlocks(ctx, 10, chainA, chainB), "failed to wait for blocks") + + t.Run("packets are relayed between chain A and chain B", func(t *testing.T) { + // packet from chain A to chain B + s.AssertPacketRelayed(ctx, chainA, channelA.PortID, channelA.ChannelID, 1) + actualBalance, err := query.Balance(ctx, chainB, chainBAddress, chainBIBCToken.IBCDenom()) + s.Require().NoError(err) + expected := testvalues.IBCTransferAmount + s.Require().Equal(expected, actualBalance.Int64()) + + // packet from chain B to chain A + s.AssertPacketRelayed(ctx, chainB, channelB.PortID, channelB.ChannelID, 1) + actualBalance, err = query.Balance(ctx, chainA, chainAAddress, chainAIBCToken.IBCDenom()) + s.Require().NoError(err) + expected = testvalues.IBCTransferAmount + s.Require().Equal(expected, actualBalance.Int64()) + }) + + t.Run("verify channel A upgraded and is fee enabled", func(t *testing.T) { + channel, err := query.Channel(ctx, chainA, channelA.PortID, channelA.ChannelID) + s.Require().NoError(err) + + // check the channel version include the fee version + version, err := feetypes.MetadataFromVersion(channel.Version) + s.Require().NoError(err) + s.Require().Equal(feetypes.Version, version.FeeVersion, "the channel version did not include ics29") + + // extra check + feeEnabled, err := query.FeeEnabledChannel(ctx, chainA, channelA.PortID, channelA.ChannelID) + s.Require().NoError(err) + s.Require().Equal(true, feeEnabled) + }) + + t.Run("verify channel B upgraded and is fee enabled", func(t *testing.T) { + channel, err := query.Channel(ctx, chainB, channelB.PortID, channelB.ChannelID) + s.Require().NoError(err) + + // check the channel version include the fee version + version, err := feetypes.MetadataFromVersion(channel.Version) + s.Require().NoError(err) + s.Require().Equal(feetypes.Version, version.FeeVersion, "the channel version did not include ics29") + + // extra check + feeEnabled, err := query.FeeEnabledChannel(ctx, chainB, channelB.PortID, channelB.ChannelID) + s.Require().NoError(err) + s.Require().Equal(true, feeEnabled) + }) + + t.Run("prune packet acknowledgements", func(t *testing.T) { + // there should be one ack for the packet that we sent before the upgrade + acks, err := query.PacketAcknowledgements(ctx, chainA, channelA.PortID, channelA.ChannelID, []uint64{}) + s.Require().NoError(err) + s.Require().Len(acks, 1) + s.Require().Equal(uint64(1), acks[0].Sequence) + + pruneAcksTxResponse := s.PruneAcknowledgements(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, uint64(1)) + s.AssertTxSuccess(pruneAcksTxResponse) + + // after pruning there should not be any acks + acks, err = query.PacketAcknowledgements(ctx, chainA, channelA.PortID, channelA.ChannelID, []uint64{}) + s.Require().NoError(err) + s.Require().Empty(acks) + }) + + t.Run("stop relayer", func(t *testing.T) { + s.StopRelayer(ctx, relayer) + }) + + t.Run("recover relayer wallets", func(t *testing.T) { + _, _, err := s.RecoverRelayerWallets(ctx, relayer, testName) + s.Require().NoError(err) + + chainARelayerWallet, chainBRelayerWallet, err = s.GetRelayerWallets(relayer) + s.Require().NoError(err) + + relayerAStartingBalance, err = s.GetChainANativeBalance(ctx, chainARelayerWallet) + s.Require().NoError(err) + t.Logf("relayer A user starting with balance: %d", relayerAStartingBalance) + }) + + s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB), "failed to wait for blocks") + + t.Run("register and verify counterparty payee", func(t *testing.T) { + _, chainBRelayerUser := s.GetRelayerUsers(ctx, testName) + resp := s.RegisterCounterPartyPayee(ctx, chainB, chainBRelayerUser, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, chainBRelayerWallet.FormattedAddress(), chainARelayerWallet.FormattedAddress()) + s.AssertTxSuccess(resp) + + address, err := query.CounterPartyPayee(ctx, chainB, chainBRelayerWallet.FormattedAddress(), channelA.Counterparty.ChannelID) + s.Require().NoError(err) + s.Require().Equal(chainARelayerWallet.FormattedAddress(), address) + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer, testName) + }) + + t.Run("send incentivized transfer packet", func(t *testing.T) { + // before adding fees for the packet, there should not be incentivized packets + packets, err := query.IncentivizedPacketsForChannel(ctx, chainA, channelA.PortID, channelA.ChannelID) + s.Require().NoError(err) + s.Require().Empty(packets) + + transferAmount := testvalues.DefaultTransferAmount(chainA.Config().Denom) + + msgPayPacketFee := feetypes.NewMsgPayPacketFee(testFee, channelA.PortID, channelA.ChannelID, chainAWallet.FormattedAddress(), nil) + msgTransfer := testsuite.GetMsgTransfer( + channelA.PortID, + channelA.ChannelID, + channelA.Version, // upgrade adds fee middleware, but keeps transfer version + sdk.NewCoins(transferAmount), + chainAWallet.FormattedAddress(), + chainBWallet.FormattedAddress(), + s.GetTimeoutHeight(ctx, chainB), + 0, + "", + nil, + ) + resp := s.BroadcastMessages(ctx, chainA, chainAWallet, msgPayPacketFee, msgTransfer) + s.AssertTxSuccess(resp) + }) + + t.Run("packets are relayed", func(t *testing.T) { + packets, err := query.IncentivizedPacketsForChannel(ctx, chainA, channelA.PortID, channelA.ChannelID) + s.Require().NoError(err) + s.Require().Empty(packets) + }) + + t.Run("tokens are received by walletB", func(t *testing.T) { + actualBalance, err := query.Balance(ctx, chainB, chainBAddress, chainBIBCToken.IBCDenom()) + s.Require().NoError(err) + + // walletB has received two IBC transfers of value testvalues.IBCTransferAmount since the start of the test. + expected := 2 * testvalues.IBCTransferAmount + s.Require().Equal(expected, actualBalance.Int64()) + }) + + 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. + // walletA has done two IBC transfers of value testvalues.IBCTransferAmount since the start of the test. + expected := testvalues.StartingTokenAmount - (2 * 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, chainARelayerWallet) + s.Require().NoError(err) + + expected := relayerAStartingBalance + testFee.AckFee.AmountOf(chainADenom).Int64() + testFee.RecvFee.AmountOf(chainADenom).Int64() + s.Require().Equal(expected, actualBalance) + }) +} + +// TestChannelDowngrade_WithICS20v1_Succeeds tests downgrading a transfer channel from ICS20 v2 to ICS20 v1. +func (s *TransferChannelUpgradesTestSuite) TestChannelDowngrade_WithICS20v1_Succeeds() { + t := s.T() + ctx := context.TODO() + + testName := t.Name() + s.SetupChannelUpgradesPath(testName) + + relayer, channelA := s.GetRelayerForTest(testName), s.GetChainAChannelForTest(testName) + + channelB := channelA.Counterparty + chainA, chainB := s.GetChains() + + chainADenom := chainA.Config().Denom + chainBIBCToken := testsuite.GetIBCToken(chainADenom, channelB.PortID, channelB.ChannelID) + + chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + chainBWallet := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount) + chainBAddress := chainBWallet.FormattedAddress() + + s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB), "failed to wait for blocks") + + var ( + err error + channel channeltypes.Channel + ) + + t.Run("verify transfer version of channel A is ics20-2", func(t *testing.T) { + channel, err = query.Channel(ctx, chainA, channelA.PortID, channelA.ChannelID) + s.Require().NoError(err) + s.Require().Equal(transfertypes.V2, channel.Version, "the channel version is not ics20-2") + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer, testName) + }) + + t.Run("execute gov proposal to initiate channel upgrade", func(t *testing.T) { + upgradeFields := channeltypes.NewUpgradeFields(channel.Ordering, channel.ConnectionHops, transfertypes.V1) + s.InitiateChannelUpgrade(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, upgradeFields) + }) + + s.Require().NoError(test.WaitForBlocks(ctx, 10, chainA, chainB), "failed to wait for blocks") + + t.Run("verify channel A downgraded and transfer version is ics20-1", func(t *testing.T) { + channel, err = query.Channel(ctx, chainA, channelA.PortID, channelA.ChannelID) + s.Require().NoError(err) + s.Require().Equal(transfertypes.V1, channel.Version, "the channel version is not ics20-1") + }) + + t.Run("verify channel B downgraded and transfer version is ics20-1", func(t *testing.T) { + channel, err = query.Channel(ctx, chainB, channelB.PortID, channelB.ChannelID) + s.Require().NoError(err) + s.Require().Equal(transfertypes.V1, channel.Version, "the channel version is not ics20-1") + }) + + t.Run("native IBC token transfer from chainA to chainB, sender is source of tokens", func(t *testing.T) { + chainBWalletAmount := ibc.WalletAmount{ + Address: chainBWallet.FormattedAddress(), // destination address + Denom: chainA.Config().Denom, + Amount: sdkmath.NewInt(testvalues.IBCTransferAmount), + } + + transferTxResp, err := chainA.SendIBCTransfer(ctx, channelA.ChannelID, chainAWallet.KeyName(), chainBWalletAmount, ibc.TransferOptions{}) + s.Require().NoError(err) + s.Require().NoError(transferTxResp.Validate(), "chain-a ibc transfer tx is invalid") + }) + + s.Require().NoError(test.WaitForBlocks(ctx, 5, chainA, chainB), "failed to wait for blocks") + + 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) + + actualTotalEscrow, err := query.TotalEscrowForDenom(ctx, chainA, chainADenom) + s.Require().NoError(err) + + expectedTotalEscrow := sdk.NewCoin(chainADenom, sdkmath.NewInt(testvalues.IBCTransferAmount)) + s.Require().Equal(expectedTotalEscrow, actualTotalEscrow) + }) + + t.Run("packets are relayed", func(t *testing.T) { + s.AssertPacketRelayed(ctx, chainA, channelA.PortID, channelA.ChannelID, 1) + + actualBalance, err := query.Balance(ctx, chainB, chainBAddress, chainBIBCToken.IBCDenom()) + s.Require().NoError(err) + + expected := testvalues.IBCTransferAmount + s.Require().Equal(expected, actualBalance.Int64()) + }) +} + +// TestChannelUpgrade_WithFeeMiddleware_CrossingHello_Succeeds tests upgrading a transfer channel to wire up fee middleware under crossing hello +func (s *TransferChannelUpgradesTestSuite) TestChannelUpgrade_WithFeeMiddleware_CrossingHello_Succeeds() { + t := s.T() + ctx := context.TODO() + + testName := t.Name() + s.SetupChannelUpgradesPath(testName) + + relayer, channelA := s.GetRelayerForTest(testName), s.GetChainAChannelForTest(testName) + + channelB := channelA.Counterparty + + 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") + + // trying to create some inflight packets, although they might get relayed before the upgrade starts + t.Run("create inflight transfer packets between chain A and chain B", func(t *testing.T) { + transferTxResp := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, testvalues.DefaultTransferCoins(chainADenom), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "", nil) + s.AssertTxSuccess(transferTxResp) + }) + + t.Run("execute gov proposals to initiate channel upgrade on chain A and chain B", func(t *testing.T) { + var wg sync.WaitGroup + + wg.Add(1) + go func() { + defer wg.Done() + chA, err := query.Channel(ctx, chainA, channelA.PortID, channelA.ChannelID) + s.Require().NoError(err) + s.InitiateChannelUpgrade(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, s.CreateUpgradeFields(chA)) + }() + + wg.Add(1) + go func() { + defer wg.Done() + chB, err := query.Channel(ctx, chainB, channelB.PortID, channelB.ChannelID) + s.Require().NoError(err) + s.InitiateChannelUpgrade(ctx, chainB, chainBWallet, channelB.PortID, channelB.ChannelID, s.CreateUpgradeFields(chB)) + }() + + wg.Wait() + }) + + s.Require().NoError(test.WaitForBlocks(ctx, 10, chainA, chainB), "failed to wait for blocks") + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer, testName) + }) + + t.Run("packets are relayed between chain A and chain B", func(t *testing.T) { + // packet from chain A to chain B + s.AssertPacketRelayed(ctx, chainA, channelA.PortID, channelA.ChannelID, 1) + }) + + t.Run("verify channel A upgraded and is fee enabled", func(t *testing.T) { + channel, err := query.Channel(ctx, chainA, channelA.PortID, channelA.ChannelID) + s.Require().NoError(err) + + // check the channel version include the fee version + version, err := feetypes.MetadataFromVersion(channel.Version) + s.Require().NoError(err) + s.Require().Equal(feetypes.Version, version.FeeVersion, "the channel version did not include ics29") + + // extra check + feeEnabled, err := query.FeeEnabledChannel(ctx, chainA, channelA.PortID, channelA.ChannelID) + s.Require().NoError(err) + s.Require().Equal(true, feeEnabled) + }) + + t.Run("verify channel B upgraded and is fee enabled", func(t *testing.T) { + channel, err := query.Channel(ctx, chainB, channelB.PortID, channelB.ChannelID) + s.Require().NoError(err) + + // check the channel version include the fee version + version, err := feetypes.MetadataFromVersion(channel.Version) + s.Require().NoError(err) + s.Require().Equal(feetypes.Version, version.FeeVersion, "the channel version did not include ics29") + + // extra check + feeEnabled, err := query.FeeEnabledChannel(ctx, chainB, channelB.PortID, channelB.ChannelID) + s.Require().NoError(err) + s.Require().Equal(true, feeEnabled) + }) +} + +// TestChannelUpgrade_WithFeeMiddleware_FailsWithTimeoutOnAck tests upgrading a transfer channel to wire up fee middleware but fails on ACK because of timeout +func (s *TransferChannelUpgradesTestSuite) TestChannelUpgrade_WithFeeMiddleware_FailsWithTimeoutOnAck() { + t := s.T() + ctx := context.TODO() + + testName := t.Name() + s.SetupChannelUpgradesPath(testName) + + relayer, channelA := s.GetRelayerForTest(testName), s.GetChainAChannelForTest(testName) + + chainA, chainB := s.GetChains() + + channelB := channelA.Counterparty + + chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + chainBWallet := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount) + + s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB), "failed to wait for blocks") + + t.Run("execute gov proposal to set upgrade timeout", func(t *testing.T) { + s.SetUpgradeTimeoutParam(ctx, chainB, chainBWallet) + }) + + t.Run("execute gov proposal to initiate channel upgrade", func(t *testing.T) { + chA, err := query.Channel(ctx, chainA, channelA.PortID, channelA.ChannelID) + s.Require().NoError(err) + + s.InitiateChannelUpgrade(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, s.CreateUpgradeFields(chA)) + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer, testName) + }) + + s.Require().NoError(test.WaitForBlocks(ctx, 10, chainA, chainB), "failed to wait for blocks") + + t.Run("verify channel A did not upgrade", func(t *testing.T) { + channel, err := query.Channel(ctx, chainA, channelA.PortID, channelA.ChannelID) + s.Require().NoError(err) + + s.Require().Equal(channeltypes.OPEN, channel.State, "the channel state is not OPEN") + s.Require().Equal(channelA.Version, channel.Version, "the channel version is not "+channelA.Version) + + errorReceipt, err := query.UpgradeError(ctx, chainA, channelA.PortID, channelA.ChannelID) + s.Require().NoError(err) + s.Require().Equal(uint64(1), errorReceipt.Sequence) + s.Require().Contains(errorReceipt.Message, "restored channel to pre-upgrade state") + }) + + t.Run("verify channel B did not upgrade", func(t *testing.T) { + channel, err := query.Channel(ctx, chainB, channelB.PortID, channelB.ChannelID) + s.Require().NoError(err) + + s.Require().Equal(channeltypes.OPEN, channel.State, "the channel state is not OPEN") + s.Require().Equal(channelA.Version, channel.Version, "the channel version is not "+channelA.Version) + + errorReceipt, err := query.UpgradeError(ctx, chainB, channelB.PortID, channelB.ChannelID) + s.Require().NoError(err) + s.Require().Equal(uint64(1), errorReceipt.Sequence) + s.Require().Contains(errorReceipt.Message, "restored channel to pre-upgrade state") + }) +} diff --git a/e2e/tests/upgrades/genesis_test.go b/e2e/tests/upgrades/genesis_test.go new file mode 100644 index 00000000000..bb36879774e --- /dev/null +++ b/e2e/tests/upgrades/genesis_test.go @@ -0,0 +1,261 @@ +//go:build !test_e2e + +package upgrades + +import ( + "context" + "testing" + "time" + + "github.com/cosmos/gogoproto/proto" + "github.com/strangelove-ventures/interchaintest/v8" + "github.com/strangelove-ventures/interchaintest/v8/chain/cosmos" + "github.com/strangelove-ventures/interchaintest/v8/ibc" + test "github.com/strangelove-ventures/interchaintest/v8/testutil" + "github.com/stretchr/testify/suite" + "go.uber.org/zap" + + sdkmath "cosmossdk.io/math" + + sdk "github.com/cosmos/cosmos-sdk/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + + "github.com/cosmos/ibc-go/e2e/testsuite" + "github.com/cosmos/ibc-go/e2e/testsuite/query" + "github.com/cosmos/ibc-go/e2e/testvalues" + controllertypes "github.com/cosmos/ibc-go/v9/modules/apps/27-interchain-accounts/controller/types" + icatypes "github.com/cosmos/ibc-go/v9/modules/apps/27-interchain-accounts/types" + channeltypes "github.com/cosmos/ibc-go/v9/modules/core/04-channel/types" + ibctesting "github.com/cosmos/ibc-go/v9/testing" +) + +func TestGenesisTestSuite(t *testing.T) { + suite.Run(t, new(GenesisTestSuite)) +} + +type GenesisTestSuite struct { + testsuite.E2ETestSuite +} + +// TODO: this configuration was originally being applied to `GetChains` in the test body, but it is not +// actually being propagated correctly. If we want to apply the configuration, we can uncomment this code +// however the test actually fails when this is done. +// func (s *GenesisTestSuite) SetupSuite() { +// configFileOverrides := make(map[string]any) +// appTomlOverrides := make(test.Toml) +// +// appTomlOverrides["halt-height"] = haltHeight +// configFileOverrides["config/app.toml"] = appTomlOverrides +// +// s.SetupChains(context.TODO(), nil, func(options *testsuite.ChainOptions) { +// // create chains with specified chain configuration options +// options.ChainSpecs[0].ConfigFileOverrides = configFileOverrides +// }) +// } + +func (s *GenesisTestSuite) TestIBCGenesis() { + t := s.T() + + haltHeight := int64(100) + + chainA, chainB := s.GetChains() + + ctx := context.Background() + testName := t.Name() + + relayer := s.CreateDefaultPaths(testName) + channelA := s.GetChainAChannelForTest(testName) + + 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() + + s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB), "failed to wait for blocks") + + t.Run("ics20: native IBC token transfer from chainA to chainB, sender is source of tokens", func(t *testing.T) { + transferTxResp := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, testvalues.DefaultTransferCoins(chainADenom), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "", nil) + s.AssertTxSuccess(transferTxResp) + }) + + t.Run("ics20: 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) + }) + + // 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("ics27: broadcast MsgRegisterInterchainAccount", func(t *testing.T) { + // explicitly set the version string because we don't want to use incentivized channels. + version := icatypes.NewDefaultMetadataString(ibctesting.FirstConnectionID, ibctesting.FirstConnectionID) + msgRegisterAccount := controllertypes.NewMsgRegisterInterchainAccount(ibctesting.FirstConnectionID, controllerAddress, version, channeltypes.ORDERED) + + txResp := s.BroadcastMessages(ctx, chainA, controllerAccount, msgRegisterAccount) + s.AssertTxSuccess(txResp) + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer, testName) + }) + + t.Run("ics20: packets are relayed", func(t *testing.T) { + s.AssertPacketRelayed(ctx, chainA, channelA.PortID, channelA.ChannelID, 1) + + actualBalance, err := query.Balance(ctx, chainB, chainBAddress, chainBIBCToken.IBCDenom()) + + s.Require().NoError(err) + + expected := testvalues.IBCTransferAmount + s.Require().Equal(expected, actualBalance.Int64()) + }) + + s.Require().NoError(test.WaitForBlocks(ctx, 10, chainA, chainB), "failed to wait for blocks") + + t.Run("ics27: verify interchain account", func(t *testing.T) { + res, err := query.GRPCQuery[controllertypes.QueryInterchainAccountResponse](ctx, chainA, &controllertypes.QueryInterchainAccountRequest{ + Owner: controllerAddress, + ConnectionId: ibctesting.FirstConnectionID, + }) + s.Require().NoError(err) + s.Require().NotZero(len(res.Address)) + + hostAccount = res.Address + s.Require().NotEmpty(hostAccount) + + channels, err := relayer.GetChannels(ctx, s.GetRelayerExecReporter(), chainA.Config().ChainID) + s.Require().NoError(err) + s.Require().Equal(len(channels), 2) + }) + + s.Require().NoError(test.WaitForBlocks(ctx, 10, chainA, chainB), "failed to wait for blocks") + + t.Run("Halt chain and export genesis", func(t *testing.T) { + s.HaltChainAndExportGenesis(ctx, chainA.(*cosmos.CosmosChain), haltHeight) + }) + + t.Run("ics20: native IBC token transfer from chainA to chainB, sender is source of tokens", func(t *testing.T) { + transferTxResp := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, testvalues.DefaultTransferCoins(chainADenom), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "", nil) + s.AssertTxSuccess(transferTxResp) + }) + + t.Run("ics20: tokens are escrowed", func(t *testing.T) { + actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet) + s.Require().NoError(err) + + expected := testvalues.StartingTokenAmount - 2*testvalues.IBCTransferAmount + s.Require().Equal(expected, actualBalance) + }) + + t.Run("ics27: 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: sdkmath.NewInt(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}, icatypes.EncodingProtobuf) + 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 := s.BroadcastMessages( + ctx, + chainA, + controllerAccount, + msgSendTx, + ) + + s.AssertTxSuccess(resp) + + s.Require().NoError(test.WaitForBlocks(ctx, 10, chainA, chainB)) + }) + }) + + s.Require().NoError(test.WaitForBlocks(ctx, 5, chainA, chainB), "failed to wait for blocks") +} + +func (s *GenesisTestSuite) HaltChainAndExportGenesis(ctx context.Context, chain *cosmos.CosmosChain, haltHeight int64) { + timeoutCtx, timeoutCtxCancel := context.WithTimeout(ctx, time.Minute*2) + defer timeoutCtxCancel() + + err := test.WaitForBlocks(timeoutCtx, int(haltHeight), chain) + s.Require().Error(err, "chain did not halt at halt height") + + err = chain.StopAllNodes(ctx) + s.Require().NoError(err, "error stopping node(s)") + + state, err := chain.ExportState(ctx, haltHeight) + s.Require().NoError(err) + + appTomlOverrides := make(test.Toml) + + appTomlOverrides["halt-height"] = 0 + + for _, node := range chain.Nodes() { + err := node.OverwriteGenesisFile(ctx, []byte(state)) + s.Require().NoError(err) + } + + for _, node := range chain.Nodes() { + err := test.ModifyTomlConfigFile( + ctx, + zap.NewExample(), + node.DockerClient, + node.TestName, + node.VolumeName, + "config/app.toml", + appTomlOverrides, + ) + s.Require().NoError(err) + + _, _, err = node.ExecBin(ctx, "comet", "unsafe-reset-all") + s.Require().NoError(err) + } + + err = chain.StartAllNodes(ctx) + s.Require().NoError(err) + + 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 halt") + + height, err := chain.Height(ctx) + s.Require().NoError(err, "error fetching height after halt") + + s.Require().Greater(height, haltHeight, "height did not increment after halt") +} diff --git a/e2e/tests/upgrades/upgrade_test.go b/e2e/tests/upgrades/upgrade_test.go new file mode 100644 index 00000000000..b2fc69cff28 --- /dev/null +++ b/e2e/tests/upgrades/upgrade_test.go @@ -0,0 +1,1393 @@ +//go:build !test_e2e + +package upgrades + +import ( + "context" + "fmt" + "sync" + "testing" + "time" + + "github.com/cosmos/gogoproto/proto" + interchaintest "github.com/strangelove-ventures/interchaintest/v8" + "github.com/strangelove-ventures/interchaintest/v8/chain/cosmos" + "github.com/strangelove-ventures/interchaintest/v8/ibc" + test "github.com/strangelove-ventures/interchaintest/v8/testutil" + testifysuite "github.com/stretchr/testify/suite" + + sdkmath "cosmossdk.io/math" + upgradetypes "cosmossdk.io/x/upgrade/types" + + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + + e2erelayer "github.com/cosmos/ibc-go/e2e/relayer" + "github.com/cosmos/ibc-go/e2e/testsuite" + "github.com/cosmos/ibc-go/e2e/testsuite/query" + "github.com/cosmos/ibc-go/e2e/testvalues" + feetypes "github.com/cosmos/ibc-go/v9/modules/apps/29-fee/types" + transfertypes "github.com/cosmos/ibc-go/v9/modules/apps/transfer/types" + v7migrations "github.com/cosmos/ibc-go/v9/modules/core/02-client/migrations/v7" + clienttypes "github.com/cosmos/ibc-go/v9/modules/core/02-client/types" + connectiontypes "github.com/cosmos/ibc-go/v9/modules/core/03-connection/types" + channeltypes "github.com/cosmos/ibc-go/v9/modules/core/04-channel/types" + "github.com/cosmos/ibc-go/v9/modules/core/exported" + solomachine "github.com/cosmos/ibc-go/v9/modules/light-clients/06-solomachine" + localhost "github.com/cosmos/ibc-go/v9/modules/light-clients/09-localhost" + ibctesting "github.com/cosmos/ibc-go/v9/testing" +) + +const ( + haltHeightOffset = int64(30) + blocksAfterUpgrade = uint64(10) +) + +func TestUpgradeTestSuite(t *testing.T) { + testCfg := testsuite.LoadConfig() + if testCfg.UpgradeConfig.Tag == "" || testCfg.UpgradeConfig.PlanName == "" { + t.Fatalf("%s and %s must be set when running an upgrade test", testsuite.ChainUpgradeTagEnv, testsuite.ChainUpgradePlanEnv) + } + + testifysuite.Run(t, new(UpgradeTestSuite)) +} + +type UpgradeTestSuite struct { + testsuite.E2ETestSuite +} + +func (s *UpgradeTestSuite) CreateUpgradeTestPath(testName string) (ibc.Relayer, ibc.ChannelOutput) { + return s.CreatePaths(ibc.DefaultClientOpts(), s.TransferChannelOptions(), testName), s.GetChainAChannelForTest(testName) +} + +// 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) { + height, err := chain.GetNode().Height(ctx) + s.Require().NoError(err, "error fetching height before upgrade") + + haltHeight := height + haltHeightOffset + plan := upgradetypes.Plan{ + Name: planName, + Height: haltHeight, + Info: fmt.Sprintf("upgrade version test from %s to %s", currentVersion, upgradeVersion), + } + + if testvalues.GovV1MessagesFeatureReleases.IsSupported(chain.Config().Images[0].Version) { + msgSoftwareUpgrade := &upgradetypes.MsgSoftwareUpgrade{ + Plan: plan, + Authority: authtypes.NewModuleAddress(govtypes.ModuleName).String(), + } + + s.ExecuteAndPassGovV1Proposal(ctx, msgSoftwareUpgrade, chain, wallet) + } else { + upgradeProposal := upgradetypes.NewSoftwareUpgradeProposal(fmt.Sprintf("upgrade from %s to %s", currentVersion, upgradeVersion), "upgrade chain E2E test", plan) + s.ExecuteAndPassGovV1Beta1Proposal(ctx, chain, wallet, upgradeProposal) + } + + err = test.WaitForCondition(time.Minute*2, time.Second*2, func() (bool, error) { + status, err := chain.GetNode().Client.Status(ctx) + if err != nil { + return false, err + } + return status.SyncInfo.LatestBlockHeight >= haltHeight, nil + }) + s.Require().NoError(err, "failed to wait for chain to halt") + + var allNodes []test.ChainHeighter + for _, node := range chain.Nodes() { + allNodes = append(allNodes, node) + } + + err = test.WaitForInSync(ctx, chain, allNodes...) + s.Require().NoError(err, "error waiting for node(s) to sync") + + err = chain.StopAllNodes(ctx) + s.Require().NoError(err, "error stopping node(s)") + + repository := chain.Nodes()[0].Image.Repository + chain.UpgradeVersion(ctx, s.DockerClient, repository, upgradeVersion) + + err = chain.StartAllNodes(ctx) + s.Require().NoError(err, "error starting upgraded node(s)") + + 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 := testsuite.LoadConfig() + + ctx := context.Background() + testName := t.Name() + relayer, channelA := s.CreateUpgradeTestPath(testName) + + 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 := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, testvalues.DefaultTransferCoins(chainADenom), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "", nil) + s.AssertTxSuccess(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, testName) + }) + + t.Run("packets are relayed", func(t *testing.T) { + s.AssertPacketRelayed(ctx, chainA, channelA.PortID, channelA.ChannelID, 1) + + actualBalance, err := query.Balance(ctx, chainB, chainBAddress, chainBIBCToken.IBCDenom()) + + s.Require().NoError(err) + + expected := testvalues.IBCTransferAmount + s.Require().Equal(expected, actualBalance.Int64()) + }) + + 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.(*cosmos.CosmosChain), chainAUpgradeProposalWallet, testCfg.UpgradeConfig.PlanName, testCfg.ChainConfigs[0].Tag, testCfg.UpgradeConfig.Tag) + }) + + t.Run("restart relayer", func(t *testing.T) { + s.StopRelayer(ctx, relayer) + s.StartRelayer(relayer, testName) + }) + + t.Run("native IBC token transfer from chainA to chainB, sender is source of tokens", func(t *testing.T) { + transferTxResp := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, testvalues.DefaultTransferCoins(chainADenom), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "", nil) + s.AssertTxSuccess(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 := query.Balance(ctx, chainB, chainBAddress, chainBIBCToken.IBCDenom()) + + s.Require().NoError(err) + + expected := testvalues.IBCTransferAmount * 2 + s.Require().Equal(expected, actualBalance.Int64()) + }) + + 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 := s.Transfer(ctx, chainB, chainBWallet, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, testvalues.DefaultTransferCoins(chainBDenom), chainBAddress, chainAAddress, s.GetTimeoutHeight(ctx, chainA), 0, "", nil) + s.AssertTxSuccess(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 := query.Balance(ctx, chainA, chainAAddress, chainAIBCToken.IBCDenom()) + + s.Require().NoError(err) + + expected := testvalues.IBCTransferAmount + s.Require().Equal(expected, actualBalance.Int64()) + }) + }) +} + +func (s *UpgradeTestSuite) TestChainUpgrade() { + t := s.T() + + ctx := context.Background() + + testName := t.Name() + s.CreateUpgradeTestPath(testName) + + // TODO(chatton): this test is still creating a relayer and a channel, but it is not using them. + chain := s.GetAllChains()[0] + + 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: sdkmath.NewInt(testvalues.StartingTokenAmount), + Denom: chain.Config().Denom, + }) + s.Require().NoError(err) + }) + + t.Run("verify tokens sent", func(t *testing.T) { + balance, err := query.Balance(ctx, chain, 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 := testsuite.LoadConfig() + proposerWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + + s.UpgradeChain(ctx, chain.(*cosmos.CosmosChain), proposerWallet, testCfg.UpgradeConfig.PlanName, testCfg.ChainConfigs[0].Tag, testCfg.UpgradeConfig.Tag) + }) + + t.Run("send funds to test wallet", func(t *testing.T) { + err := chain.SendFunds(ctx, interchaintest.FaucetAccountKeyName, ibc.WalletAmount{ + Address: userWalletAddr, + Amount: sdkmath.NewInt(testvalues.StartingTokenAmount), + Denom: chain.Config().Denom, + }) + s.Require().NoError(err) + }) + + t.Run("verify tokens sent", func(t *testing.T) { + balance, err := query.Balance(ctx, chain, userWalletAddr, chain.Config().Denom) + s.Require().NoError(err) + + expected := testvalues.StartingTokenAmount * 3 + 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 := testsuite.LoadConfig() + + ctx := context.Background() + testName := t.Name() + + relayer, channelA := s.CreateUpgradeTestPath(testName) + + 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 := query.ClientStatus(ctx, chainA, testvalues.TendermintClientID(0)) + s.Require().NoError(err) + s.Require().Equal(exported.Active.String(), status) + + status, err = query.ClientStatus(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 := s.BroadcastMessages( + ctx, + chainA.(*cosmos.CosmosChain), + chainAWallet, + msgCreateSoloMachineClient, + ) + + s.AssertTxSuccess(resp) + + 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 := query.ClientStatus(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 := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, testvalues.DefaultTransferCoins(chainADenom), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "", nil) + s.AssertTxSuccess(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, testName) + }) + + t.Run("packets are relayed", func(t *testing.T) { + s.AssertPacketRelayed(ctx, chainA, channelA.PortID, channelA.ChannelID, 1) + + actualBalance, err := query.Balance(ctx, chainB, chainBAddress, chainBIBCToken.IBCDenom()) + s.Require().NoError(err) + + expected := testvalues.IBCTransferAmount + s.Require().Equal(expected, actualBalance.Int64()) + }) + + // 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.(*cosmos.CosmosChain), chainAUpgradeProposalWallet, testCfg.UpgradeConfig.PlanName, testCfg.ChainConfigs[0].Tag, testCfg.UpgradeConfig.Tag) + }) + + // see this issue https://github.com/informalsystems/hermes/issues/3579 + // this restart is a temporary workaround to a limitation in hermes requiring a restart + // in some cases after an upgrade. + tc := testsuite.LoadConfig() + if tc.GetActiveRelayerConfig().ID == e2erelayer.Hermes { + s.RestartRelayer(ctx, relayer, testName) + } + + t.Run("check that the tendermint clients are active again after upgrade", func(t *testing.T) { + status, err := query.ClientStatus(ctx, chainA, testvalues.TendermintClientID(0)) + s.Require().NoError(err) + s.Require().Equal(exported.Active.String(), status) + + status, err = query.ClientStatus(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 := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, testvalues.DefaultTransferCoins(chainADenom), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB.(*cosmos.CosmosChain)), 0, "", nil) + s.AssertTxSuccess(transferTxResp) + }) + + t.Run("packets are relayed", func(t *testing.T) { + s.AssertPacketRelayed(ctx, chainA, channelA.PortID, channelA.ChannelID, 1) + + actualBalance, err := query.Balance(ctx, chainB, chainBAddress, chainBIBCToken.IBCDenom()) + s.Require().NoError(err) + + expected := testvalues.IBCTransferAmount * 2 + s.Require().Equal(expected, actualBalance.Int64()) + }) + + 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) + }) +} + +func (s *UpgradeTestSuite) TestV7ToV7_1ChainUpgrade() { + t := s.T() + testCfg := testsuite.LoadConfig() + + ctx := context.Background() + testName := t.Name() + + relayer, channelA := s.CreateUpgradeTestPath(testName) + + 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("transfer native tokens from chainA to chainB", func(t *testing.T) { + transferTxResp := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, testvalues.DefaultTransferCoins(chainADenom), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB.(*cosmos.CosmosChain)), 0, "", nil) + s.AssertTxSuccess(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, testName) + }) + + chainBIBCToken := testsuite.GetIBCToken(chainADenom, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID) + + t.Run("packet is relayed", func(t *testing.T) { + s.AssertPacketRelayed(ctx, chainA, channelA.PortID, channelA.ChannelID, 1) + + actualBalance, err := query.Balance(ctx, chainB, chainBAddress, chainBIBCToken.IBCDenom()) + s.Require().NoError(err) + + expected := testvalues.IBCTransferAmount + s.Require().Equal(expected, actualBalance.Int64()) + }) + + s.Require().NoError(test.WaitForBlocks(ctx, 5, chainA), "failed to wait for blocks") + + t.Run("upgrade chain", func(t *testing.T) { + govProposalWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + s.UpgradeChain(ctx, chainA.(*cosmos.CosmosChain), govProposalWallet, testCfg.UpgradeConfig.PlanName, testCfg.ChainConfigs[0].Tag, testCfg.UpgradeConfig.Tag) + }) + + t.Run("ensure the localhost client is active and sentinel connection is stored in state", func(t *testing.T) { + status, err := query.ClientStatus(ctx, chainA, exported.LocalhostClientID) + s.Require().NoError(err) + s.Require().Equal(exported.Active.String(), status) + + connectionResp, err := query.GRPCQuery[connectiontypes.QueryConnectionResponse](ctx, chainA, &connectiontypes.QueryConnectionRequest{ConnectionId: exported.LocalhostConnectionID}) + s.Require().NoError(err) + s.Require().Equal(connectiontypes.OPEN, connectionResp.Connection.State) + s.Require().Equal(exported.LocalhostClientID, connectionResp.Connection.ClientId) + s.Require().Equal(exported.LocalhostClientID, connectionResp.Connection.Counterparty.ClientId) + s.Require().Equal(exported.LocalhostConnectionID, connectionResp.Connection.Counterparty.ConnectionId) + }) + + t.Run("ensure escrow amount for native denom is stored in state", func(t *testing.T) { + actualTotalEscrow, err := query.TotalEscrowForDenom(ctx, chainA, chainADenom) + s.Require().NoError(err) + + expectedTotalEscrow := sdk.NewCoin(chainADenom, sdkmath.NewInt(testvalues.IBCTransferAmount)) + s.Require().Equal(expectedTotalEscrow, actualTotalEscrow) // migration has run and total escrow amount has been set + }) + + 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 := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, testvalues.DefaultTransferCoins(chainADenom), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "", nil) + s.AssertTxSuccess(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.Int64()) + }) +} + +func (s *UpgradeTestSuite) TestV7ToV8ChainUpgrade() { + t := s.T() + testCfg := testsuite.LoadConfig() + + ctx := context.Background() + testName := t.Name() + + relayer, channelA := s.CreateUpgradeTestPath(testName) + + 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("transfer native tokens from chainA to chainB", func(t *testing.T) { + transferTxResp := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, testvalues.DefaultTransferCoins(chainADenom), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "", nil) + s.AssertTxSuccess(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, testName) + }) + + chainBIBCToken := testsuite.GetIBCToken(chainADenom, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID) + + t.Run("packet is relayed", func(t *testing.T) { + s.AssertPacketRelayed(ctx, chainA, channelA.PortID, channelA.ChannelID, 1) + + actualBalance, err := query.Balance(ctx, chainB, chainBAddress, chainBIBCToken.IBCDenom()) + s.Require().NoError(err) + + expected := testvalues.IBCTransferAmount + s.Require().Equal(expected, actualBalance.Int64()) + }) + + s.Require().NoError(test.WaitForBlocks(ctx, 5, chainA), "failed to wait for blocks") + + t.Run("upgrade chain", func(t *testing.T) { + govProposalWallet := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount) + s.UpgradeChain(ctx, chainB.(*cosmos.CosmosChain), govProposalWallet, testCfg.UpgradeConfig.PlanName, testCfg.ChainConfigs[0].Tag, testCfg.UpgradeConfig.Tag) + }) + + t.Run("update params", func(t *testing.T) { + authority, err := query.ModuleAccountAddress(ctx, govtypes.ModuleName, chainB) + s.Require().NoError(err) + s.Require().NotNil(authority) + + msg := clienttypes.NewMsgUpdateParams(authority.String(), clienttypes.NewParams(exported.Tendermint, "some-client")) + s.ExecuteAndPassGovV1Proposal(ctx, msg, chainB, chainBWallet) + }) + + t.Run("query params", func(t *testing.T) { + clientParamsResp, err := query.GRPCQuery[clienttypes.QueryClientParamsResponse](ctx, chainB, &clienttypes.QueryClientParamsRequest{}) + s.Require().NoError(err) + + allowedClients := clientParamsResp.Params.AllowedClients + + s.Require().Len(allowedClients, 2) + s.Require().Contains(allowedClients, exported.Tendermint) + s.Require().Contains(allowedClients, "some-client") + }) + + t.Run("query human readable ibc denom", func(t *testing.T) { + s.AssertHumanReadableDenom(ctx, chainB, chainADenom, channelA) + }) + + 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 := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, testvalues.DefaultTransferCoins(chainADenom), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "", nil) + s.AssertTxSuccess(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.Int64()) + }) +} + +func (s *UpgradeTestSuite) TestV8ToV8_1ChainUpgrade() { + t := s.T() + ctx := context.Background() + + testName := t.Name() + relayer := s.CreatePaths(ibc.DefaultClientOpts(), s.FeeTransferChannelOptions(), testName) + + channelA := s.GetChainAChannelForTest(testName) + + 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("transfer native tokens from chainA to chainB", func(t *testing.T) { + txResp := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, testvalues.DefaultTransferCoins(chainADenom), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "", nil) + s.AssertTxSuccess(txResp) + }) + + 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("pay packet fee", func(t *testing.T) { + t.Run("no packet fees in escrow", func(t *testing.T) { + packets, err := query.IncentivizedPacketsForChannel(ctx, chainA, channelA.PortID, channelA.ChannelID) + s.Require().NoError(err) + s.Require().Empty(packets) + }) + + testFee := testvalues.DefaultFee(chainADenom) + packetID := channeltypes.NewPacketID(channelA.PortID, channelA.ChannelID, 1) + packetFee := feetypes.NewPacketFee(testFee, chainAWallet.FormattedAddress(), nil) + + t.Run("pay packet fee", func(t *testing.T) { + txResp := s.PayPacketFeeAsync(ctx, chainA, chainAWallet, packetID, packetFee) + s.AssertTxSuccess(txResp) + }) + + t.Run("query incentivized packets", func(t *testing.T) { + packets, err := query.IncentivizedPacketsForChannel(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.Equal(testFee.RecvFee)) + s.Require().True(actualFee.AckFee.Equal(testFee.AckFee)) + s.Require().True(actualFee.TimeoutFee.Equal(testFee.TimeoutFee)) + }) + }) + + t.Run("upgrade chain", func(t *testing.T) { + testCfg := testsuite.LoadConfig() + proposalWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + s.UpgradeChain(ctx, chainA.(*cosmos.CosmosChain), proposalWallet, testCfg.UpgradeConfig.PlanName, testCfg.ChainConfigs[0].Tag, testCfg.UpgradeConfig.Tag) + }) + + t.Run("29-fee migration partially refunds escrowed tokens", func(t *testing.T) { + actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet) + s.Require().NoError(err) + + testFee := testvalues.DefaultFee(chainADenom) + legacyTotal := testFee.RecvFee.Add(testFee.AckFee...).Add(testFee.TimeoutFee...) + refundCoins := legacyTotal.Sub(testFee.Total()...) // Total() returns the denomwise max of (recvFee + ackFee, timeoutFee) + + expected := testvalues.StartingTokenAmount - testvalues.IBCTransferAmount - legacyTotal.AmountOf(chainADenom).Int64() + refundCoins.AmountOf(chainADenom).Int64() + s.Require().Equal(expected, actualBalance) + + // query incentivised packets and assert calculated values are correct + packets, err := query.IncentivizedPacketsForChannel(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.Equal(testFee.RecvFee)) + s.Require().True(actualFee.AckFee.Equal(testFee.AckFee)) + s.Require().True(actualFee.TimeoutFee.Equal(testFee.TimeoutFee)) + + escrowBalance, err := query.Balance(ctx, chainA, authtypes.NewModuleAddress(feetypes.ModuleName).String(), chainADenom) + s.Require().NoError(err) + + expected = testFee.Total().AmountOf(chainADenom).Int64() + s.Require().Equal(expected, escrowBalance.Int64()) + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer, testName) + }) + + chainBIBCToken := testsuite.GetIBCToken(chainADenom, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID) + + t.Run("packet is relayed", func(t *testing.T) { + s.AssertPacketRelayed(ctx, chainA, channelA.PortID, channelA.ChannelID, 1) + + actualBalance, err := query.Balance(ctx, chainB, chainBAddress, chainBIBCToken.IBCDenom()) + s.Require().NoError(err) + + expected := testvalues.IBCTransferAmount + s.Require().Equal(expected, actualBalance.Int64()) + }) + + s.Require().NoError(test.WaitForBlocks(ctx, 5, chainA), "failed to wait for blocks") + + 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 := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, testvalues.DefaultTransferCoins(chainADenom), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "", nil) + s.AssertTxSuccess(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.Int64()) + }) +} + +func (s *UpgradeTestSuite) TestV8ToV8_1ChainUpgrade_FeeMiddlewareChannelUpgrade() { + t := s.T() + testCfg := testsuite.LoadConfig() + ctx := context.Background() + + testName := t.Name() + + relayer, channelA := s.CreateUpgradeTestPath(testName) + + channelB := channelA.Counterparty + + chainA, chainB := s.GetChains() + chainADenom := chainA.Config().Denom + chainBDenom := chainB.Config().Denom + chainAIBCToken := testsuite.GetIBCToken(chainBDenom, channelA.PortID, channelA.ChannelID) + _ = chainAIBCToken + chainBIBCToken := testsuite.GetIBCToken(chainADenom, channelB.PortID, channelB.ChannelID) + + chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + chainAAddress := chainAWallet.FormattedAddress() + + chainBWallet := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount) + chainBAddress := chainBWallet.FormattedAddress() + + var ( + chainARelayerWallet, chainBRelayerWallet ibc.Wallet + relayerAStartingBalance int64 + testFee = testvalues.DefaultFee(chainADenom) + ) + + s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB), "failed to wait for blocks") + + // trying to create some inflight packets, although they might get relayed before the upgrade starts + t.Run("create inflight transfer packets between chain A and chain B", func(t *testing.T) { + chainBWalletAmount := ibc.WalletAmount{ + Address: chainBWallet.FormattedAddress(), // destination address + Denom: chainADenom, + Amount: sdkmath.NewInt(testvalues.IBCTransferAmount), + } + + transferTxResp, err := chainA.SendIBCTransfer(ctx, channelA.ChannelID, chainAWallet.KeyName(), chainBWalletAmount, ibc.TransferOptions{}) + s.Require().NoError(err) + s.Require().NoError(transferTxResp.Validate(), "chain-a ibc transfer tx is invalid") + + chainAwalletAmount := ibc.WalletAmount{ + Address: chainAWallet.FormattedAddress(), // destination address + Denom: chainBDenom, + Amount: sdkmath.NewInt(testvalues.IBCTransferAmount), + } + + transferTxResp, err = chainB.SendIBCTransfer(ctx, channelB.ChannelID, chainBWallet.KeyName(), chainAwalletAmount, ibc.TransferOptions{}) + s.Require().NoError(err) + s.Require().NoError(transferTxResp.Validate(), "chain-b ibc transfer tx is invalid") + }) + + s.Require().NoError(test.WaitForBlocks(ctx, 5, chainA, chainB), "failed to wait for blocks") + + t.Run("upgrade chains", func(t *testing.T) { + var wg sync.WaitGroup + + wg.Add(1) + go func() { + defer wg.Done() + + t.Run("chain A", func(t *testing.T) { + govProposalWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + s.UpgradeChain(ctx, chainA.(*cosmos.CosmosChain), govProposalWallet, testCfg.UpgradeConfig.PlanName, testCfg.ChainConfigs[0].Tag, testCfg.UpgradeConfig.Tag) + }) + }() + + wg.Add(1) + go func() { + defer wg.Done() + + t.Run("chain B", func(t *testing.T) { + govProposalWallet := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount) + s.UpgradeChain(ctx, chainB.(*cosmos.CosmosChain), govProposalWallet, testCfg.UpgradeConfig.PlanName, testCfg.ChainConfigs[1].Tag, testCfg.UpgradeConfig.Tag) + }) + }() + + wg.Wait() + }) + + t.Run("query params", func(t *testing.T) { + t.Run("on chain A", func(t *testing.T) { + channelParamsResp, err := query.GRPCQuery[channeltypes.QueryChannelParamsResponse](ctx, chainA, &channeltypes.QueryChannelParamsRequest{}) + s.Require().NoError(err) + + upgradeTimeout := channelParamsResp.Params.UpgradeTimeout + s.Require().Equal(clienttypes.ZeroHeight(), upgradeTimeout.Height) + s.Require().Equal(uint64(time.Minute*10), upgradeTimeout.Timestamp) + }) + + t.Run("on chain B", func(t *testing.T) { + channelParamsResp, err := query.GRPCQuery[channeltypes.QueryChannelParamsResponse](ctx, chainB, &channeltypes.QueryChannelParamsRequest{}) + s.Require().NoError(err) + + upgradeTimeout := channelParamsResp.Params.UpgradeTimeout + s.Require().Equal(clienttypes.ZeroHeight(), upgradeTimeout.Height) + s.Require().Equal(uint64(time.Minute*10), upgradeTimeout.Timestamp) + }) + }) + + t.Run("execute gov proposal to initiate channel upgrade", func(t *testing.T) { + chA, err := query.Channel(ctx, chainA, channelA.PortID, channelA.ChannelID) + s.Require().NoError(err) + + s.InitiateChannelUpgrade(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, s.CreateUpgradeFields(chA)) + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer, testName) + }) + + s.Require().NoError(test.WaitForBlocks(ctx, 10, chainA, chainB), "failed to wait for blocks") + + t.Run("packets are relayed between chain A and chain B", func(t *testing.T) { + // packet from chain A to chain B + actualBalance, err := query.Balance(ctx, chainB, chainBAddress, chainBIBCToken.IBCDenom()) + s.Require().NoError(err) + expected := testvalues.IBCTransferAmount + s.Require().Equal(expected, actualBalance.Int64()) + + // packet from chain B to chain A + actualBalance, err = query.Balance(ctx, chainA, chainAAddress, chainAIBCToken.IBCDenom()) + s.Require().NoError(err) + expected = testvalues.IBCTransferAmount + s.Require().Equal(expected, actualBalance.Int64()) + }) + + t.Run("verify channel A upgraded and is fee enabled", func(t *testing.T) { + channel, err := query.Channel(ctx, chainA, channelA.PortID, channelA.ChannelID) + s.Require().NoError(err) + + // check the channel version include the fee version + version, err := feetypes.MetadataFromVersion(channel.Version) + s.Require().NoError(err) + s.Require().Equal(feetypes.Version, version.FeeVersion, "the channel version did not include ics29") + + // extra check + feeEnabled, err := query.FeeEnabledChannel(ctx, chainA, channelA.PortID, channelA.ChannelID) + s.Require().NoError(err) + s.Require().Equal(true, feeEnabled) + }) + + t.Run("verify channel B upgraded and is fee enabled", func(t *testing.T) { + channel, err := query.Channel(ctx, chainB, channelB.PortID, channelB.ChannelID) + s.Require().NoError(err) + + // check the channel version include the fee version + version, err := feetypes.MetadataFromVersion(channel.Version) + s.Require().NoError(err) + s.Require().Equal(feetypes.Version, version.FeeVersion, "the channel version did not include ics29") + + // extra check + feeEnabled, err := query.FeeEnabledChannel(ctx, chainB, channelB.PortID, channelB.ChannelID) + s.Require().NoError(err) + s.Require().Equal(true, feeEnabled) + }) + + t.Run("prune packet acknowledgements", func(t *testing.T) { + // there should be one ack for the packet that we sent before the upgrade + acks, err := query.PacketAcknowledgements(ctx, chainA, channelA.PortID, channelA.ChannelID, []uint64{}) + s.Require().NoError(err) + s.Require().Len(acks, 1) + s.Require().Equal(uint64(1), acks[0].Sequence) + + pruneAcksTxResponse := s.PruneAcknowledgements(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, uint64(1)) + s.AssertTxSuccess(pruneAcksTxResponse) + + // after pruning there should not be any acks + acks, err = query.PacketAcknowledgements(ctx, chainA, channelA.PortID, channelA.ChannelID, []uint64{}) + s.Require().NoError(err) + s.Require().Empty(acks) + }) + + t.Run("stop relayer", func(t *testing.T) { + s.StopRelayer(ctx, relayer) + }) + + t.Run("recover relayer wallets", func(t *testing.T) { + _, _, err := s.RecoverRelayerWallets(ctx, relayer, testName) + s.Require().NoError(err) + + chainARelayerWallet, chainBRelayerWallet, err = s.GetRelayerWallets(relayer) + s.Require().NoError(err) + + relayerAStartingBalance, err = s.GetChainANativeBalance(ctx, chainARelayerWallet) + s.Require().NoError(err) + t.Logf("relayer A user starting with balance: %d", relayerAStartingBalance) + }) + + s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB), "failed to wait for blocks") + + t.Run("register and verify counterparty payee", func(t *testing.T) { + _, chainBRelayerUser := s.GetRelayerUsers(ctx, testName) + resp := s.RegisterCounterPartyPayee(ctx, chainB, chainBRelayerUser, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, chainBRelayerWallet.FormattedAddress(), chainARelayerWallet.FormattedAddress()) + s.AssertTxSuccess(resp) + + address, err := query.CounterPartyPayee(ctx, chainB, chainBRelayerWallet.FormattedAddress(), channelA.Counterparty.ChannelID) + s.Require().NoError(err) + s.Require().Equal(chainARelayerWallet.FormattedAddress(), address) + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer, testName) + }) + + t.Run("send incentivized transfer packet", func(t *testing.T) { + // before adding fees for the packet, there should not be incentivized packets + packets, err := query.IncentivizedPacketsForChannel(ctx, chainA, channelA.PortID, channelA.ChannelID) + s.Require().NoError(err) + s.Require().Empty(packets) + + transferAmount := testvalues.DefaultTransferAmount(chainA.Config().Denom) + + msgPayPacketFee := feetypes.NewMsgPayPacketFee(testFee, channelA.PortID, channelA.ChannelID, chainAWallet.FormattedAddress(), nil) + msgTransfer := testsuite.GetMsgTransfer( + channelA.PortID, + channelA.ChannelID, + channelA.Version, // upgrade adds fee middleware, but keeps transfer version + sdk.NewCoins(transferAmount), + chainAWallet.FormattedAddress(), + chainBWallet.FormattedAddress(), + s.GetTimeoutHeight(ctx, chainB), + 0, + "", + nil, + ) + resp := s.BroadcastMessages(ctx, chainA, chainAWallet, msgPayPacketFee, msgTransfer) + s.AssertTxSuccess(resp) + }) + + t.Run("escrow fees equal (ack fee + recv fee)", func(t *testing.T) { + actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet) + s.Require().NoError(err) + + // walletA has done two IBC transfers of value testvalues.IBCTransferAmount since the start of the test. + expected := testvalues.StartingTokenAmount - (2 * testvalues.IBCTransferAmount) - testFee.AckFee.AmountOf(chainADenom).Int64() - testFee.RecvFee.AmountOf(chainADenom).Int64() + s.Require().Equal(expected, actualBalance) + }) + + t.Run("packets are relayed", func(t *testing.T) { + packets, err := query.IncentivizedPacketsForChannel(ctx, chainA, channelA.PortID, channelA.ChannelID) + s.Require().NoError(err) + s.Require().Empty(packets) + }) + + t.Run("tokens are received by walletB", func(t *testing.T) { + actualBalance, err := query.Balance(ctx, chainB, chainBAddress, chainBIBCToken.IBCDenom()) + s.Require().NoError(err) + + // walletB has received two IBC transfers of value testvalues.IBCTransferAmount since the start of the test. + expected := 2 * testvalues.IBCTransferAmount + s.Require().Equal(expected, actualBalance.Int64()) + }) + + t.Run("relayerA is paid ack and recv fee", func(t *testing.T) { + actualBalance, err := s.GetChainANativeBalance(ctx, chainARelayerWallet) + s.Require().NoError(err) + + expected := relayerAStartingBalance + testFee.AckFee.AmountOf(chainADenom).Int64() + testFee.RecvFee.AmountOf(chainADenom).Int64() + s.Require().Equal(expected, actualBalance) + }) +} + +func (s *UpgradeTestSuite) TestV8ToV9ChainUpgrade() { + t := s.T() + testCfg := testsuite.LoadConfig() + ctx := context.Background() + + testName := t.Name() + + relayer, channelA := s.CreateUpgradeTestPath(testName) + + chainA, chainB := s.GetChains() + chainADenom := chainA.Config().Denom + chainBDenom := chainB.Config().Denom + + chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + chainAAddress := chainAWallet.FormattedAddress() + + chainBWallet := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount) + chainBAddress := chainBWallet.FormattedAddress() + + chainAIBCToken := testsuite.GetIBCToken(chainBDenom, channelA.PortID, channelA.ChannelID) + chainBIBCToken := testsuite.GetIBCToken(chainADenom, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID) + + s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB), "failed to wait for blocks") + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer, testName) + }) + + t.Run("transfer native tokens from chainA to chainB", func(t *testing.T) { + transferTxResp := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, testvalues.DefaultTransferCoins(chainADenom), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "", nil) + s.AssertTxSuccess(transferTxResp) + + s.AssertPacketRelayed(ctx, chainA, channelA.PortID, channelA.ChannelID, 1) + + actualBalance, err := query.Balance(ctx, chainB, chainBAddress, chainBIBCToken.IBCDenom()) + s.Require().NoError(err) + + expected := testvalues.IBCTransferAmount + s.Require().Equal(expected, actualBalance.Int64()) + }) + + t.Run("transfer native tokens from chainB to chainA", func(t *testing.T) { + transferTxResp := s.Transfer(ctx, chainB, chainBWallet, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, testvalues.DefaultTransferCoins(chainBDenom), chainBAddress, chainAAddress, s.GetTimeoutHeight(ctx, chainA.(*cosmos.CosmosChain)), 0, "", nil) + s.AssertTxSuccess(transferTxResp) + + s.AssertPacketRelayed(ctx, chainA, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, 1) + + actualBalance, err := query.Balance(ctx, chainA, chainAAddress, chainAIBCToken.IBCDenom()) + s.Require().NoError(err) + + expected := testvalues.IBCTransferAmount + s.Require().Equal(expected, actualBalance.Int64()) + }) + + s.Require().NoError(test.WaitForBlocks(ctx, 5, chainA), "failed to wait for blocks") + + t.Run("stop relayer", func(t *testing.T) { + s.StopRelayer(ctx, relayer) + }) + + t.Run("upgrade chain", func(t *testing.T) { + govProposalWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + s.UpgradeChain(ctx, chainA.(*cosmos.CosmosChain), govProposalWallet, testCfg.UpgradeConfig.PlanName, testCfg.ChainConfigs[0].Tag, testCfg.UpgradeConfig.Tag) + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer, testName) + }) + + t.Run("query denoms after upgrade", func(t *testing.T) { + resp, err := query.TransferDenoms(ctx, chainA) + s.Require().NoError(err) + s.Require().Len(resp.Denoms, 1) + s.Require().Equal(chainAIBCToken, resp.Denoms[0]) + }) + + 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 := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, testvalues.DefaultTransferCoins(chainADenom), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "", nil) + s.AssertTxSuccess(transferTxResp) + + 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.Int64()) + }) +} + +func (s *UpgradeTestSuite) TestV8ToV9ChainUpgrade_Localhost() { + t := s.T() + testCfg := testsuite.LoadConfig() + ctx := context.Background() + + chainA, chainB := s.GetChains() + chainADenom := chainA.Config().Denom + + rlyWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + userAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + userBWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + + var ( + srcChannelID string + dstChannelID string + ) + + s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB), "failed to wait for blocks") + + t.Run("open localhost channel", func(t *testing.T) { + var ( + msgChanOpenInitRes channeltypes.MsgChannelOpenInitResponse + msgChanOpenTryRes channeltypes.MsgChannelOpenTryResponse + ) + + msgChanOpenInit := channeltypes.NewMsgChannelOpenInit( + transfertypes.PortID, transfertypes.V1, + channeltypes.UNORDERED, []string{exported.LocalhostConnectionID}, + transfertypes.PortID, rlyWallet.FormattedAddress(), + ) + txResp := s.BroadcastMessages(ctx, chainA, rlyWallet, msgChanOpenInit) + s.AssertTxSuccess(txResp) + s.Require().NoError(testsuite.UnmarshalMsgResponses(txResp, &msgChanOpenInitRes)) + srcChannelID = msgChanOpenInitRes.ChannelId + + msgChanOpenTry := channeltypes.NewMsgChannelOpenTry( + transfertypes.PortID, transfertypes.V1, + channeltypes.UNORDERED, []string{exported.LocalhostConnectionID}, + transfertypes.PortID, srcChannelID, + transfertypes.V1, localhost.SentinelProof, clienttypes.ZeroHeight(), rlyWallet.FormattedAddress(), + ) + txResp = s.BroadcastMessages(ctx, chainA, rlyWallet, msgChanOpenTry) + s.AssertTxSuccess(txResp) + s.Require().NoError(testsuite.UnmarshalMsgResponses(txResp, &msgChanOpenTryRes)) + dstChannelID = msgChanOpenTryRes.ChannelId + + msgChanOpenAck := channeltypes.NewMsgChannelOpenAck( + transfertypes.PortID, srcChannelID, + dstChannelID, transfertypes.V1, + localhost.SentinelProof, clienttypes.ZeroHeight(), rlyWallet.FormattedAddress(), + ) + txResp = s.BroadcastMessages(ctx, chainA, rlyWallet, msgChanOpenAck) + s.AssertTxSuccess(txResp) + + msgChanOpenConfirm := channeltypes.NewMsgChannelOpenConfirm( + transfertypes.PortID, dstChannelID, + localhost.SentinelProof, clienttypes.ZeroHeight(), rlyWallet.FormattedAddress(), + ) + txResp = s.BroadcastMessages(ctx, chainA, rlyWallet, msgChanOpenConfirm) + s.AssertTxSuccess(txResp) + }) + + t.Run("ibc transfer over localhost", func(t *testing.T) { + txResp := s.Transfer(ctx, chainA, userAWallet, transfertypes.PortID, srcChannelID, testvalues.DefaultTransferCoins(chainADenom), userAWallet.FormattedAddress(), userBWallet.FormattedAddress(), s.GetTimeoutHeight(ctx, chainA), 0, "", nil) + s.AssertTxSuccess(txResp) + + packet, err := ibctesting.ParsePacketFromEvents(txResp.Events) + s.Require().NoError(err) + s.Require().NotNil(packet) + + msgRecvPacket := channeltypes.NewMsgRecvPacket(packet, localhost.SentinelProof, clienttypes.ZeroHeight(), rlyWallet.FormattedAddress()) + + txResp = s.BroadcastMessages(ctx, chainA, rlyWallet, msgRecvPacket) + s.AssertTxSuccess(txResp) + + ack, err := ibctesting.ParseAckFromEvents(txResp.Events) + s.Require().NoError(err) + s.Require().NotNil(ack) + + msgAcknowledgement := channeltypes.NewMsgAcknowledgement(packet, ack, localhost.SentinelProof, clienttypes.ZeroHeight(), rlyWallet.FormattedAddress()) + txResp = s.BroadcastMessages(ctx, chainA, rlyWallet, msgAcknowledgement) + s.AssertTxSuccess(txResp) + + s.AssertPacketRelayed(ctx, chainA, transfertypes.PortID, srcChannelID, 1) + ibcToken := testsuite.GetIBCToken(chainADenom, transfertypes.PortID, dstChannelID) + actualBalance, err := query.Balance(ctx, chainA, userBWallet.FormattedAddress(), ibcToken.IBCDenom()) + s.Require().NoError(err) + s.Require().Equal(testvalues.IBCTransferAmount, actualBalance.Int64()) + }) + + t.Run("localhost exists in state before upgrade", func(t *testing.T) { + status, err := query.ClientStatus(ctx, chainA, exported.LocalhostClientID) + s.Require().NoError(err) + s.Require().Equal(exported.Active.String(), status) + + state, err := s.ClientState(ctx, chainA, exported.LocalhostClientID) + s.Require().NoError(err) + s.Require().NotNil(state) + }) + + t.Run("upgrade chain", func(t *testing.T) { + govProposalWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + s.UpgradeChain(ctx, chainA.(*cosmos.CosmosChain), govProposalWallet, testCfg.UpgradeConfig.PlanName, testCfg.ChainConfigs[0].Tag, testCfg.UpgradeConfig.Tag) + }) + + t.Run("localhost does not exist in state after upgrade", func(t *testing.T) { + status, err := query.ClientStatus(ctx, chainA, exported.LocalhostClientID) + s.Require().NoError(err) + s.Require().Equal(exported.Active.String(), status) + + state, err := s.ClientState(ctx, chainA, exported.LocalhostClientID) + s.Require().Error(err) + s.Require().Nil(state) + }) + + t.Run("query localhost transfer channel ends after upgrade", func(t *testing.T) { + channelEndA, err := query.Channel(ctx, chainA, transfertypes.PortID, srcChannelID) + s.Require().NoError(err) + s.Require().NotNil(channelEndA) + + channelEndB, err := query.Channel(ctx, chainA, transfertypes.PortID, dstChannelID) + s.Require().NoError(err) + s.Require().NotNil(channelEndB) + + s.Require().Equal(channelEndA.ConnectionHops, channelEndB.ConnectionHops) + }) + + t.Run("ibc transfer back over localhost after upgrade", func(t *testing.T) { + ibcToken := testsuite.GetIBCToken(chainADenom, transfertypes.PortID, dstChannelID) + transferCoins := testvalues.DefaultTransferCoins(ibcToken.IBCDenom()) + txResp := s.Transfer(ctx, chainA, userBWallet, transfertypes.PortID, dstChannelID, transferCoins, userBWallet.FormattedAddress(), userAWallet.FormattedAddress(), s.GetTimeoutHeight(ctx, chainA), 0, "", nil) + s.AssertTxSuccess(txResp) + + packet, err := ibctesting.ParsePacketFromEvents(txResp.Events) + s.Require().NoError(err) + s.Require().NotNil(packet) + + msgRecvPacket := channeltypes.NewMsgRecvPacket(packet, localhost.SentinelProof, clienttypes.ZeroHeight(), rlyWallet.FormattedAddress()) + + txResp = s.BroadcastMessages(ctx, chainA, rlyWallet, msgRecvPacket) + s.AssertTxSuccess(txResp) + + ack, err := ibctesting.ParseAckFromEvents(txResp.Events) + s.Require().NoError(err) + s.Require().NotNil(ack) + + msgAcknowledgement := channeltypes.NewMsgAcknowledgement(packet, ack, localhost.SentinelProof, clienttypes.ZeroHeight(), rlyWallet.FormattedAddress()) + txResp = s.BroadcastMessages(ctx, chainA, rlyWallet, msgAcknowledgement) + s.AssertTxSuccess(txResp) + + s.AssertPacketRelayed(ctx, chainA, transfertypes.PortID, dstChannelID, 1) + + actualBalance, err := query.Balance(ctx, chainA, userAWallet.FormattedAddress(), chainADenom) + s.Require().NoError(err) + s.Require().Equal(testvalues.StartingTokenAmount, actualBalance.Int64()) + }) +} + +func (s *UpgradeTestSuite) TestV8ToV9ChainUpgrade_ICS20v2ChannelUpgrade() { + t := s.T() + testCfg := testsuite.LoadConfig() + ctx := context.Background() + + testName := t.Name() + + relayer, channelA := s.CreateUpgradeTestPath(testName) + + chainA, chainB := s.GetChains() + chainADenom := chainA.Config().Denom + chainBDenom := chainB.Config().Denom + + chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + chainAAddress := chainAWallet.FormattedAddress() + + chainBWallet := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount) + chainBAddress := chainBWallet.FormattedAddress() + + chainBIBCToken := testsuite.GetIBCToken(chainADenom, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID) + + s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB), "failed to wait for blocks") + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer, testName) + }) + + t.Run("transfer native tokens from chainA to chainB", func(t *testing.T) { + transferTxResp := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, testvalues.DefaultTransferCoins(chainADenom), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "", nil) + s.AssertTxSuccess(transferTxResp) + + s.AssertPacketRelayed(ctx, chainA, channelA.PortID, channelA.ChannelID, 1) + + actualBalance, err := query.Balance(ctx, chainB, chainBAddress, chainBIBCToken.IBCDenom()) + s.Require().NoError(err) + + expected := testvalues.IBCTransferAmount + s.Require().Equal(expected, actualBalance.Int64()) + }) + + t.Run("verify channel version before upgrade", func(t *testing.T) { + channel, err := query.Channel(ctx, chainA, channelA.PortID, channelA.ChannelID) + s.Require().NoError(err) + s.Require().Equal(transfertypes.V1, channel.Version, "the channel version is not ics20-1") + }) + + s.Require().NoError(test.WaitForBlocks(ctx, 5, chainA), "failed to wait for blocks") + + t.Run("upgrade chain", func(t *testing.T) { + govProposalWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + s.UpgradeChain(ctx, chainA.(*cosmos.CosmosChain), govProposalWallet, testCfg.UpgradeConfig.PlanName, testCfg.ChainConfigs[0].Tag, testCfg.UpgradeConfig.Tag) + }) + + t.Run("upgrade channel to ics20-2", func(t *testing.T) { + chA, err := query.Channel(ctx, chainA, channelA.PortID, channelA.ChannelID) + s.Require().NoError(err) + + upgradeFields := channeltypes.NewUpgradeFields(chA.Ordering, chA.ConnectionHops, transfertypes.V2) + s.InitiateChannelUpgrade(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, upgradeFields) + }) + + t.Run("verify channel A upgraded and transfer version is ics20-2", func(t *testing.T) { + err := test.WaitForCondition(time.Minute*2, time.Second*2, func() (bool, error) { + channel, err := query.Channel(ctx, chainA, channelA.PortID, channelA.ChannelID) + if err != nil { + return false, err + } + + return channel.Version == transfertypes.V2, nil + }) + s.Require().NoError(err, "failed to wait for channel to be upgraded to ics20-2") + }) + + t.Run("multi-denom IBC token transfer from chainB to chainA, to make sure the upgrade did not break the packet flow", func(t *testing.T) { + transferCoins := []sdk.Coin{ + testvalues.DefaultTransferAmount(chainBIBCToken.IBCDenom()), + testvalues.DefaultTransferAmount(chainBDenom), + } + + transferTxResp := s.Transfer(ctx, chainB, chainBWallet, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, transferCoins, chainBAddress, chainAAddress, s.GetTimeoutHeight(ctx, chainA), 0, "", nil) + s.AssertTxSuccess(transferTxResp) + + s.AssertPacketRelayed(ctx, chainB, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, 2) + + actualNativeBalance, err := chainA.GetBalance(ctx, chainAAddress, chainADenom) + s.Require().NoError(err) + expected := testvalues.StartingTokenAmount + s.Require().Equal(expected, actualNativeBalance.Int64()) + + chainAIBCToken := testsuite.GetIBCToken(chainBDenom, channelA.PortID, channelA.ChannelID) + actualIBCTokenBalance, err := query.Balance(ctx, chainA, chainAAddress, chainAIBCToken.IBCDenom()) + s.Require().NoError(err) + expected = testvalues.IBCTransferAmount + s.Require().Equal(expected, actualIBCTokenBalance.Int64()) + }) +} + +// ClientState queries the current ClientState by clientID +func (*UpgradeTestSuite) ClientState(ctx context.Context, chain ibc.Chain, clientID string) (*clienttypes.QueryClientStateResponse, error) { + res, err := query.GRPCQuery[clienttypes.QueryClientStateResponse](ctx, chain, &clienttypes.QueryClientStateRequest{ClientId: clientID}) + if err != nil { + return res, err + } + + return res, nil +} diff --git a/e2e/tests/wasm/contracts/ics10_grandpa_cw.wasm.gz b/e2e/tests/wasm/contracts/ics10_grandpa_cw.wasm.gz new file mode 100644 index 0000000000000000000000000000000000000000..4fce8dbe2e121a7c732de224580026cb42a8c7ce GIT binary patch literal 263909 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⁢fgO^WE?u^D@C03br~K2p@ctc7ukK7f?|j4T^l^D_l~`M`@pR9PNn4% zS(RF@ExM7TI=t-Hu}N0f;^SM0YKbQNSFZq;o@#2{_XtM5mYOPC*mGp_Qjfnlol%W;zKixF2xLI zLbnhBuHK6q48cjYhRJrz#luOKQ|Ydm?smppceLEf29J&uvS8NPB!#R;&=yQ_*U5k` zU-lY4rxDINnnp(b;P1nQ?|**x|CXfusC zoQ8hx@S<0Wu<=8-zQE##wF1G1O+!bCulzmbQh{+>0PU)^DDgf~wP0^q^hv(Q3`zbb z$Wbu;AN-8;{*z?A@>Evctto`So)6t^nLS z2Pd0)w|qg4Uj@=G?S&kV$Prcct`x>Osw>XJ9r`g$+E^)#$koSnhkSkxYBE&{sIGCy zU-b2pi?5dBF*&}~P|nM-X&q*Yq06x*w#e@YUEbj`D$4Ii6)auYhF-*Rk~vYiGjd0} z@>*|B1QdYqAr0=7u1e_&IiPmBh)-F9e@{2F;s)0!%XF7CBD~;6*SLOA?3Tt)NaIZ` zEex&X6jV^Qro$pPnh&xm86LT*^>FmYgE~U1bnYOZ~_eAiLuRM)}07<7WA! zu@OGWa!F&#C3P&9^rnk&NpHf=_$Ka*Z!$aMn_P9igxQ17!zHmg-`t|$^tN(Q4)nV% zsmfG#DK=sgvsu7YE@#)1Kg!ys_!aEG*k8kIy=&wlE&P>a>-Y4y#K3LImCzCtgJ9xH zh0>Lp=)0-v#R9W=4S-o-r|2Rjj!7+6DD(A^v2FrB4fBM$z1jj4_w$>r zT`S*b19`Vjabv(>2x)7NGaDh+UVW;v3uwMzqr#-M+NdE#nT)P6nDi<^0cZK+8NQ z-!{1$eA!oJlPbf!fIM(=e9?zbr!ieniow@aQp}lPDD1Oq0w!uw%qiE<)egyH3mh)j zG}J%S%-))PwSm!&Y>VL;c4nm+<_+23GiNtuyUf{5*>1D&L4t(*YzvP9g35**#;I=S z57j^Bte;QXkIK56g(?X>jk4?LIM}6=BI%!-u+wa*>IrnquNo-bvY%A`Ja3t+wfqWO z=4&m_vgH$|4!RjL$mV9#nZ_Zwium)~zSadXBE>N$`F}l%#p0C$Y+VP=_ z_Zk4m>D{F5SF!?ftb?MYQu+@RVSTrl^M_2mT~o=Zwo@oO^iV}G={`qF8B@}PMA-(% zDU>p?loI8PYm_q4a&BD{9Ytz6X6v~j{gwZPCqIGdRj}?wp8`fEuRaFt0ObOJqs~#?rFp(oW!YyDVz$F z+LZJ1OZpQ`|CWt0+67>@a?PBg@Hq9zZ!uBjV39rNE^l3%MARrs1VrbeO}(jr~%)m{onGdA2B1BS4|Hs87}wb!%i% zSIfG!%(6}|vaC}q>y*nnHOo46>H*^{v;4AFRgu=4YL<=n17V;BjF|r*i}o{QjqxXP zv6o0Bc%1%+rpBK{ibwEbMxl3l{ROv24?|lFV-?Yrf}&)W`42q%11>^Ddqpqe#VkQPFQ*+NQq>bGmj5*QhvYkBVHELn2fSV^|ztjS#o@6?xHe(U|z zX+jikP!u-2H&PvFw7QjkS-{qcRh{y%kiGIZz4FDU&1aw_7@f_L>?5l@%+4}MVY-Tu z?9q9>DNSkBbejI`(^ZUQuRILL^=zu<=u|F*@O7Q4J3}1};TAJhcbGaBVm(eF5Y{#= zhFFhNkfWU!RfzRC1yS31T7}TAOhFpAu2s_<&MEldPO2lT>XN6k5l}s@qk3FN^|+4eaUIp;I;zKYRFCVZ9uJ{gQ8 zizVpos3H-$IRy98a97Z$HQi*YgTF94Jqi_Z<+MzNvR>`C|fp(iF=HmsE*TQ(_dSzjYd<%UUR%KBu= zu(2vbMi-*K)k;l*A)7SY?gbe#VZ1}CWmQ=nYk=p7R_e^p%C>xYIsa+PTufxIQvv_y z0%*u5m`vY+6y&$5k{AX%rHP4?j@}0)lUmWH;LW(**+IeBPZ)B-YorD+6o-PfNd1|5 zsTjppFF>zTN*t!ZwacB6G;A<#B2yp$-D<~~VxhHa!+3Npp^Z@qhxzG{{4_p5c(tEtG6F`Z$feYYU=I0h%g>?U#Fr1S0Ou5R zcygRXcHD;WdJx{%md`k(x)fi1sRgIjiHFqe02+?~)Pgfsa9(P`*|`Wf&tF6yp1jmV zo)?;S*iX4(_f@V<_Au=m3^nrJb>Q`HcgSzPKX%_Ao5j#CH|%$E?`vBXiGlA_w8Orp zlT8z0d8&G6d;@}crc8Z}+6@zGY7y~d@jueCa_*OHJoYN2IMJ9NV;xw<8chu8aV_({ z3FvVxbG`}Zu`MQ;Czxe?7fl-oCSepx`f0m)#mdd4|4~pjdtLfz`~(dzZY5>M_09TJ zz8A-%L4Iqtisg~sJiO2Nx01jk)Mlba;Ir51W{3dSMI86g5449M+@m<|H9H1k9_&Vd zO~Gsgldp6mnADA60&krh-UudN^98KJ8_=ow`7wUo2SyBZD#MxZjt}J^BvQS9m)usSF&$R*7ptAjikk`5Ez%_dP{Yk z;M%Q_2~^8+7@)ps6>6dQc1Dpsanfv zwmj+AMVKSp1*s7VbSP?uwUL$8P;6x7T&+bTD`%?~`&mzl(41++HKQ$yNfQPPdF`d9 zaaiq@FE)pQ4}0Yc&7tT-kuIA<;fp;0@f77swmfIHQ%IuNPERGeot{i|J3W!;cFIt~ z7aQ<}&~3GEY7ulcK*hP18)dz^fOQ1b-8(TJZyjbsLy?Ge7K$BaLyK6q)-~G{xTBQ`tZ}a}84i?W^9)y-1hPfqTU^C{ZEh!xIx-l;O2?G@98i4ZewBB@){tRP_>EQ;G;VT=V8CzQud&&4Bn9*Rrz zR~dFYrikIapt{V}*D+<_n5eI#{&x8(9XKGT*Xd&$iZxq<$9>dg z!uP`;*}6jh`xpPs>b>*DC;ShY?UT11%cU7U$N$hud}t_SMm067|PTm zNT~8d1vQ`G?M<9uazmEB#oc*S?|Go1QSrw ze_7Z;A4#F)w>4QaTO(2*uzzj&@VDjD@F&RnCha^EjIRPz!ffrW3`(2^I!jY>yDB^l z8HsSlV_!)qUBPMNlF0GKgh2=v6VWI~kKAo)Da0&-)51>s=m?6M+|MXr+vGjKjjxpRd|dD)*;i|PA}7+fLL5O6Bk~Y-o0tv{%KJ?~+Gg!%UsJGpS+mZ`O}JvHk(rkA zF{pvAFHilf6;3;*c%=w+M}L~R9M4#x4noLG@FFxI5IoK`HM7Z~X3d!2q?psJ2~KR8 ztF@eF%WTyG5${|>q>ZLYZ!$S#A!S3|KFWs5Ix6xk4U>sV7b=lxnnN=A+1QQucdqqb z43H5?B&xGMQnlt_IeInTG6#0`Y8g0kU|6q~q$7u=k@hlcEz4Ol5H_=_#n*IQ+3nlH z89D6JC;x``^$`wnosTL2y&1o~)&EN$q(0v%w2?;R#g>766(=~8t=R1SdaSwGiak4n zJErp)x6ka+w7@;xG}6R>iT-w{#>+6X>}lizlx1-$toDHw z*$Pd7}}i zHC{$oYMFJ2vs6GMb&f0^r1OaN^V(%+5BFwP)f|Etw6Bv2t_cN~J|m3H^PYnjT&z62 zb{K4J`D}nK!cP&vhIDV>pNg^lS+u+NItAO5irF==P05D@4;Hy*b2We?^!tPlT}4qe z57{{)WNq4CtU`Y+75w{%PYtsim?;d6=DcXBhibs!G@J8cRVJg`B0cmq_t_Y@NhE3? z+e4pA&3J*Snt(HHg!eM*PAeP3P$q6=@`IL6f|;_3Y_bZl*T!O%2`3tpA+Zk=^)H91 zJ?x`t`e-fJhc>2Kd~ZT$nO`!60pn)Kt}aG@EL$}|1y*?E(nEAIYvDU5RUH@UhqN@z zwHBS9YIJ@J(SA|+!E6GhdB*hZpk2rr*fPQSC{%&q{4H6ky|zqfK7I&>e~8=<{?HGm zb?DxdW8xT#$OYxc;I)a>yxcxxBi?0Ky?i9%z2%TIM1GkNkiSL)-ruN!P@WBUZ=!Ka zW^%PENVd`uhAF0q$1!5v5m!poB{ui<{=N=9WI|HTkk;=pdZS3It7u9Ru5yx^UPhXe z6kPK$?KAp>Th5KzVjo`PHbK%GZLi=wfnZ~r8Idz8viBZ}S(vzjIbhmwOO}g1b5@LT z%*Dh&3kA?3VGG;oTiTYAn90A!U|);*C4hA35&m1QhLHX2I^M;Kcd>!*5Z=`a2sKCW zu8rLVFV4axc-M4zXJ)PCQbJg~b3a7x2Y=`X(>io-s*oGOyM~7TB#2+_W<5sR>k}PK zyv;-c*VsY{!+8XrHw6@;p*#Z7NFD)u7jiU*Lsf4O5yyg`4sAnVfp8EuW!>G%Ox%fK zn0()-p0JAlSDxfraA~IW?^i)^bs1dir5#PVn%=7Wb$T1jaWcfnGBbQJbfWgfkGMqR z+fa9KP%wG9)>|-=2h-XjWu$EIZ_(X*Nb$uK$k|3nqFtA7`7E1Il+ZPuVH0W+x~9`? zLPZf~P;h$^jlf^5r?3gJfM4Xyf){jzB$bYX|*GTj3%lZDnuzXPeZ z5BmiJ5fVryR&6dMt|tSotIb2BHm@1ppeYqfn6Iofr9x3PFny(}N&D4)P*X zc*H)K!akY8zF`Xcb+!4JRhwIqF=X~UkJ>ybpI)LiPoNQyzrW*D;tBKt^7q$kEhpIW zxvB->7A2#15=d|mwi-s*G7TOZIs|lfnbVr<`>gn`?`pcM6MmK38SkJdzv%NZ>K9Kr zp(HUX$dk1e)j^)9TF?bl7f@)c+B|_QlhjJHCeX~XIs{1`nqoZwBa1$h&ue0473^|tm+&l za_YrKC!TEf-pU9u5Vsv&V&rLNd(W!Tp7Knh#Gria!TxnIPe|&nQkJXK0GO3^q|A$LmP@o)}qmJzw!a^8&+XY*HqZk%d4=p zB3EKEoO8w>!Lg4_=XDi!%c`)WUD8*C^)aQ%(inT?g7hPey_%`66Jtiu>%^+C5BWNk zkCL9em+9m56X@}-x}zS-$EiaSa%T=y9+-dv`!bV9QCA_CW)mQZkVX%z=4B8tnnr=m zq%0bHmYF<1l>!^pwbCL&f_5sf;aRT=?1&2NHCd)|`&ul?wOo>GZFcjOz>Kw{+0EOX zY$doXForAlXe9(9UB-0$hP$4U!6YCPL@L8=gK0Jj>?`QFuT|BqfKtv|Qc`Ut)0Uu$ z%E~OuS`@^TXcjAV|E$yaBGUz4r}FZ7iH$?EbY5w&$P5XzaYli?2D^6TLS#W|jNExmBYv#5b(u89p1Hu+{bYwtKJuVCn=&)~ho`Y0IH=&HK(6-6UEd7fsEU|?a zDa+l<;|KkUR4n5BbhXaFBwS-Skfu{cEBn!^?Ph0`LmAzEPIIu2K^ZN_LqwK@Dl{mg z`4f;tP9F79n#hGThBkt5R2ki>E2EosIjJ(bMSB=ECJn2MHYK5?tWpre|Brt2-R+!@g&)kKKa*e&fG|cE>17tlYqZ1~3DpE|85vanf zM`g4Ep`|d$BZU)GHJD`ZYdT)=KGCb1TceBySpj~Ls34Elqq4O zSGg%)qgNXtog8k+X-8F|$9%1Y^HPX8SW!Ob!pc{aFNTm;p7rg+E6Nwx@=UGedA1y{ zTI9-j38}L2Ik~}?E!8IOr$*YJ2{!HKdwohhuUKdrm5=ZVQ>+LDzy_U*KJVVtF^_tD zl!8~oK>H8j%=PB-5jztCsqxL;Y=9tlR!HM|*V1p{W-n!(HQZ46xWz}&wX<4u?Y{}x z7%x8@*rXFI@Fss>s*12><5znh3a<0+*IH`#Y6-R%Ws$u{P6a;ill!0a2RnUN7Ve0= zqCEPGh-X~D=`=TJ3~UYr>!@!_#BqptNGNCe?GR!;=ZVU%5SCSdXHL7-Y0^Wi3oEWM z>XtRuE@7=IZiKrdEeUp?{M#@8z7^Rj@6Rr=<*Fc_1-n@uYA)2|TYvNN%VYO*wT7}t z>&ej25v2lCKayl2|%+02R^qj*#yjc{jVh8d_*@mo{cgtyDqL6f(RIs?8+NVl{d z)QCcQcxa>^P>%fwnIorsi4GnPtmDwK^s#2gwImY{cgg{!HdbhHynK}$QPXp^91r;* z8sp{j9{j)jjp3I3o$^%#az+>x+}|cJj|-eN`XG*DPgCJJFm8(DOk=bngtBo9?#y%m?vPzqd|o! zXMr`32B@^_?JxWdUU^X8kjLe$dDkJ+(kP$%`9+>J%V*?F2Ac0FpEYMU%Ua(uD39wU zzQ6}1yjjntP#=nCC%6h0)Lk&uHsQhLBGC(B;Wx>_!*;>Lq#S%`3VduOsT|a0Z+p4; z)5j5=7~EKd(F+(n9A z-Coe%Y~DfNEq)0%KYH;p?mgUJhW54C%!ghJTaSIaPyx7Vn$9BA8A2EHJ=#yR)=#4S zEa_m69Zh08aK(P`+Vgb7=UcK`wW1$77BFaMEnC!LD^V{s+ki}{a!X3iyEvZnna*!M z5~gAUQdyi33gpWc^2<%8;E_Gl*Ygwf>u-7vh5b3U2 z@Oevh&5F>@{TUT(ckEApNyb3o9ij{rh#8no-weo?&s;M!48I&78irrChKAuo&}N*k zUxwGR1KFCmH>R7t69M+Xl)Qaf{-JYxNf}y`mcLVLnPi*>H)6q^M69ou9AKTo2F$vhnekoo94Lo;P7gNMOL&qw>T z#?e_$V_ddz=TQcPBAR$ozDT5}`roxO$SwPeXagAC87w5f@?e9+CW3%pxv*g+3%l=E zK5SUYMPFK+6I+B$#Dh^DYr0sI&Ox_=pk!8{u5uPnRa!A}|1N(qaHj8)8H?PJXfw#H zUsGW*_#KqiU2n$?+pFKV3`oIUUox^881i|x54jOFF5a)H&UCN!YYBd|YPdgCkozoK zDz{Vs?vPW_0ci8?3ILmPR{++mKLo9Z04)NF1*&T>n)mBKf|yf5Y1Ur~riV~XY(R`# zfbrPj)M2@@7&e)WBG!DAt<<7-f?cv@12amlkCj*$vN}4~a&?}J(YdY>R$rCV&jr_d zbAI{AFF);-Q;Ou}(L{yL525~npcpEd?*|J&wzt(&XZ&s56p@*(m=QG-Q_7&+o)6S>9BAriZ=jm6Z^He)ez9O9`+PU`?>6|pHR@*Wu8~@4=y)e?1P3{9YWI`8Q<#o4oD$ zH{xnr{vI5z$-fzgH|KvJhpqWPz~S2bn{aq*{$3oe%l{z`*XIKqzAE33!&m3`p|h#{ zTXD5I|3^4X=I_H{Wqv;ntMUhMxHA7XeCAC4Ag(s${}|7u`TKFTBL62iOyu8!!&v?h z4!!)_ap>p&2bngJ_vG;{J10Mi*3E)D#dqNBYT%O`U%zvUV9m}=a|bP_DBI@yvu_c^ zD!wKACp%O8DoFP2@?`dZ;O6WhdGw%Q)4`oD>wnD;Tb8gG%f2mpF#E?lGr8|O4gw=b zZ_2>=e(TN+AI^AGsUH}jeIaPZmE!KZNOyBl!&+Uy>D@z?YI zuTuzg<@8?Zrno!%2KI2x(jI1D{MaKmW$(gU-_5t)#a`aQk$w&O%HGE3(oVJE zC$rrpneAvS8|`9?k^^6TMM;B-n`xssQ0&QWz7x7bfo2Ux32sV?y|^hkP#}b%hYWK! z%Wc=po$sj>Po?@)YM$~h^2`*TnbK#bUg0wtp2_r?Y`JIN*%S6-R-jd`D-ZqXXN+g~ zXv1i?Gnq34_Zsy9?Mu%oP>{<8p(KySa0x&1eI0?pGR0r#c7&wP!IDg#xY=CnqY zrK(RKibj>mst@5L6R8(wuX7086aI3YW_buPL(&ZUn!m|gFLUnQMhJc_$7lh5m;ins37`sSy4U^zj`yObFo*K_B0RLr(~Q2>N(84(o;Rhu9*%4u`cu z_(SXzUys8jLij`M6yE?bl9hhLH+vjy1Ek;^YpYe-VqLIu7U`PB^14T;hR~94;1d0M z(h?ylSioP4+le;W*9esma(kDM2}^F@jpcnem-oAtBe#OpLI{M2f=mcW!HswFCHqK3 z?=nPGFj??Ph}z9UF{VH%}e^3582Or zT(1UDEq$?fp<(+`6Rq!$ZA?+IZGpBfuNF z*K}~&+&bLB*`Zc*>rn&B;jcZd=yNm>6#0Rde*NP={^TzPA1vNgVHwftzdZ5VU;L3j zK4d?|*PcG~%Rl+6fBy4-*)aWUPm4iu2aad`143qfWHdWa+$xyu!^~aWCPY`zSgvoo z^HWeEQSl=u#~7G$;6U*{d3a z$C_N;!OuDon9tJingjBmtbf^K}rFUod@S_gdFTGPf zYF~CI8#NDkc6+vum+FR>-ICp|kA&v(F2doIg~ME6;Xqit+rr`=g~cs3SbTjA7Pl^e z1@w%2hhVW|Ki9=DD7Fw1XM^JJ699pk;-jIN;p8xw(M(osmjspj_hrEserjHyF;O9 zFTYlofrZndAvg)7?y->i2TLJ!M-5W9jY5h*si2f8Nf?Gws*U!pI|K^C!UH^!#h9pg zFCiEKbH1KnBIe9T#Xsb73JnIGlsmpwwoTmeb!ZSi?jA0yu+S|^?)-Y5$es7{7diQc z>>K&WHjorq^OWV1FNAzdyT=klSuRf42bc?(E&V+!WUD%HGXeG+RO@ z(<+yDW_Rhbjo4_>_*^)I#>edC-)D#C?0hT?pk-9 zJ=xc>2O26wR{A>W{vFxZD)Q<}-jV%7-f}21FFEC2A`47kG4g`Fc){N6Yveh_^0#I0 zRrIxeEJWIeARl_L&);rw*Ws(YqB_61c3$JF9X@|R=)hY)cEBO;2V^9;2Ht)^K3mR< zebsZqq}F;AUbXfvVOejjUEL~(1d4q}_DwqP`XRUJ=x^ufW$gS8aBX<2@{`dAu;kt@ zEcUH#oI_uTz6q~t+{3@`)!%Q{?->5=X3CL}Np2bXuG_TLj^RLl8)Xq@hVzlq<`|B9 znHkPUN}B~7Z>uwnCt}L?n5=jJ(Zp9HM(O81W82ojn?>*8>5b{hX6E0Y``J3EvDcLu zUAd%V`9VF4Z;T$M4K$?jF?Pns(AY4o@zGve<0G%thsu~AV;Ukm)QX5m^KjPR?41kX zo`p{O<#&cwlIi0u{qmpI8kj%oUsMfp^_Oah^jf&u?EOV>t%uM$AqFg@=|rugky;&H z!$PDs$GYq#XNMz|z2vVjWa)Q?WdQBlxnN(iV|k*ySKf2;&arnC(eyy=Wq;-G$)ZmQCdXwg0&I`DAR?RI z9)6Ybp(p~=>8NbV?$}DG#WhHrSurfSmS5c=aWKt?@&M_7Y79L3@IWg9Aw#PKp0ak zCj&0oXx9U~Y#?!D9*XhHE=FAP`5flM*|AEc)1V=OnH8W9*#o6XLruimoPduv&D*Qy z6kNS&roma8r@(TWX5E#wJp;47M>R6H$OQC3mbQiGdP=6w_LOX$=_wgI-NUZeA23dM z2Bw}y`duVdeJo38!>pEDwn?@@BR1yR7TE@^*gDsCjBQZ4S-rQ9q3siD3H8mXJ}j*b za1Z6tKYZFtKL*RAaNj+%ehZZ>YAyL1D`nT(rmaj{YnxH!#8BH6kW?DOZR^orbGYqw zxJriF0Gu9Fp;6_WATQ{l=3!KMC&+%*9TZn8b+AomrD>B=5ZiR9SD6B2OufoXAe%(5 zG9ieN*Q?A6zVU!Q({*s1$F!a94p^1SJ#?yTh^=in*)_!0&0yCM+uF3q*2A_Sa)QsJiQ2ZItg96bCokbqi>d;=MGU=(^=|rE5Idb&IEPji%S_jm12xdAzC?Sk=m%vi4 z&UKdn6S75b2{fSr>wU@KTms0m9Xc@#L6bGPYrtel?iwsvk-G-Uh6^583d1!9hSN)7 zxaRyYT=RDWhSPxI-v;z%Mxi(TzoGZap?AgqhTdy{-unLyz1IM}*ZuzxdS6M{IasaD z))Is~m6I_wLoW1hw%?dbT<}=T32$>&ww6 ztIgvn1aBOjKwUr)Oyr3C8K>k3Yxc1fgB(`yc{##{eP(=+BeM16*nrjB7UZ}RuNY%Y zK3MmB9r}~w3`0=l>(Ot2AUxwpf+G#H8*K7CVd%Y^DW5G;Lm>|#*b#7@&O{Wz$y&>v ztP!*`T;Sh8=kb1cz?Uha9w{eMUWA+%$#mT3=0u>LGPNDwL5@>L7nwWM>Ds%ELAnw6_|RFjxyGc zhetVPN151BPL7Pyyt1RCRRD^lbzgb(PoEZf>zrpjTYY0}*3qkQ8dbt+lm5KQA4ipN zistQAvlUL!Y=zUP5>Cii_%B}d!TeVT`3i2Vh z;UjO;M)kXvueb%Dc#D3De8U-EKjO5nUvbLU&!8jfo^9a?g9AlmKZC{izDxbV)jswR zY$w{~#_(=!o29ADHr}g^b2OXT#`hZQnh#5YZG4|LLRI=UZHzRN9491u+cxS_cI?mY z(6jA>>`pz~GSFOeks0O*U$K>g-IMLHcDq~N(L?e~+8Y<-dJnDztZL2^;Nn^!itA&r zQQ}&FX$^q4zJr6hMP0|?e15CG7UsjfdJ4DmoAnfixIIR&_Kn|1eR0QF+(~(I;WNfH zII3Tji%)#UE7zAtpRk@Lrs4F$XPoD&UB*~%SDb&nU7_pEtTRWYF0{y&F0Wwucqo`pdsfN)p+vCFi`lqqffT{>BZn^7fJL`QRu30qhC=|^$FF=aE_ zq^$PTWIaN zlC@new66e(R; z4AkFlF;rToo>E~gT8hpmG&H#eriiKHrRdf{7s{az4Qs(szEREBKYgApb?_WwOAUmu zmCYDB5fUorEa=0!l5WwjENQC4_A67GYGnP&mS%hQbVgwaVmu83U1L?ACc2kcK9Jf^ zlxA)y)J_B%^0r0QoR~ht#UX?)v8yX4v~3be^gPlj?`n#Cwo(`4d7nc?8rU#0x{ef_ zJjO&CQIC}@k=7(LKe4#8RpTy(pa~`ds131VMi2XtTFHnXZGoRXOWj%G&bGL-?e1*H zetYJEsm4lAF4B)8g!r`CKz-&Q!U#hh5HYlEIbTxz^dolLisZCJh&3~`o8KEW-RKPs zXKroYpPOu`4P$O?N^|b8-^7i&EwM1PYh|@5HuS~BpwSoeck|*!|KcysqWkgsP?yLC zuZ+q-rc!Oc(;$QaNZ)A&!?oaa$LQJl2>0TyXtxLs<1@jF$h6T#Mh0YEKy1FI*+CM5 z%jA3t5#wsT%jHrynb=%T$z@_Hl7`5uknwE6M0U&HnSu7G=@1U!1&HiL&Q^VO-ZC-1 zzPzh?P4$LlJAc;3vZvpa`H1nuBp|6!JB#vl%nWdti3zH@v~uB$kmF<=*Hvc9aT9hp z=W}ZqN4{dzFE2HX7aH!SCBAXB|AnAvBD+{$ehg8iMT3EnA|WHB7q6GeIq_Sp2D-*+=uYceGfMsVkHq-?K}*F`m7B8-m-$ zkrd?_2Ht};(JTcxpOf<=8j#OK#R6G=vkRI(6KHZOf$%s4&8@*{1h@Iu2@GVJt#;`K zj=pk~#vyB>XF?6v^Y7H3V$jIO+N7YBI-3@>ka< z6kFTs`h>2w)n=J@0Z9jlkcyVToH5Mmf-h`_?-EgV1Sv9**|tgPX!3w~!+Ai?{*ucB zq9x9m%YX?#@(_tWxOO5YIk{AmWE7XM$3~|-_KS;N`Tu(5qo1=`XO?}(Q_H+#xwImU zjx>kE4a+7kdFdBihnH++qLW%82Ajv^-eunroo9@Pe&GUe(De;KkhYAB(g(5GP{X!t zgDfgkB`3xK{iTZyO!=*8J~pQ_+;%lxN8NRP+-9?~`I^RUDl0Sys(TA2mlM`uwi%m# zzL_j%fh==^%NrCy(kFwZ=pUkH8MEqs0R&c;0aKQ|=xYKQVws109&!w>^_cF$H;~nu zVdL2b1sYzaY2}_Yz{;D7qfB#f*w;XJO?cDhiVv7$DMI!Nk3}}Z`(;qfX2@LbF-emn z7o<@`BqKwH!a{Q{#bvao=Yf!<|LV`Mde43O0~QahR6L1G8Afr8T7|D z6=NFBbct(2CI^k&+Thw`n_tag7ny0R4ZQT3UkIXM%chlDnO)-YNo;T~Sy}bfJi8ZT zLSiGp&og<+ee;IZne_yci*=dx6Y@MN>0Ks_B`+RxFJ9+fyn1Bn?#+CTK5OpoA%MBV z>%56iF$tj4jtb!dpp_2L%G;*y`V=Rl?|>d(%c$6h^}OmjY~>Anrsmg=4q4IXbWk)@ z!Zl^RWbJfr*D+P(ugz4ZOs1BD?^fE=K%%V%XvmoWiBw608ioe7neGfb^L767*pVBi zIW&b)qRz6^tiObskrX0ZT3Flu?HHJ1mN|CL1p2Zs1dbgD&C5fK5$r)8R*BZJZa^B4 zV*3mH4yMc!qCS=`IrEtYJTf&Nt7(ER&jj6wlu|3aa8GHN1b!GkvDhb2L#FC)AlFX> z(PMwnKw2m^0sDRAT>{g6hBbgfTW3Jp*O6}ETb1BdzcQngc3;MOxB$v0ze~8GHRZ|g zvT62UT`T38{i&?ZhS^XKQ_nOGmg3Q1Yw%u8*mhPpoO{s;>NjcHAYkQt^>A+o3_3Kc zo>BTJqgya1eWct^A?q_iT0Fme4#FNl($qzDO0T^l)az%F&qGh2@l2Hi&8lT?I9}bL z`Du|3ORrBd`LnmXunTr;a33>)2p`6)EqZ_&Ha26}uIGIJHJPtT9k~oQ=u!twwJ*6I zzu*NSlX&4SkkcLe$w2uUV7P9O-c(|N`Y1CTL2e@Q$LUR)&#IxKRKtpYgdphVS&wRT zK?<)7$elAM;MXWb)31TdvplE#xmU!++WtDW!wzM~^jncnZD6vH%j8cHamTe@&=_dJ znLw$+GY6SK@Zd*m;h@=BX}GF<aOL^)$ZNp>oQ<;^AT*HeWmWG+~v4f@| z{Uj?82{iDEclp7?f=$`}A`n;vWmok$_^k9#k}SwPEDbp@tCei8a?jMDJoJqG{%grv zUazZPrzhnp*^-bY&C9%G~8WtnY%0i8Q=j|%vKHb?jjBv!d_S2H>nQ6$_#eMbztrca5J61 zPXq$p3_0#FszATggR^fVI{IJ0N3khVI`Yn%1bGc&pmQZg=%`N{Pw@gYyG$@bDxw-kU-26o*z$5^0=d-x8 zD|b!~%G2)7vp=kN`r2(|x}AMoZtn0swVNM#oHxs@gj46673)&PI!~Dd0!jPI{PawM zl_|@dDA?8Jz>V?O6HNLb_v$+k?n!?m;oIdcZ}e=Re)7ym8kOxg=V2n)mWYIhuI zc5d^dfHP=0OUa2wB#2b(v>S*ar%Gm1e^4>{za|V5qYITjZopNty+q zivn%!I_T_^4wRN?QZvjW(&L$hBDCT0NNc==_;@ry^q3r^|03W94hIR+!bl0oBZ1#E zc)}hD2Rmq4V~mRVdYbHEc%xl39C$wPYgU zM`E;JhoV}M1@boD&17jJWobME4WjdNiQlNG<%p{ZCXECG6vOK%`6G}c&DSTt+I))C z4JaFx=rmxDM_5d(Nt6laQWkQM{xH~{9+b9V_Aqx$FrHxHZT7kv8X(_~dY=stUKvZ! z4?W)#!qD6$pnRa$$Ph_!JL&Yx^8NUm3jmZZd$f_^N;F{?Zv z`i~q`1ME9?i3qydt|qR8nnkwmGruWY_wOWCs+WhEra>$-MwMOwwr^w5 z-4=k&qVwdW-6z4%WPzR~_6&jxaJ3~@fvFck?p_zI++hegLJdc`#B%moA+_5O*)=GS z;r!9hFKSlC4+Y97z2-^rFFq->lm1@#C49WEAD>hoUrz4nsn*&^Ie^2L27+H(8T20Z zKUhx|CQRN7p|beQ)Q|nRr#Cdr4XDz{(C$N+!bd0aFoUXo0X%8Lnk$!uAIo+9nJwV5`Xy$zn?K zj_6{K7E2#BkL)iKq>O~d4Ed3DMEV$F7C#FxCbYr_nN74@J{Hx@Qxx+er-w{=-hic1PJ8!Z!t*5j5?{0QEx}Ot`-?=iZF0x|9cmpx2Cq`1 zPz0*p3pWOn#TZn(v0JinHnuHb$fv5$7)KpUR&9E(>B)$-;KQ>nRW^vc20e(ioKs;s zjW-4nUX^BD_o^;mRh6uWwVz0V7ODSvGAMsp@I$HZpZof2VDOz}Jx@l^zEwl})@wlf z)@w%lhDH0vGN}7`pnb!kedEi6_Q??1Cx82B|IuIx+D|E3PTf~deOYkcz9Ojg{dXSY zL&fQsTnI_|7M;7(Y6{bjVr zFrQJK`Bav({7=B?NFzR|Xc>N%IE^2IWu7%S&7TKaJiBW)Rm}>Q{uzVPLzH;vrau>& zX5hkxf`?7A7vng?A|hlZVshp3p-dnVDwk6D4jsCfqbZ$Yze0yGO*{`}2WXJ>ZQ#t5Ey_ZZRs?07Iu#oX^|d-}WH2<- z?tD}9S%%aNjeYq%Pi$HGL|}}3=X+$w(nlEef9m{?>|62(G#EaY1#OiAt07*$_$~jIjdFzIt{a^%=;QaJdrMWq9aGv@@;U2(LtMO z?x~Uj|M>lo!bJLL8_FqMBMUl2%Hp>5O(?r$o`#cZ9c*npV5ZY^ulJPTv{jeYHcW5n z6Wug1W9N^9AaD$=VQ0Ws`UaLh`AXmNmHrT`8dYnTbW^_Or?4<%WmTOp^$NPY_Iy3+ zRs`y7)?C$PQ0sCT;-WKN?+-$1yxA!(ofJi2l|SF{ySbJohUt_jP`6ZXg?fac8>a%Z3m4{zdNv{WHT-MO0A%( z>84<1_U*djKG>v|QI-O=j6G->18NzY@O+_`k)AGq`oA za926=tT#yiQR#Q-79lk5alh#A%;vrt`Or8Ta_bV z1d=#McJ&b>gRA=t`_TcXyX?<6&iOrx)boBmMlJ;X10PFIZB>ldLfVe@_=6sLiGqPWBnKdiL;*j4t!Jk z9tU;BmRHlHD z4`wlt|J_IkgnC}Qx&`$+uuS6e#i3$PN`#Z5QLYvaDQnywQDI|J76aC%21D0!%A8tZBQ0MPj%sR-{>S48_c$qoQP+l;u>>*Q2Oyu}PlBZ@9P~4O z?mIn)T%W-51qG$&&`Fl}D<)-CK2Zd40{69O4)&SK(=29GK_jc>TZ%zVkjlLBSc^@T zr9*x;`GiBA7>4~5z78_|ms&ofN-Ps|;WpeAoZbR^h0M)|d_?EP@a2y91uxouA%Ubn z(9qkGZNVZu^kWtRehRO7!5@^*D@v81jWIki4O#(%PWg&ZCw;v>L5d+MEQ(&T`L?=0tBQCU)89a5BNKTKR;HF36G|)&2;N)_w#6BKGcn043&S z8xClZl=YL-N9qs(yCOZF*w143KdKF71A?rVAc@zGH2y5BD zn>aKB3erYg^_2YoTj?DZl->QxV+BG3jm?rYFWT?);DO<^R1Y2y8R=JMJiEr^@rGGh z8Ay4|nwkw9CxJU~2&a<{Jfkw{;nc4#dg-5Wd$9^=mEf0KtgcIKpVZAk4fR}g#N#~R z=hzCcLH%2l5AE<07*|3?W22KK%Omz=oKOj_+uw>L(o2)e&)mmWn*wr>z*y&LcN?M1Q;L3ufx z+X6Ycet`0zINLselbab0T*Q7)sCn8oolXtlLKhfA)ipdz&5IB$7V3+@$6v9Pr>Cpa zkZ&sw{T^O_y}tf#zLa4xH@Nq|hiBKgF>M*>a5<*ybx32~{>8rY;Hg*po&9d_yq)^m z>0#IDm9MbVUblO(PLH}yXI^2aT{xOr=wIVz#C9KO_cA5r#;tXjy!g8pdD9IJ_DkN` z>9)V5cOH2S?`(HlU(!1dzJ_4*OVgJdlu$kAe z!kXCOzFb2(ed^0Gqy|LiFV~Qs{hcqzkYeao@$#H`4U&D}tKbH(%uud6A4z=DP1yW% zIFlFSW+2sb|H&ZL(&4>(WGGbHUYO7vbA)EoD9QMa{-HY8!5unMSDNyW;7}QH z?B&95J6BuWvd((r;+9_d?{EnDXn6$ZPvK8O=fv_j&Y#sg=;eA!&eP|~L;2+y{4kzA zr+zQxU*NJUmu}>rX$7Gwm3Is*zjLj(8mKp*7H;*R^wU3SE@5kg@7zS&;AU?%zA#qq z1|7EtoL0<`PkY_QV9PeY7D|}9NSdax?G)igpLhGN_^@8{8?U#zoARk&U-YaOiHePm ziH}XRr;a@RL7#ehJ4{bUVfFNOd`3@y-l6fC+Dl|GZ*+q>w`4HBVx#LjhW?(^uvB+= z3)QKCya!ON;3!OY9@a*WAHe*YLFjbXI?r4Lsqem=<)5**>}nuk)IhW5$LKKyrnW9f&(9I9`{_j>Ady=72-QtmCUPCsa8Na%2&*$Nym z5`o0fM1>4zG-6vVYRi5a;l%IC0(ThQ>^Z>!JYX7?#yJh=p+be!RNC#^nI znKbP*bkBT=a}Zu&4rIhLZp4o+gLM_~?eK$FH~h#w^Cb>HxbW~dy5WC%3BZ>dzON`$ z&dJ^7*7T2>_6HDA;|W&E`>9$y+zq;_q}^ik(XYcIQWcOYR@c#F+%<*Fbpg>Xlv%UU zOps9>d)9efp;C4Fyd!-O&UZlDu`#$I?`v-nw3n{CK0Z6NP6K7aAkh<4HkUKsT7H2# zgBzxc04@tzYmb~!`|%TBcxKJs9w0q)AF84ag_|^GeW+g^jlG@*p#|#hg{Vdu8O=$6 zbM!!NGRNg@8c7iHgaTp|B!N`fIiWl1@W2a^iP znhn*nCtK@283KW6>zz@4=Vz^6-e8&{cmw)h*|pXhXOgS^sq6fR+mv?|=uV(XHoTsE zfj6Bua9EEooAEB=XL(oVJ%!YEf{2jXK5%qwO7$;{=oUngmURpQAo-rL3yYfIzAB6x z)mMFAZFVs@*T7-;rnZm{z zzhFtwV2vAztk@sYOV|#NkBIIdg8%R?Z93JgfT-&(Qlx;l7gJEi+lvgkc=MvnMl-db z>1t8ag@kKrr4N!?Z&RFF*# zAW7C#YZgdc&$FiKT&p6$-xMl!T0qV4rVIh2DO+haD452prKFWp&YBflVrrdgqgq-C z0W5NlZn8KR(o<%Xn3MFyj+oOQCstY>j?!}9u>%!JpZm?_NqUVd#|B|Ed(gu!!*2O} zo0^c8dD}JaZ9l%u+Y+_-D++XsEx)!y{6rG8l*>gziP)q(^M#->uMN)jX}Bi?TXSEz z=j#WIW-gEZp6o(n<#WGpqjI@o6PS+pfn7mCA%L`kBx=+XZra~a!8q6B=8QeQJEvLU-5C95Z(q?X--7BVZp=aYQ@;KZlyA9_mdeWpY8D1b36)+?qv<(q zir55m)}2Ceya{TmGv27CYMu$%@vs`+LRT^JT2@JpNAKGftdlOUB%A;k<7*aciR8Ha zL$5M_PARN@$2ECyor}vC9UJ3;UyNbOcMaE|;hGmagf^J3^7X4278+me@3@Y?@;-JP4&b@VeU5Lj#m1q*cXnBi z{#Nc{OI0+)154Aj7aF4K=`yzDChIY-CD!=*&JRaw-KfRfz5;bWmV5td-!r=Y)vfmG z)t%MdUVkQ0zmSD53tIi?*#5Y_@-_sy}_(ec9(s6XTNJ^7M;AOGtA)vx6Sk56|8 zP@l7xjybl?v1N`N>IU}#S@i)l;#8wg2m@$ZM}KMRFO8+YH1wBv=`XSV5}hBip&4Lc zj{bRX?LZ@>VL4{-oi#(MA`@Whr+=Ds{;vz^gbEvWX%s_#`OI(09@B!RCam;C2!Z`P zerQnlR6h4xswWEc788|KzJOa&m6%?Vb5$xm^4l)Z+V)4K(r@EMsw*;jC#Mbi6W_YO z$*+t%-(upI;%(J09*%SCD+>suBGjw0Zv}>AbE! zVO99J!0GYNFJe|d?IOhg&1Zt(Z4W0OH1guK+&|4-AgB^GP1DUbs4_K8lX3 zVp?5iL#Z^U)%P*0uD2NZ)n0EOO8q&lT2YbvrcH{?MD97`4dv{gHrY6;QJAFtcV`e* zrXev#ayTXY43d)`PBb`*m8zs2`hl5XhVR89`2NPLA&;-+e{l1=x1{*c&~zl_3(k>? zUcK*uFS1p+HiQDV;|6_zk2MEcHOtIIqdeL)MY>$eLerG!vMuOQIDo^=^2KMKMplGI zdAKRu?ytzj{I5N2Rndl>Z`su^yaHoe(dYbdZq|m$mNio~%nqkhZD{h)ddY>e-Vl;- zh;=M#FyU%cp;?3ZGNTF+8wMNhWq}5?9rZP9(g|}se|b+}pFwKI5| z&{27FgLgEmSD$9fsane^ww$cBoMg+1TFVKxEY?~U*>bGba*QoUs}>Aqp($f;=$Ma+ zoDRaU04G-@X2o$K7?e*w^)$m0<>>~5%)g3ZlDfL1pICV1DVagJcntDGx(M=cT3XVd zU=k&B&!fLOde39OTHW)Z=sN#IQ^)a?IUbcGbOMKQje>w)`a)<1p}_UhuLceu6=tW6 zCK)r{@l<07iJxq23lD|3@evSQInG7KR}wJsYWK>01|G2O{K;0FtaEcIb~OZg0&Bt)Tr^!DDr650UNp-b?=j_{I1N~7$*5WI>E!3eWE#n*^A8) z%s$o}!R({W5zJm_j$rn=<_KobCd*pkXoE|XgEzQ68(3Vz ztA=nJ>(n#2eN{zfa~9oF$VpXDOUCxz5> z?0QBjVmh(c2XrXX`jYiwAC4fl4u9N21i6kNDAEdngMluhYU0^)R_+?FeNb?u$=tB~ zxMOlY7wP#KIR_5T1Pjf)&Fp>6oY&7I)pCQyA$!#*8kJbqV20VG8g)Py_lkDSMMCEq zPy+LwQ|9$&eQ8F@x|YHgve^+FkvJ=G_#qHZuUEv_*V~}b(}_Yw7O%L+yfuVWY%Uf; zr~o*>MK$Q7p@LYzUUaK@4;7#%MrR&96Fm2jOn2QU_S~mOPUuvU)~fdak}c$_uV0e> zjGu*c@-(uqA_uiJF1pYFkHZ6M9%&klAVwNIjxP+i219Sg z+g|dFpwlp3@n)-0;oj^&>NMAY8#cW75r_7SuWoahjy&t=$ck~ZYCo0eYbZn<4k<-c z8b^8|`|3#9vix1yQ4LuSuCyW#lz;q(i=I&>^vdV{&}qm#((slvQUe9GR3%#NE1Ie( z70@jTHN@IH2C8@{pzR)H%4IGX7pDIEpnI_11|IYMk9r$0-8Bw(=BdggpqCy9oHbh^ zJ<>7we4}=)O}!2+E_*CmwR}ls=UNXXMKn%i(kSf^EW@mOHC^AGayatuKP!*zle5o7 zOp^zzJ{&Uc=IJvyeXr)ED_WXckM0Q?d~)XKXT5Ua$3E+&KSWhtSNK#0St5fzQh7ok zbKwxx1oh=vbn|>Qa$TX1Bz*)_s3TQHu7aZ5UBcy z_6iS6%xQoR%P&-uY8ec3cTMssv=JmKdZ9o$@KzQc{o0xm;4w5E`6DR7 z58-sBG=&uVWdjx>rz(JR2pK*EznMouUHZU6>ca(H)4z#z@aKY_dB1fvp9x+HjLt{K znI3NHf-j(+{h^>zoPG>xWff44GGQ66lH)T$oiqqKARL5qbX-o~i1IU^x<|iC?0}wYV|X$EJ74~RjCV@w3vm3P32`>zup8-s zC$d$wZwVj59@=(13!Ocw!^zOm8s22lFmqi0)8+$H@H5

mR?iT>t!EjG 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